File: Internal\ObjectToken\ObjectToken.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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Speech.Internal.SapiInterop;

namespace System.Speech.Internal.ObjectTokens
{
    [DebuggerDisplay("{Name}")]
    internal class ObjectToken : RegistryDataKey, ISpObjectToken
    {
        #region Constructors

        protected ObjectToken(ISpObjectToken sapiObjectToken, bool disposeSapiToken)
            : base(sapiObjectToken)
        {
            ArgumentNullException.ThrowIfNull(sapiObjectToken);

            _sapiObjectToken = sapiObjectToken;
            _disposeSapiObjectToken = disposeSapiToken;
        }

        /// <summary>
        /// Creates a ObjectToken from an already-existing ISpObjectToken.
        /// Assumes the token was created through enumeration, thus should not be disposed by us.
        /// </summary>
        /// <returns>ObjectToken object</returns>
        internal static ObjectToken Open(ISpObjectToken sapiObjectToken)
        {
            return new ObjectToken(sapiObjectToken, false);
        }

        /// <summary>
        /// Creates a new ObjectToken from a category
        /// Unlike the other Open overload, this one creates a new SAPI object, so Dispose must be called if
        /// you are creating ObjectTokens with this function.
        /// </summary>
        /// <returns>ObjectToken object</returns>
        internal static ObjectToken? Open(string? sCategoryId, string? sTokenId, bool fCreateIfNotExist)
        {
            ISpObjectToken sapiObjectToken = (ISpObjectToken)new SpObjectToken();

            try
            {
                sapiObjectToken.SetId(sCategoryId, sTokenId, fCreateIfNotExist);
            }
            catch (Exception)
            {
                Marshal.ReleaseComObject(sapiObjectToken);
                return null;
            }

            return new ObjectToken(sapiObjectToken, true);
        }

        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    if (_disposeSapiObjectToken && _sapiObjectToken != null)
                    {
                        Marshal.ReleaseComObject(_sapiObjectToken);
                        _sapiObjectToken = null!;
                    }
                    if (_attributes != null)
                    {
                        _attributes.Dispose();
                        _attributes = null;
                    }
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }

        #endregion

        #region public Methods

        /// <summary>
        /// Tests whether two AutomationIdentifier objects are equivalent
        /// </summary>
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            return obj is ObjectToken token && string.Equals(Id, token.Id, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// Overrides Object.GetHashCode()
        /// </summary>
        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }

        #endregion

        #region Internal Properties

        internal RegistryDataKey Attributes
        {
            get
            {
                return _attributes ??= OpenKey("Attributes")!;
            }
        }

        internal ISpObjectToken SAPIToken
        {
            get
            {
                return _sapiObjectToken;
            }
        }

        /// <summary>
        /// Returns the Age from a voice token
        /// </summary>
        internal string Age
        {
            get
            {
                string age;
                if (Attributes == null || !Attributes.TryGetString("Age", out age))
                {
                    age = string.Empty;
                }
                return age;
            }
        }

        /// <summary>
        /// Returns the gender
        /// </summary>
        internal string Gender
        {
            get
            {
                string gender;
                if (Attributes == null || !Attributes.TryGetString("Gender", out gender))
                {
                    gender = string.Empty;
                }
                return gender;
            }
        }

        /// <summary>
        /// Returns the Name for the voice
        /// Look first in the Name attribute, if not available then get the default string
        /// </summary>
        internal string TokenName()
        {
            string name = string.Empty;
            if (Attributes != null)
            {
                Attributes.TryGetString("Name", out name);

                if (string.IsNullOrEmpty(name))
                {
                    TryGetString(null, out name);
                }
            }
            return name;
        }

        /// <summary>
        /// Returns the Culture defined in the Language field for a token
        /// </summary>
        internal CultureInfo? Culture
        {
            get
            {
                CultureInfo? culture = null;
                string langId;
                if (Attributes.TryGetString("Language", out langId))
                {
                    culture = SapiAttributeParser.GetCultureInfoFromLanguageString(langId);
                }
                return culture;
            }
        }

        /// <summary>
        /// Returns the Culture defined in the Language field for a token
        /// </summary>
        internal string Description
        {
            get
            {
                string description = string.Empty;
                string sCultureId = string.Format(CultureInfo.InvariantCulture, "{0:x}", CultureInfo.CurrentUICulture.LCID);
                if (!TryGetString(sCultureId, out description))
                {
                    TryGetString(null, out description);
                }
                return description;
            }
        }

        #endregion

        #region internal Methods

        #region ISpObjectToken Implementation

        public void SetId([MarshalAs(UnmanagedType.LPWStr)] string? pszCategoryId, [MarshalAs(UnmanagedType.LPWStr)] string? pszTokenId, [MarshalAs(UnmanagedType.Bool)] bool fCreateIfNotExist)
        {
            throw new NotImplementedException();
        }

