dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.47k stars 4.77k forks source link

[ComInterfaceGenerator] PreserveSig with collection return type will not properly marshal back to caller #89893

Closed jtschuster closed 1 year ago

jtschuster commented 1 year ago

This is probably not a realistic situation, but we may want to fix it or warn when the [PreserveSig] return value unmarshaller doesn't take an int as the unmanaged type.

This method definition

        [PreserveSig]
        [return: MarshalUsing(CountElementName = nameof(size))]
        StatelessCollection<StatelessType> ReturnPreserveSig(int size);

will generate this unmanaged to managed method

    internal static global::SharedTypes.ComInterfaces.NativeCollection<global::SharedTypes.ComInterfaces.StatelessType> ABI_ReturnPreserveSig(System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, int size)
    {
        global::SharedTypes.ComInterfaces.IStatelessCollectionStatelessElement @this = default;
        global::SharedTypes.ComInterfaces.StatelessCollection<global::SharedTypes.ComInterfaces.StatelessType> __retVal = default;
        global::SharedTypes.ComInterfaces.NativeCollection<global::SharedTypes.ComInterfaces.StatelessType> __retVal_native = default;
        // Setup - Perform required setup.
        int __retVal_native__numElements;
        System.Runtime.CompilerServices.Unsafe.SkipInit(out __retVal_native__numElements);
        try
        {
            // Unmarshal - Convert native data to managed data.
            @this = System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance<global::SharedTypes.ComInterfaces.IStatelessCollectionStatelessElement>(__this_native);
            __retVal = @this.ReturnPreserveSig(size);
            // Marshal - Convert managed data to native data.
            __retVal_native = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller<global::SharedTypes.ComInterfaces.StatelessType, nint>.ManagedToUnmanaged.AllocateContainerForUnmanagedElements(__retVal, out __retVal_native__numElements);
            {
                System.ReadOnlySpan<global::SharedTypes.ComInterfaces.StatelessType> __retVal_native__managedSpan = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller<global::SharedTypes.ComInterfaces.StatelessType, nint>.ManagedToUnmanaged.GetManagedValuesSource(__retVal);
                System.Span<nint> __retVal_native__nativeSpan = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller<global::SharedTypes.ComInterfaces.StatelessType, nint>.ManagedToUnmanaged.GetUnmanagedValuesDestination(__retVal_native, __retVal_native__numElements);
                __retVal_native__nativeSpan.Clear();
                ; // Should be a for loop here that marshals elements
            }
        }
        catch (System.Exception __exception)
        {
            __retVal_native = System.Runtime.InteropServices.Marshalling.ExceptionAsDefaultMarshaller<global::SharedTypes.ComInterfaces.NativeCollection<global::SharedTypes.ComInterfaces.StatelessType>>.ConvertToUnmanaged(__exception);
        }

        return __retVal_native;
    }

For comparison, a regular managed return value

        [return: MarshalUsing(CountElementName = nameof(size))]
        StatelessCollection<StatelessType> Return(int size);

Generates this unmanaged to managed stub

    internal static int ABI_Return(System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, int size, global::SharedTypes.ComInterfaces.NativeCollection<global::SharedTypes.ComInterfaces.StatelessType>* __invokeRetValUnmanaged__param)
    {
        global::SharedTypes.ComInterfaces.IStatelessCollectionStatelessElement @this = default;
        ref global::SharedTypes.ComInterfaces.NativeCollection<global::SharedTypes.ComInterfaces.StatelessType> __invokeRetValUnmanaged = ref *__invokeRetValUnmanaged__param;
        global::SharedTypes.ComInterfaces.StatelessCollection<global::SharedTypes.ComInterfaces.StatelessType> __invokeRetVal = default;
        int __retVal = default;
        // Setup - Perform required setup.
        int __invokeRetValUnmanaged__numElements;
        System.Runtime.CompilerServices.Unsafe.SkipInit(out __invokeRetValUnmanaged__numElements);
        try
        {
            // Unmarshal - Convert native data to managed data.
            __retVal = 0; // S_OK
            @this = System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance<global::SharedTypes.ComInterfaces.IStatelessCollectionStatelessElement>(__this_native);
            __invokeRetVal = @this.Return(size);
            // Marshal - Convert managed data to native data.
            __invokeRetValUnmanaged = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller<global::SharedTypes.ComInterfaces.StatelessType, nint>.ManagedToUnmanaged.AllocateContainerForUnmanagedElements(__invokeRetVal, out __invokeRetValUnmanaged__numElements);
            {
                System.ReadOnlySpan<global::SharedTypes.ComInterfaces.StatelessType> __invokeRetValUnmanaged__managedSpan = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller<global::SharedTypes.ComInterfaces.StatelessType, nint>.ManagedToUnmanaged.GetManagedValuesSource(__invokeRetVal);
                System.Span<nint> __invokeRetValUnmanaged__nativeSpan = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller<global::SharedTypes.ComInterfaces.StatelessType, nint>.ManagedToUnmanaged.GetUnmanagedValuesDestination(__invokeRetValUnmanaged, __invokeRetValUnmanaged__numElements);
                __invokeRetValUnmanaged__nativeSpan.Clear();
                for (int __i0 = 0; __i0 < __invokeRetValUnmanaged__managedSpan.Length; ++__i0)
                {
                    __invokeRetValUnmanaged__nativeSpan[__i0] = global::SharedTypes.ComInterfaces.StatelessTypeMarshaller.ManagedToUnmanaged.ConvertToUnmanaged(__invokeRetValUnmanaged__managedSpan[__i0]);
                }
            }
        }
        catch (System.Exception __exception)
        {
            __retVal = System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller<int>.ConvertToUnmanaged(__exception);
        }

        return __retVal;
    }
ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/interop-contrib See info in area-owners.md if you want to be subscribed.

Issue Details
This is probably not a realistic situation, but we may want to fix it or warn when the [PreserveSig] return value unmarshaller doesn't take an int as the unmanaged type. This method definition ```C# [PreserveSig] [return: MarshalUsing(CountElementName = nameof(size))] StatelessCollection ReturnPreserveSig(int size); ``` will generate this unmanaged to managed method ```C# internal static global::SharedTypes.ComInterfaces.NativeCollection ABI_ReturnPreserveSig(System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, int size) { global::SharedTypes.ComInterfaces.IStatelessCollectionStatelessElement @this = default; global::SharedTypes.ComInterfaces.StatelessCollection __retVal = default; global::SharedTypes.ComInterfaces.NativeCollection __retVal_native = default; // Setup - Perform required setup. int __retVal_native__numElements; System.Runtime.CompilerServices.Unsafe.SkipInit(out __retVal_native__numElements); try { // Unmarshal - Convert native data to managed data. @this = System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance(__this_native); __retVal = @this.ReturnPreserveSig(size); // Marshal - Convert managed data to native data. __retVal_native = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller.ManagedToUnmanaged.AllocateContainerForUnmanagedElements(__retVal, out __retVal_native__numElements); { System.ReadOnlySpan __retVal_native__managedSpan = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller.ManagedToUnmanaged.GetManagedValuesSource(__retVal); System.Span __retVal_native__nativeSpan = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller.ManagedToUnmanaged.GetUnmanagedValuesDestination(__retVal_native, __retVal_native__numElements); __retVal_native__nativeSpan.Clear(); ; // Should be a for loop here that marshals elements } } catch (System.Exception __exception) { __retVal_native = System.Runtime.InteropServices.Marshalling.ExceptionAsDefaultMarshaller>.ConvertToUnmanaged(__exception); } return __retVal_native; } ``` For comparison, a regular managed return value ```C# [return: MarshalUsing(CountElementName = nameof(size))] StatelessCollection Return(int size); ``` Generates this unmanaged to managed stub ```C# internal static int ABI_Return(System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, int size, global::SharedTypes.ComInterfaces.NativeCollection* __invokeRetValUnmanaged__param) { global::SharedTypes.ComInterfaces.IStatelessCollectionStatelessElement @this = default; ref global::SharedTypes.ComInterfaces.NativeCollection __invokeRetValUnmanaged = ref *__invokeRetValUnmanaged__param; global::SharedTypes.ComInterfaces.StatelessCollection __invokeRetVal = default; int __retVal = default; // Setup - Perform required setup. int __invokeRetValUnmanaged__numElements; System.Runtime.CompilerServices.Unsafe.SkipInit(out __invokeRetValUnmanaged__numElements); try { // Unmarshal - Convert native data to managed data. __retVal = 0; // S_OK @this = System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance(__this_native); __invokeRetVal = @this.Return(size); // Marshal - Convert managed data to native data. __invokeRetValUnmanaged = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller.ManagedToUnmanaged.AllocateContainerForUnmanagedElements(__invokeRetVal, out __invokeRetValUnmanaged__numElements); { System.ReadOnlySpan __invokeRetValUnmanaged__managedSpan = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller.ManagedToUnmanaged.GetManagedValuesSource(__invokeRetVal); System.Span __invokeRetValUnmanaged__nativeSpan = global::SharedTypes.ComInterfaces.StatelessCollectionMarshaller.ManagedToUnmanaged.GetUnmanagedValuesDestination(__invokeRetValUnmanaged, __invokeRetValUnmanaged__numElements); __invokeRetValUnmanaged__nativeSpan.Clear(); for (int __i0 = 0; __i0 < __invokeRetValUnmanaged__managedSpan.Length; ++__i0) { __invokeRetValUnmanaged__nativeSpan[__i0] = global::SharedTypes.ComInterfaces.StatelessTypeMarshaller.ManagedToUnmanaged.ConvertToUnmanaged(__invokeRetValUnmanaged__managedSpan[__i0]); } } } catch (System.Exception __exception) { __retVal = System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller.ConvertToUnmanaged(__exception); } return __retVal; } ```
Author: jtschuster
Assignees: -
Labels: `area-System.Runtime.InteropServices`
Milestone: -