Skip to content

COM source generation generates unexpected buffer marshal logic #118135

@AaronRobinsonMSFT

Description

@AaronRobinsonMSFT

The following source will generated an unmanaged to managed stub that doesn't work as expected.

// Definition
[GeneratedComInterface]
[Guid("8A2AF35B-D028-4191-A01F-3422AB0CF724")]
public partial interface IInterface1
{
    void M(
        int bufferSize,
        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] int[]? buffer1,
        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] int[]? buffer2);
}
// Usage from native
IInterface*1 i = ...
int buffer1[10];
i->M(10, buffer1, nullptr); // Note the second buffer is null, but using the same buffer length.

The generated marshalling logic for unmanaged to managed has a logical flaw in it. See comment below.

    [global::System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute(CallConvs = new[] { typeof(global::System.Runtime.CompilerServices.CallConvMemberFunction) })]
    internal static int ABI_M(global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, int bufferSize, int* __buffer1_native, int* __buffer2_native)
    {
        global::Program.IInterface1 @this = default;
        int[] buffer1 = default;
        int[] buffer2 = default;
        int __retVal = default;
        // Setup - Perform required setup.
        int __buffer2_native__numElements;
        global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __buffer2_native__numElements);
        int __buffer1_native__numElements;
        global::System.Runtime.CompilerServices.Unsafe.SkipInit(out __buffer1_native__numElements);
        try
        {
            // Unmarshal - Convert native data to managed data.
            global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.GetManagedValuesDestination(buffer2).Clear();

            // The below should likely be: "__buffer2_native == null ? 0 : bufferSize"
            __buffer2_native__numElements = bufferSize; 
            buffer2 = global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.AllocateContainerForManagedElements(__buffer2_native, __buffer2_native__numElements);
            global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.GetManagedValuesDestination(buffer1).Clear();

            // The below should likely be: "__buffer1_native == null ? 0 : bufferSize"
            __buffer1_native__numElements = bufferSize;
            buffer1 = global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.AllocateContainerForManagedElements(__buffer1_native, __buffer1_native__numElements);
            @this = global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance<global::Program.IInterface1>(__this_native);
            @this.M(bufferSize, buffer1, buffer2);
            // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.
            __retVal = 0; // S_OK
            // Marshal - Convert managed data to native data.
            global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.GetManagedValuesDestination(buffer2).CopyTo(global::System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref global::System.Runtime.InteropServices.MemoryMarshal.GetReference(global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.GetUnmanagedValuesSource(__buffer2_native, __buffer2_native__numElements)), global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.GetUnmanagedValuesSource(__buffer2_native, __buffer2_native__numElements).Length));
            global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.GetManagedValuesDestination(buffer1).CopyTo(global::System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref global::System.Runtime.InteropServices.MemoryMarshal.GetReference(global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.GetUnmanagedValuesSource(__buffer1_native, __buffer1_native__numElements)), global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<int, int>.GetUnmanagedValuesSource(__buffer1_native, __buffer1_native__numElements).Length));
        }
        catch (global::System.Exception __exception)
        {
            __retVal = global::System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller<int>.ConvertToUnmanaged(__exception);
        }

        return __retVal;
    }

Metadata

Metadata

Type

No type

Projects

Status

No status

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions