| File: System\Runtime\InteropServices\Marshalling\StrategyBasedComWrappers.cs | Web Access |
| Project: src\runtime\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; using System.Runtime.Versioning; 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> [UnsupportedOSPlatform("android")] [UnsupportedOSPlatform("browser")] [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] [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 expects that all provided COM objects are either free threaded and that calls to <c>IUnknown</c> methods can be made from any thread or affinitized to the current apartment and <c>IUnknown</c> methods can safely be called on the current thread. /// This strategy is always safe on non-Windows platforms, since COM apartments do not exist on those platforms. /// </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(nint, CreateObjectFlags)" /> 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.CreateObject(nint, CreateObjectFlags, object?, out CreatedWrapperFlags)" /> protected sealed override object? CreateObject(nint externalComObject, CreateObjectFlags flags, object? userState, out CreatedWrapperFlags wrapperFlags) { return base.CreateObject(externalComObject, flags, userState, out wrapperFlags); } /// <inheritdoc cref="ComWrappers.ReleaseObjects" /> protected sealed override void ReleaseObjects(IEnumerable objects) { throw new NotImplementedException(); } } }