Skip to content

Extend ComWrappers API to better support aggregation #44446

@AaronRobinsonMSFT

Description

@AaronRobinsonMSFT

Background and Motivation

The ComWrappers API is intended to permit external developers the ability to create standalone COM interop platforms. During the shut down of the .NET 5 release it was determined the COM Aggregation scenarios are difficult to handle properly. There are two updates that would assist making the scenario less magic and clearer to describe and document.

Proposed API

Modification (1) - Add 2 new values - Aggregation to indicate aggregation and Unwrap to indicate the IntPtr should be inspected if it is a CCW first - to the CreateObjectFlags enumeration.

namespace System.Runtime.InteropServices
{
    [System.FlagsAttribute]
    public enum CreateObjectFlags
    {
        None = 0,
        TrackerObject = 1,
        UniqueInstance = 2,
+       Aggregation = 4,
+       Unwrap = 8,
    }
}

Modification (2) - Add a new API overload for ComWrappers.GetOrRegisterObjectForComInstance() that accepts an inner value. The details of what this inner value represents are found in the official COM Aggregation documentation.

namespace System.Runtime.InteropServices
{
    public abstract class ComWrappers
    {
        public object GetOrRegisterObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, object wrapper);

+       public object GetOrRegisterObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, object wrapper, System.IntPtr inner);
    }
}

Usage Examples

The scenario in question is non-trivial and difficult to reduce to a comprehensible example - below is an annotated attempt.

// A globally accessible ComWrappers instance.
ComWrappers cw = ...;

object createdWrapper = ...;

// Create native outer based on createdWrapper.
IntPtr outer = ...;

// Create native instance and aggregate with the above outer.
// In the aggregation scenario it is required to request the IUnknown interface.
Guid clsid = typeof(Demo).GUID;
Guid IID_IUnknown = typeof(IUnknown).GUID;
IntPtr inner = Native.CoCreateInstance(ref clsid, ref IID_IUnknown, outer);

// Retrieve the default interface from the inner.
Guid iid = typeof(IDemo).GUID;
IntPtr defInterface;
Marshal.QueryInterface(inner, ref iid, out defInterface);

// Register the created wrapper for the supplied default interface.
cw.GetOrRegisterObjectForComInstance(defInterface, CreateObjectFlags.Aggregation, createdWrapper, inner);

Alternative Designs

No alternative designs were considered.

Risks

There are few if any risks. This is simply a better surface area for a cumbersome and poorly describable scenario.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions