Skip to content

Custom Marshallers never use stateful GetPinnableReference on struct #90621

Closed

Description

@jtschuster

Even when present, and in scenarios where the stateless GetPinnableReference would be used, the stateful method will not be used.

Activity

  1. added this to the 9.0.0 milestone on Aug 15, 2023
  2. jkoritzinsky commented on Aug 15, 2023

    @jkoritzinsky
    Member

    @jtschuster can you share the case where this occurs? I could have sworn that we have tests that validate this behavior in the LibraryImportGenerator.Tests test suite.

  3. jtschuster commented on Aug 15, 2023

    @jtschuster
    MemberAuthor

    In IStatefulPinnedMarshalling, with a stateless GetPinnableReference on the marshaller, the by value parameter RCW looks like this:

        void global::SharedTypes.ComInterfaces.IStatefulPinnedMarshalling.Method(global::SharedTypes.ComInterfaces.StatefulPinnedType param)
        {
            var(__this, __vtable_native) = ((System.Runtime.InteropServices.Marshalling.IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(typeof(global::SharedTypes.ComInterfaces.IStatefulPinnedMarshalling));
            int __invokeRetVal;
            // Pin - Pin data in preparation for calling the P/Invoke.
            fixed (void* __param_native = &global::SharedTypes.ComInterfaces.StatefulPinnedTypeMarshaller.GetPinnableReference(param))
            {
                __invokeRetVal = ((delegate* unmanaged[MemberFunction]<void*, nint, int> )__vtable_native[3])(__this, (nint)__param_native);
            }
    
            // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.
            System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(__invokeRetVal);
            System.GC.KeepAlive(this);
        }

    With only a stateful GetPinnableReference, it looks like this:

        void global::SharedTypes.ComInterfaces.IStatefulPinnedMarshalling.Method(global::SharedTypes.ComInterfaces.StatefulPinnedType param)
        {
            var(__this, __vtable_native) = ((System.Runtime.InteropServices.Marshalling.IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(typeof(global::SharedTypes.ComInterfaces.IStatefulPinnedMarshalling));
            nint __param_native = default;
            int __invokeRetVal = default;
            // Setup - Perform required setup.
            global::SharedTypes.ComInterfaces.StatefulPinnedTypeMarshaller __param_native__marshaller = new();
            try
            {
                // Marshal - Convert managed data to native data.
                __param_native__marshaller.FromManaged(param);
                // Pin - Pin data in preparation for calling the P/Invoke.
                fixed (void* __param_native__unused = __param_native__marshaller)
                {
                    // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned.
                    __param_native = __param_native__marshaller.ToUnmanaged();
                    __invokeRetVal = ((delegate* unmanaged[MemberFunction]<void*, nint, int> )__vtable_native[3])(__this, __param_native);
                }
    
                // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.
                System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(__invokeRetVal);
                __param_native__marshaller.OnInvoked();
                System.GC.KeepAlive(this);
            }
            finally
            {
                // CleanupCallerAllocated - Perform cleanup of caller allocated resources.
                __param_native__marshaller.Free();
            }
        }
  4. jtschuster commented on Aug 15, 2023

    @jtschuster
    MemberAuthor

    Looks like this test is supposed to, but it doesn't actually validate that GetPinnableReference was called.

  5. jkoritzinsky commented on Aug 16, 2023

    @jkoritzinsky
    Member

    No, that is calling GetPinnableReference. The GetPinnableReference call is part of the fixed (void* __param_native__unused = __param_native__marshaller) statement (it's implied by the C# language).

  6. ghost locked as resolved and limited conversation to collaborators on Sep 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions