Skip to content

[API Proposal]: ComWrappers API to only check for an existing RCW #113581

@AaronRobinsonMSFT

Description

@AaronRobinsonMSFT

Background and motivation

A current API exists for taking in an IntPtr to an IUnknown and checking if it is a CCW (COM Callable Wrapper). Adding a symmetrical API for checking if there is an existing wrapper for an RCW (Runtime Callable Wrapper) is also potentially interesting since it could be the case that users may want to perform a non-exception throwing check prior to calling ComWrappers.GetOrRegisterObjectForComInstance() or ComWrappers.GetOrCreateObjectForComInstance().

API Proposal

The proposal here is to reuse the TryGetObject() pattern, but for a native IUnknown as opposed to check if the IUnknown is an existing CCW (wrapper of a managed object).

namespace System.Runtime.InteropServices;

public abstract class ComWrappers
{
+    public static bool TryGetObject(IntPtr externalComObject, ComWrappers comWrappers, out object? wrapper);   
}

API Usage

public static MyCallbackApi(IntPtr unknown)
{
   if (!ComWrappers.TryGetObject(unknown, g_comWrapper, out object? existingWrapper))
   {
       //
       // Do some inspection of the unknown and/or perform actions that need to be done
       // prior to actually attempting the creation.
       //
       existingWrapper = g_comWrapper.GetOrCreateObjectForComInstance(unknown, CreateObjectFlags.None);
   }
}

Alternative Designs

Convert to instance method

public bool TryGetObject(IntPtr externalComObject, out object? wrapper);

Follow the existing pattern and create a "Try" version

public bool TryGetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, [NotNullWhen(true)] out object? wrapper);  // Instance

Risks

This is for a niche WinRT scenario. It has no other obvious uses at present. It does however create a symmetrical API for both CCWs and RCWs.

The API has a trivial implementation, but does introduce an inherent race condition around what is in essence a concurrent dictionary (RCW cache). The "try" operation is checking for state that might be addressed by another thread calling the GetOrCreateObjectForComInstance or GetOrRegisterObjectForComInstance. This can be mitigated if the user's ComWrappers implementation ensures that it would always create a wrapper for an IUnknown instance based solely on the state of that IUnknown, but that is a hard requirement to enforce.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions