| File: ComInterfaceMarshallingInfoProvider.cs | Web Access |
| Project: src\runtime\src\libraries\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj (Microsoft.Interop.SourceGeneration) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Text; using System.Linq; using Microsoft.CodeAnalysis; namespace Microsoft.Interop { /// <summary> /// This class supports generating marshalling info for types with the <c>System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute</c> attribute. /// </summary> public class ComInterfaceMarshallingInfoProvider : IMarshallingInfoAttributeParser { private readonly Compilation _compilation; public ComInterfaceMarshallingInfoProvider(Compilation compilation) { _compilation = compilation; } public bool CanParseAttributeType(INamedTypeSymbol attributeType) => attributeType.ToDisplayString() == TypeNames.GeneratedComInterfaceAttribute; public MarshallingInfo? ParseAttribute(AttributeData attributeData, ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback) { return CreateComInterfaceMarshallingInfo(_compilation, type); } public static MarshallingInfo CreateComInterfaceMarshallingInfo( Compilation compilation, ITypeSymbol interfaceType, TypePositionInfo? iidParameterIndexInfo = null) { INamedTypeSymbol? comInterfaceMarshaller = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_Marshalling_ComInterfaceMarshaller_Metadata); if (comInterfaceMarshaller is null) return NoMarshallingInfo.Instance; comInterfaceMarshaller = comInterfaceMarshaller.Construct(interfaceType); if (ManualTypeMarshallingHelper.HasEntryPointMarshallerAttribute(comInterfaceMarshaller)) { if (ManualTypeMarshallingHelper.TryGetValueMarshallersFromEntryType(comInterfaceMarshaller, interfaceType, compilation, out CustomTypeMarshallers? marshallers)) { // Filter the available marshallers based on the ComInterfaceOptions declared on the interface. // A [GeneratedComInterface] that only specifies ManagedObjectWrapper (CCW) supports data flow // in the managed-to-unmanaged direction, and one that only specifies ComObjectWrapper (RCW) // supports the unmanaged-to-managed direction. Restricting the CustomTypeMarshallers dictionary // to only the modes that match the supported direction lets the shared resolver machinery // (AttributedMarshallingModelGeneratorResolver) surface a build-time diagnostic when a caller // marshals in an unsupported direction. CustomTypeMarshallers filteredMarshallers = FilterMarshallersByComInterfaceOptions(marshallers.Value, interfaceType); if (iidParameterIndexInfo is not null) { return new IidParameterIndexNativeMarshallingInfo( EntryPointType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(comInterfaceMarshaller), Marshallers: filteredMarshallers, IidParameterIndexInfo: iidParameterIndexInfo); } return new NativeMarshallingAttributeInfo( EntryPointType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(comInterfaceMarshaller), Marshallers: filteredMarshallers); } } return NoMarshallingInfo.Instance; } private static CustomTypeMarshallers FilterMarshallersByComInterfaceOptions(CustomTypeMarshallers marshallers, ITypeSymbol interfaceType) { GetComInterfaceWrapperSupport(interfaceType, out bool supportsManagedObjectWrapper, out bool supportsComObjectWrapper); // Fast path: both wrappers are generated -> no directional restrictions. if (supportsManagedObjectWrapper && supportsComObjectWrapper) { return marshallers; } // ComInterfaceMarshaller<T> declares [CustomMarshaller(..., MarshalMode.Default, ...)], so // TryGetValueMarshallersFromEntryType produces a single Default entry. Expand it into // explicit per-direction entries only for the directions the interface actually supports, // and drop the Default fallback so ValidateCustomNativeTypeMarshallingSupported can report // the unsupported direction. if (!marshallers.Modes.TryGetValue(MarshalMode.Default, out CustomTypeMarshallerData defaultData)) { return marshallers; } ImmutableDictionary<MarshalMode, CustomTypeMarshallerData>.Builder builder = ImmutableDictionary.CreateBuilder<MarshalMode, CustomTypeMarshallerData>(); // Preserve any explicitly-defined non-Default entries. foreach (KeyValuePair<MarshalMode, CustomTypeMarshallerData> kvp in marshallers.Modes) { if (kvp.Key != MarshalMode.Default) { builder[kvp.Key] = kvp.Value; } } if (supportsManagedObjectWrapper) { // CCW: data flowing managed -> unmanaged is supported. builder[MarshalMode.ManagedToUnmanagedIn] = defaultData; builder[MarshalMode.UnmanagedToManagedOut] = defaultData; builder[MarshalMode.ElementIn] = defaultData; } if (supportsComObjectWrapper) { // RCW: data flowing unmanaged -> managed is supported. builder[MarshalMode.ManagedToUnmanagedOut] = defaultData; builder[MarshalMode.UnmanagedToManagedIn] = defaultData; builder[MarshalMode.ElementOut] = defaultData; } // Set a targeted reason string so ValidateCustomNativeTypeMarshallingSupported can emit // a more informative diagnostic explaining which ComInterfaceOptions flag is missing. string? reason = (!supportsComObjectWrapper) ? SR.Format(SR.ComObjectWrapperNotSpecifiedReason, interfaceType.Name) : SR.Format(SR.ManagedObjectWrapperNotSpecifiedReason, interfaceType.Name); return new CustomTypeMarshallers(builder.ToImmutable(), reason); } private static void GetComInterfaceWrapperSupport(ITypeSymbol interfaceType, out bool supportsManagedObjectWrapper, out bool supportsComObjectWrapper) { // Default when the attribute is absent, Options is not set, or Options is None (0) // matches GeneratedComInterfaceCompilationData.Options: both wrappers are generated. supportsManagedObjectWrapper = true; supportsComObjectWrapper = true; foreach (AttributeData attr in interfaceType.GetAttributes()) { if (attr.AttributeClass?.ToDisplayString() != TypeNames.GeneratedComInterfaceAttribute) { continue; } foreach (KeyValuePair<string, TypedConstant> namedArg in attr.NamedArguments) { if (namedArg.Key == "Options" && namedArg.Value.Value is int rawOptions && rawOptions != 0) { ComInterfaceOptions options = (ComInterfaceOptions)rawOptions; supportsManagedObjectWrapper = options.HasFlag(ComInterfaceOptions.ManagedObjectWrapper); supportsComObjectWrapper = options.HasFlag(ComInterfaceOptions.ComObjectWrapper); } } break; } } } }