File: Internal\SapiInterop\SapiProxy.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.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace System.Speech.Internal.SapiInterop
{
    internal abstract class SapiProxy(ISpRecognizer recognizer) : IDisposable
    {
        #region Constructors

        public virtual void Dispose()
        {
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Internal Methods

        internal abstract object? Invoke(ObjectDelegate pfn);
        internal abstract void Invoke2(VoidDelegate pfn);

        #endregion

        #region Internal Properties

        internal ISpRecognizer Recognizer
        {
            get
            {
                return _recognizer;
            }
        }

        internal ISpRecognizer2 Recognizer2 =>
            _recognizer2 ??= (ISpRecognizer2)_recognizer;

        internal ISpeechRecognizer SapiSpeechRecognizer =>
            _speechRecognizer ??= (ISpeechRecognizer)_recognizer;

        #endregion

        #region Protected Fields

        protected ISpeechRecognizer? _speechRecognizer;
        protected ISpRecognizer2? _recognizer2;
        protected ISpRecognizer _recognizer = recognizer;

        #endregion

        #region Protected Fields

        internal class PassThrough : SapiProxy, IDisposable
        {
            #region Constructors

            internal PassThrough(ISpRecognizer recognizer)
                : base(recognizer)
            {
            }

            ~PassThrough()
            {
                Dispose(false);
            }
            public override void Dispose()
            {
                try
                {
                    Dispose(true);
                }
                finally
                {
                    base.Dispose();
                }
            }

            #endregion

            #region Internal Methods

            internal override object? Invoke(ObjectDelegate pfn)
            {
                return pfn.Invoke();
            }

            internal override void Invoke2(VoidDelegate pfn)
            {
                pfn.Invoke();
            }

            #endregion

            #region Private Methods

            private void Dispose(bool disposing)
            {
                _recognizer2 = null;
                _speechRecognizer = null;
                Marshal.ReleaseComObject(_recognizer);
            }

            #endregion
        }

        internal class MTAThread : SapiProxy, IDisposable
        {
            #region Constructors

            internal MTAThread(SapiRecognizer.RecognizerType type)
                : base(null!) // Set _recognizer field instead of base constructor
            {
                _mta = new Thread(new ThreadStart(SapiMTAThread));
                if (!_mta.TrySetApartmentState(ApartmentState.MTA))
                {
                    throw new InvalidOperationException();
                }
                _mta.IsBackground = true;
                _mta.Start();

                if (type == SapiRecognizer.RecognizerType.InProc)
                {
                    Invoke2(delegate { _recognizer = (ISpRecognizer)new SpInprocRecognizer(); });
                }
                else
                {
                    Invoke2(delegate { _recognizer = (ISpRecognizer)new SpSharedRecognizer(); });
                }
                System.Diagnostics.Debug.Assert(_recognizer is not null);
            }

            ~MTAThread()
            {
                Dispose(false);
            }

            public override void Dispose()
            {
                try
                {
                    Dispose(true);
                }
                finally
                {
                    base.Dispose();
                }
            }

            #endregion

            #region Internal Methods

            internal override object? Invoke(ObjectDelegate pfn)
            {
                lock (this)
                {
                    _doit = pfn;
                    _process.Set();
                    _done.WaitOne();
                    if (_exception == null)
                    {
                        return _result;
                    }
                    else
                    {
                        ExceptionDispatchInfo.Throw(_exception);
                        return null;
                    }
                }
            }

            internal override void Invoke2(VoidDelegate pfn)
            {
                lock (this)
                {
                    _doit2 = pfn;
                    _process.Set();
                    _done.WaitOne();
                    if (_exception != null)
                    {
                        ExceptionDispatchInfo.Throw(_exception);
                    }
                }
            }

            #endregion

            #region Private Methods

            private void Dispose(bool disposing)
            {
                lock (this)
                {
                    _recognizer2 = null;
                    _speechRecognizer = null;
                    Invoke2(delegate { Marshal.ReleaseComObject(_recognizer); });
                    ((IDisposable)_process).Dispose();
                    ((IDisposable)_done).Dispose();
                }
                base.Dispose();
            }

            private void SapiMTAThread()
            {
                while (true)
                {
                    try
                    {
                        _process.WaitOne();
                        _exception = null;
                        if (_doit != null)
                        {
                            _result = _doit.Invoke();
                            _doit = null;
                        }
                        else
                        {
                            _doit2!.Invoke();
                            _doit2 = null;
                        }
                    }
                    catch (Exception e)
                    {
                        _exception = e;
                    }
                    try
                    {
                        _done.Set();
                    }
                    catch (ObjectDisposedException)
                    {
                        break;
                    }
                }
            }

            #endregion

            #region Private Fields

            private Thread _mta;
            private AutoResetEvent _process = new(false);
            private AutoResetEvent _done = new(false);
            private ObjectDelegate? _doit;
            private VoidDelegate? _doit2;
            private object? _result;
            private Exception? _exception;

            #endregion
        }

        internal delegate object? ObjectDelegate();
        internal delegate void VoidDelegate();
    }

    #endregion
}