Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CloakedIid<T> for WinRT/COM interfaces #1308

Open
Sergio0694 opened this issue Mar 26, 2023 · 2 comments
Open

Add support for CloakedIid<T> for WinRT/COM interfaces #1308

Sergio0694 opened this issue Mar 26, 2023 · 2 comments
Labels
enhancement New feature or request

Comments

@Sergio0694
Copy link
Member

Sergio0694 commented Mar 26, 2023

Proposal: support CloakedIid for WinRT/COM interfaces

Summary

When defining a WinRT/COM class, WRL exposes the CloakedIid<T> helper, which allows interfaces to be hidden and only show up when explicitly queried for. That is, they wouldn't contribute to the list of implemented interfaces from IInspectable::GetIids. This is particularly common and useful for interop interfaces. Right now there's no way to express this with CsWinRT, as even using interface interfaces only makes it so the interface isn't explicitly visible in metadata, but if you retrieve a CCW for the object and call GetIids, those GUIDs would still show up. For instance, Win2D uses this pattern extensively (all the interop interfaces are implemented by the public WinRT objects as having a cloaked iid), but there's no way to replicate it with types authored in C#.

The proposal is to have a new attribute that would be recognized by the CsWinRT architecture, and would cause the corresponding interface to be skipped when computing the list of IIDs to return from the generated GetIids method from IInspectable:

namespace WinRT.Interop;

[AttributeUsage(AttributeTargets.AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class CloakedIidAttribute : Attribute
{
}

Rationale

  • Allow CCWs defined in C# with CsWinRT to have parity with CloakedIid<T> from WRL

Use example

Consider the following declarations:

// Some custom type
public sealed class MyType : MyInterface
{
}

// Some custom interface
[Guid("SOME_GUID")]
[CloakedIid]
[WindowsRuntimeType]
[WindowsRuntimeHelperType(typeof(Interface))]
public interface MyInterface
{
    [Guid("SOME_GUID")]
    public struct Vftbl
    {
        public static readonly IntPtr AbiToProjectionVftablePtr = InitVtbl();

        private static IntPtr InitVtbl()
        {
            Vftbl* lpVtbl = (Vftbl*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(Vftbl));

            lpVtbl->IUnknownVftbl = IUnknownVftbl.AbiToProjectionVftbl;

            return (IntPtr)lpVtbl;
        }

        internal IUnknownVftbl IUnknownVftbl;
    }
}

You would then get:

MyType myType = new();

// Get the CCW for the custom type
IntPtr myTypePtr = MarshalInspectable<MyType>.FromManaged(myType);

Guid myInterfaceIid = new Guid("SOME_GUID");

// Get the iids
int iidCount;
IntPtr iids;
IInspectable.Vftbl.AbiToProjectionVftable.GetIids(myTypePtr, &iidCount, &iids);

// This should print false
Console.WriteLine(new Span<Guid>((void*)iids, iidCount).Contains(myInterfaceIid));

// QI for the hidden interface
int result = Marshal.QueryInterface(myTypePtr, ref myInterfaceIid, out IntPtr myInterfacePtr);

// This should print 0
Console.WriteLine(result);

Open Questions

Not 100% clear to me whether this would be entirely on CsWinRT, or whether this should require any support from the ComWrappers side? If so, let me know whether I should move this to the runtime repo, or whether this is the right place for it 🙂

Also, the proposed API would make the cloaked iid feature opt-in per interface type. Should we consider a way to make this opt-in per concrete type on a given target interface, mirroring what WRL does? That seems more complex and potentially less trimmer friendly?

cc. @AaronRobinsonMSFT @jkoritzinsky @manodasanW

@Sergio0694 Sergio0694 added the enhancement New feature or request label Mar 26, 2023
@manodasanW
Copy link
Member

@Sergio0694 To make I understand the proposal, basically we want to be able to author types in C# (either using authoring support or even just a C# implemented class that is passed for an interface) and have it implement interfaces some of which you would be able to QI for if you know the IID but it will not get reported in GetIIDs calls.

In the Win2D scenario, I assume these interfaces are not represented in metadata either, or are they?

@Sergio0694
Copy link
Member Author

Sergio0694 commented Mar 30, 2023

Yup, that's exactly right! For Win2D, this is used for several interop interfaces, which are only declared either in the published header for Win2D or in some headers from the Windows SDK, but which are not part of metadata, yes. That is, in Win2D, those interfaces are only implemented by the type implementing the public WinRT interfaces, but they are not declared in IDL (so they also don't result in any projections being generated).

For instance, the public CanvasDevice type in Win2D (or rather, the implementation class for the public ICanvasDevice interface) uses CloakedIid to implement ID2D1DeviceContextPool, ICanvasResourceWrapper and IDxgiInterfaceAccess, which are all just COM interfaces defined in the Win2D/D2D headers. For custom effects, those have to implement the ICanvasImageInterop interface, also defined in the published header. You can get that to work today with CsWinRT (see #1283) through a custom interface with the right marshalling stubs to work with CsWinRT; but even if you make that interface internal, which makes it not leak through your managed API surface in .NET, it's still not really hidden at the ABI layer, as someone getting a CCW for that object and calling IInspectable::GetIIds will still see that IID. Using [CloakedIid] instead, we'd be able to make that interop interface "invisible", which is how you'd also implement it if you were authoring the custom effect in C++.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants