File: Internal\SapiInterop\EventNotify.cs
Web Access
Project: src\src\runtime\src\libraries\System.Speech\src\System.Speech.csproj (System.Speech)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Speech.AudioFormat;
using System.Threading;

namespace System.Speech.Internal.SapiInterop
{
    internal class SpNotifySink : ISpNotifySink
    {
        public SpNotifySink(EventNotify eventNotify)
        {
            _eventNotifyReference = new WeakReference(eventNotify);
        }

        void ISpNotifySink.Notify()
        {
            EventNotify? eventNotify = (EventNotify?)_eventNotifyReference.Target;
            if (eventNotify != null)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(eventNotify.SendNotification));
            }
        }

        private WeakReference _eventNotifyReference;
    }
    /// Dispatches events from ISpEventSource to DispatchEventDelegate on a thread
    /// compatible with the application model of the thread that created this object.
    internal class EventNotify
    {
        #region Constructors

        internal EventNotify(ISpEventSource sapiEventSource, IAsyncDispatch dispatcher, bool additionalSapiFeatures)
        {
            // Remember event source
            _sapiEventSourceReference = new WeakReference(sapiEventSource);

            _dispatcher = dispatcher;
            _additionalSapiFeatures = additionalSapiFeatures;

            // Start listening to events from sapiEventSource.
            _notifySink = new SpNotifySink(this);
            sapiEventSource.SetNotifySink(_notifySink);
        }

        #endregion Constructors

        #region Internal Methods

        // Finalizer is not required since ISpEventSource and AsyncOperation both implement appropriate finalizers.
        internal void Dispose()
        {
            lock (this)
            {
                // Since we are explicitly calling Dispose(), sapiEventSource (RCW) will normally be alive.
                // If Dispose() is called from a finalizer this may not be the case so check for null.
                if (_sapiEventSourceReference != null)
                {
                    ISpEventSource? sapiEventSource = (ISpEventSource?)_sapiEventSourceReference.Target;
                    if (sapiEventSource != null)
                    {
                        // Stop listening to events from sapiEventSource.
                        sapiEventSource.SetNotifySink(null);
                        _notifySink = null;
                    }
                }
                _sapiEventSourceReference = null;
            }
        }

        internal void SendNotification(object? _)
        {
            lock (this)
            {
                // Call dispatchEventDelegate for each SAPI event currently queued.
                if (_sapiEventSourceReference != null)
                {
                    ISpEventSource? sapiEventSource = (ISpEventSource?)_sapiEventSourceReference.Target;
                    if (sapiEventSource != null)
                    {
                        List<SpeechEvent> speechEvents = new();
                        SpeechEvent? speechEvent;
                        while (null != (speechEvent = SpeechEvent.TryCreateSpeechEvent(sapiEventSource, _additionalSapiFeatures, _audioFormat)))
                        {
                            speechEvents.Add(speechEvent);
                        }
                        _dispatcher.Post(speechEvents.ToArray());
                    }
                }
            }
        }

        #endregion Methods

        #region Internal Properties

        internal SpeechAudioFormatInfo? AudioFormat
        {
            set
            {
                _audioFormat = value;
            }
        }

        #endregion Methods

        #region Private Methods

        #endregion

        #region Private Fields

        private IAsyncDispatch _dispatcher;
        private WeakReference? _sapiEventSourceReference;
        private bool _additionalSapiFeatures;
        private SpeechAudioFormatInfo? _audioFormat;
        private ISpNotifySink? _notifySink;
        #endregion Private Fields
    }
}