        public void GetId([MarshalAs(UnmanagedType.LPWStr)] out IntPtr ppszCoMemTokenId)
        {
            ppszCoMemTokenId = Marshal.StringToCoTaskMemUni(Id);
        }

        public void Slot15() { throw new NotImplementedException(); } // void GetCategory(out ISpObjectTokenCategory ppTokenCategory);
        public void Slot16() { throw new NotImplementedException(); } // void CreateInstance(object pUnkOuter, UInt32 dwClsContext, ref Guid riid, ref IntPtr ppvObject);
        public void Slot17() { throw new NotImplementedException(); } // void GetStorageFileName(ref Guid clsidCaller, [MarshalAs(UnmanagedType.LPWStr)] string pszValueName, [MarshalAs(UnmanagedType.LPWStr)] string pszFileNameSpecifier, UInt32 nFolder, [MarshalAs(UnmanagedType.LPWStr)] out string ppszFilePath);
        public void Slot18() { throw new NotImplementedException(); } // void RemoveStorageFileName(ref Guid clsidCaller, [MarshalAs(UnmanagedType.LPWStr)] string pszKeyName, int fDeleteFile);
        public void Slot19() { throw new NotImplementedException(); } // void Remove(ref Guid pclsidCaller);
        public void Slot20() { throw new NotImplementedException(); } // void IsUISupported([MarshalAs(UnmanagedType.LPWStr)] string pszTypeOfUI, IntPtr pvExtraData, UInt32 cbExtraData, object punkObject, ref Int32 pfSupported);
        public void Slot21() { throw new NotImplementedException(); } // void DisplayUI(UInt32 hWndParent, [MarshalAs(UnmanagedType.LPWStr)] string pszTitle, [MarshalAs(UnmanagedType.LPWStr)] string pszTypeOfUI, IntPtr pvExtraData, UInt32 cbExtraData, object punkObject);
        public void MatchesAttributes([MarshalAs(UnmanagedType.LPWStr)] string pszAttributes, [MarshalAs(UnmanagedType.Bool)] out bool pfMatches) { throw new NotImplementedException(); }

        #endregion

        /// <summary>
        /// Check if the token supports the attributes list given in. The
        /// attributes list has the same format as the required attributes given to
        /// SpEnumTokens.
        /// </summary>
        internal bool MatchesAttributes(string[] sAttributes)
        {
            bool fMatch = true;

            for (int iAttribute = 0; iAttribute < sAttributes.Length; iAttribute++)
            {
                string s = sAttributes[iAttribute];
                fMatch &= HasValue(s) || (Attributes != null && Attributes.HasValue(s));
                if (!fMatch)
                {
                    break;
                }
            }
            return fMatch;
        }

        internal T? CreateObjectFromToken<T>(string name)
            where T : class
        {
            T? instanceValue = default(T);
            string clsid;

            if (!TryGetString(name, out clsid))
            {
                throw new ArgumentException(SR.Get(SRID.TokenCannotCreateInstance));
            }

            try
            {
                // Application Class Id
                Type type = Type.GetTypeFromCLSID(new Guid(clsid))!;

                // Create the object instance
                instanceValue = (T?)Activator.CreateInstance(type);

                // Initialize the instance
                if (instanceValue is ISpObjectWithToken objectWithToken)
                {
                    int hresult = objectWithToken.SetObjectToken(this);
                    if (hresult < 0)
                    {
                        throw new ArgumentException(SR.Get(SRID.TokenCannotCreateInstance));
                    }
                }
                else
                {
                    Debug.Fail("Cannot query for interface " + typeof(ISpObjectWithToken).GUID + " from COM class " + clsid);
                }
            }
            catch (Exception e)
            {
                if (e is MissingMethodException || e is TypeLoadException || e is FileLoadException || e is FileNotFoundException || e is MethodAccessException || e is MemberAccessException || e is TargetInvocationException || e is InvalidComObjectException || e is NotSupportedException || e is FormatException)
                {
                    throw new ArgumentException(SR.Get(SRID.TokenCannotCreateInstance));
                }
                throw;
            }
            return instanceValue;
        }

        #endregion

        #region private Methods

        #endregion

        #region Private Types

        //--- ISpObjectWithToken ----------------------------------------------------
        [ComImport, Guid("5B559F40-E952-11D2-BB91-00C04F8EE6C0"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface ISpObjectWithToken
        {
            [PreserveSig]
            int SetObjectToken(ISpObjectToken pToken);
            [PreserveSig]
            int GetObjectToken(IntPtr ppToken);
        }

        #endregion
        #region private Fields

        private ISpObjectToken _sapiObjectToken;

        private bool _disposeSapiObjectToken;

        private RegistryDataKey? _attributes;

        #endregion
    }
}