File: Marshalling\IidParameterIndexMarshallerResolver.cs
Web Access
Project: 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.

namespace Microsoft.Interop
{
    internal sealed class IidParameterIndexMarshallerResolver : IMarshallingGeneratorResolver
    {
        public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context)
        {
            if (info.MarshallingAttributeInfo is not IidParameterIndexNativeMarshallingInfo iidInfo
                || context.Direction != MarshalDirection.UnmanagedToManaged)
            {
                return ResolvedGenerator.UnresolvedGenerator;
            }

            return ResolvedGenerator.Resolved(new Marshaller(iidInfo.IidParameterIndexInfo).Bind(info, context));
        }

        private sealed class Marshaller(TypePositionInfo iidParameterIndexInfo) : IUnboundMarshallingGenerator
        {
            public ManagedTypeInfo AsNativeType(TypePositionInfo info) => new PointerTypeInfo("void*", "void*", false);

            public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => info.IsByRef ? SignatureBehavior.PointerToNativeType : SignatureBehavior.NativeType;

            public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context)
                => info.IsByRef ? ValueBoundaryBehavior.AddressOfNativeIdentifier : ValueBoundaryBehavior.NativeIdentifier;

            public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, TypePositionInfo info, out GeneratorDiagnostic? diagnostic)
                => ByValueMarshalKindSupportDescriptor.Default.GetSupport(marshalKind, info, out diagnostic);

            public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true;

            public void Generate(IndentedTextWriter writer, TypePositionInfo info, StubCodeContext codeContext, StubIdentifierContext context)
            {
                if (context.CurrentStage != StubIdentifierContext.Stage.Marshal)
                {
                    return;
                }

                (string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info);
                string unknownIdentifier = context.GetAdditionalIdentifier(info, "unknown");
                string queryInterfaceHResultIdentifier = context.GetAdditionalIdentifier(info, "queryInterfaceHResult");
                string queriedInterfaceIdentifier = context.GetAdditionalIdentifier(info, "queriedInterface");
                string iidExpression = MarshallerHelpers.GetIndexedManagedElementExpression(iidParameterIndexInfo, codeContext, context);

                writer.WriteLine($"void* {unknownIdentifier} = (void*)global::System.Runtime.InteropServices.Marshalling.ComInterfaceMarshaller<object>.ConvertToUnmanaged({managedIdentifier});");
                writer.WriteLine($"if ({unknownIdentifier} != null)");
                using (writer.WriteBlock())
                {
                    writer.WriteLine($"{TypeNames.GlobalAlias}{TypeNames.System_IntPtr} {queriedInterfaceIdentifier} = 0;");
                    writer.WriteLine($"int {queryInterfaceHResultIdentifier} = {TypeNames.GlobalAlias}{TypeNames.System_Runtime_InteropServices_Marshal}.QueryInterface(({TypeNames.GlobalAlias}{TypeNames.System_IntPtr}){unknownIdentifier}, in {iidExpression}, out {queriedInterfaceIdentifier});");
                    writer.WriteLine($"global::System.Runtime.InteropServices.Marshalling.ComInterfaceMarshaller<object>.Free({unknownIdentifier});");
                    writer.WriteLine($"if ({queryInterfaceHResultIdentifier} < 0)");
                    using (writer.WriteBlock())
                    {
                        writer.WriteLine($"if ({queriedInterfaceIdentifier} != 0)");
                        using (writer.WriteBlock())
                        {
                            writer.WriteLine($"{TypeNames.GlobalAlias}{TypeNames.System_Runtime_InteropServices_Marshal}.Release({queriedInterfaceIdentifier});");
                        }
                        writer.WriteLine($"{nativeIdentifier} = null;");
                        // The stub's exception marshaller converts the failure back to an HRESULT and runs cleanup.
                        writer.WriteLine($"{TypeNames.GlobalAlias}{TypeNames.System_Runtime_InteropServices_Marshal}.ThrowExceptionForHR({queryInterfaceHResultIdentifier});");
                    }
                    writer.WriteLine($"{nativeIdentifier} = (void*){queriedInterfaceIdentifier};");
                }
                writer.WriteLine("else");
                using (writer.WriteBlock())
                {
                    writer.WriteLine($"{nativeIdentifier} = null;");
                }
            }
        }
    }
}