File: System\Runtime\InteropServices\Marshalling\StrategyBasedComWrappers.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\src\System.Runtime.InteropServices.csproj (System.Runtime.InteropServices)
// 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;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
 
namespace System.Runtime.InteropServices.Marshalling
{
    /// <summary>
    /// A <see cref="ComWrappers"/>-based type that uses customizable strategy objects to implement COM object wrappers and managed object wrappers exposed to COM.
    /// </summary>
    [CLSCompliant(false)]
    public class StrategyBasedComWrappers : ComWrappers
    {
        internal static StrategyBasedComWrappers DefaultMarshallingInstance { get; } = new();
 
        /// <summary>
        /// The default strategy to discover interface details about COM interfaces.
        /// </summary>
        /// <remarks>
        /// This strategy can discover interfaces and classes that use source-generated COM interop that use the <see cref="GeneratedComInterfaceAttribute"/> and <see cref="GeneratedComClassAttribute"/> attributes.
        /// This strategy looks for an <see cref="IUnknownDerivedAttribute{T, TImpl}"/> or <see cref="ComExposedClassAttribute{T}"/> attribute on the type of the provided object to discover COM type information.
        /// </remarks>
        public static IIUnknownInterfaceDetailsStrategy DefaultIUnknownInterfaceDetailsStrategy { get; } = Marshalling.DefaultIUnknownInterfaceDetailsStrategy.Instance;
 
        /// <summary>
        /// The default strategy to use for calling <c>IUnknown</c> methods.
        /// </summary>
        /// <remarks>
        /// This strategy assumes that all provided COM objects are free threaded and that calls to <c>IUnknown</c> methods can be made from any thread.
        /// </remarks>
        public static IIUnknownStrategy DefaultIUnknownStrategy { get; } = FreeThreadedStrategy.Instance;
 
        /// <summary>
        /// The default strategy to use for caching COM objects.
        /// </summary>
        /// <returns>The default strategy caches the interface pointers per interface no matter what thread they were initially retrieved on.</returns>
        protected static IIUnknownCacheStrategy CreateDefaultCacheStrategy() => new DefaultCaching();
 
        /// <summary>
        /// Get or create the interface details strategy for a new COM object wrapper.
        /// </summary>
        /// <returns>The interface details strategy to use for the new COM object.</returns>
        protected virtual IIUnknownInterfaceDetailsStrategy GetOrCreateInterfaceDetailsStrategy()
        {
            if (OperatingSystem.IsWindows() && RuntimeFeature.IsDynamicCodeSupported && ComObject.BuiltInComSupported && ComObject.ComImportInteropEnabled)
            {
                return GetInteropStrategy();
            }
            return DefaultIUnknownInterfaceDetailsStrategy;
 
            // This logic is split into a separate method, otherwise the trimmer will think that these suppressions are unnecessary on various platforms and error on them.
            // The easiest way to handle this is to put the case that needs annotations into a separate method.
            [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "The usage is guarded, but the analyzer and the trimmer don't understand it.")]
            [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "The opt-in feature is documented to not work in trimming scenarios.")]
            static IIUnknownInterfaceDetailsStrategy GetInteropStrategy()
            {
                return ComImportInteropInterfaceDetailsStrategy.Instance;
            }
        }
 
        /// <summary>
        /// Get or create the IUnknown strategy for a new COM object wrapper.
        /// </summary>
        /// <returns>The IUnknown strategy to use for the new COM object.</returns>
        protected virtual IIUnknownStrategy GetOrCreateIUnknownStrategy() => DefaultIUnknownStrategy;
 
        /// <summary>
        /// Create the caching strategy for a new COM object wrapper.
        /// </summary>
        /// <returns>The caching strategy to use for the new COM object.</returns>
        protected virtual IIUnknownCacheStrategy CreateCacheStrategy() => CreateDefaultCacheStrategy();
 
        /// <inheritdoc cref="ComWrappers.ComputeVtables" />
        protected sealed override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
        {
            if (GetOrCreateInterfaceDetailsStrategy().GetComExposedTypeDetails(obj.GetType().TypeHandle) is { } details)
            {
                return details.GetComInterfaceEntries(out count);
            }
            count = 0;
            return null;
        }
 
        /// <inheritdoc cref="ComWrappers.CreateObject" />
        protected sealed override unsafe object CreateObject(nint externalComObject, CreateObjectFlags flags)
        {
            if (flags.HasFlag(CreateObjectFlags.TrackerObject)
                || flags.HasFlag(CreateObjectFlags.Aggregation))
            {
                throw new NotSupportedException();
            }
 
            var rcw = new ComObject(GetOrCreateInterfaceDetailsStrategy(), GetOrCreateIUnknownStrategy(), CreateCacheStrategy(), (void*)externalComObject)
            {
                UniqueInstance = flags.HasFlag(CreateObjectFlags.UniqueInstance)
            };
 
            return rcw;
        }
 
        /// <inheritdoc cref="ComWrappers.ReleaseObjects" />
        protected sealed override void ReleaseObjects(IEnumerable objects)
        {
            throw new NotImplementedException();
        }
    }
}