Skip to content

[API Proposal]: Change visibility of ComWrappers.GetIUnknownImpl to public #80451

@jkoritzinsky

Description

@jkoritzinsky

Background and motivation

Any implementation of an unmanaged-to-managed vtable for a COM-style object needs to expose an IUnknown implementation in the vtable. The runtime provides an implementation for objects used with ComWrappers through the GetIUnknownImpl method. However, this method is currently designated as protected as the initial ComWrappers design presumed that the COM wrapper virtual method tables would be created/allocated in the ComputeVtables method.

Through implementation experience in CsWinRT and the upcoming COM source generator, we've discovered that for generated-code systems where the number of COM interfaces is not a bounded set (in comparison to hand-written ComWrappers implementations for a few interfaces), the generally accepted design is to allocate the vtable as a static field on the type itself. This produces a problem since GetIUnknownImpl is currently protected. This leads to every platform designing their own mechanism to expose the pointers returned by this method, effectively circumventing the protected nature of the API. We should just expose this API publicly so users don't need to manually provide their own implementations to expose the same values in a myriad of different ways.

API Proposal

namespace System.Runtime.InteropServices;

public abstract class ComWrappers
{
-    protected static void GetIUnknownImpl (out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease);
+    public static void GetIUnknownImpl (out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease);
}

API Usage

nint* vtable = (nint*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IInterfaceType), sizeof(nint) * 4);
ComWrappers.GetIUnknownImpl(out vtable[0], out vtable[1], out vtable[2]);
vtable[3] = (nint)(delegate* unmanaged[Stdcall, MemberFunction]<void*, int>)&ABI_Method;

Alternative Designs

We could introduce an API that returns a ReadOnlySpan<nint> that represents the IUnknown vtable already constructed from the three elements.

We could expose this API on a derived type of ComWrappers that is used by the new COM source generator instead of on the base ComWrappers type.

We could emit an internal version of this API into each assembly with the COM source generator.

We could emit a file-scoped version of this API into each assembly with the COM source generator with something like the following (does not add any API surface that needs to be reviewed):

file abstract class IUnknownVTableComWrappers : ComWrappers
{
	public static void GetIUnknownImpl(out void* pQueryInterface, out void* pAddRef, out void* pRelease)
    {
		nint qi, addRef, release;
        ComWrappers.GetIUnknownImpl(out qi, out addRef, out release);
        pQueryInterface = (void*)qi;
        pAddRef = (void*)addRef;
        pRelease = (void*)release;
    }
}

Risks

Low risk. The method is already protected. This would just make it a little more usable.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions