Repository navigation
Description
Background and motivation
For COM interop we expect to be able to cast RCWs to arbitrary [ComImport] interfaces. We use this and Type.IsComObject to implement TypeDescriptor functionality.
We can reimplement to support both legacy COM Interop and ComWrapppers if we have the ability to get IUnknown* for any ComWrappers generated RCWs.
Without this support we cannot enable trimming for WinForms without breaking critical features such as drag/drop/cut/paste and accessibility. This also prevents providing COM ComWrappers support with TypeDescriptor (which WinForms owns).
API Proposal
namespace System.Runtime.InteropServices;
public abstract class ComWrappers
{
+ public static bool TryGetComInstance(object obj, out void* unknown);
}API Usage
public static bool TryCreateComTypeDescriptor(object obj, out ComTypeDescriptor? descriptor)
{
void* unknown;
if (obj.GetType().IsCOMObject)
{
unknown = (void*)Marshal.GetIUnknownForObject(obj);
}
else
{
ComWrappers.TryGetComInstance(obj, out unknown);
}
return unknown is not null ? new ComTypeDescriptor(unknown) : null;
}Alternative Designs
One alternative (or addition) is to change all possible Marshal APIs that provide interface pointers to support ComWrappers RCWs:
namespace System.Runtime.InteropServices;
// Existing interface pointer methods
public static class Marshal
{
public static IntPtr GetComInterfaceForObject(object! o, Type! T);
public static IntPtr GetComInterfaceForObject(object! o, Type! T, CustomQueryInterfaceMode mode);
public static IntPtr GetComInterfaceForObject<T, TInterface>([DisallowNull] T o);
public static IntPtr GetComInterfaceForObjectInContext(object o, Type t);
public static IntPtr GetIDispatchForObject(object! o);
public static IntPtr GetIDispatchForObjectInContext(object o);
public static IntPtr GetIUnknownForObject(object! o);
public static IntPtr GetIUnknownForObjectInContext(object o);
}I don't know how plausible this is, but it would make writing consuming code much easier.
Risks
No response
Activity
JeremyKuhne commentedon Dec 14, 2022
cc: @lonitra, @AaronRobinsonMSFT, @jkoritzinsky, @LakshanF
related: #79674
AaronRobinsonMSFT commentedon Dec 14, 2022
This is similar to my comment in #79674. This would throw if there wasn't a globally registered
ComWrappersinstance. Which if there is one and it is known, this isn't an issue because one can callComWrappers.GetOrCreateObjectForComInstance().JeremyKuhne commentedon Dec 14, 2022
How does this help?
ComWrappersRCWs implement specific interfaces, not arbitrary[ComImport]interfaces with matching guids. We'd presumably have to publish all interfaces we use as "interchange types". We only have a few defined interfaces inComTypesand I don't believeIUnknownorIDispatchare defined anywhere. How would we publish these interfaces? WithHRESULTs explicitly exposed or not? With "marshalling" or not?For ActiveX Controls there are dozens of interfaces involved- and we allow providing your own instance of the class. In order for this to work (in the same way as before) we'd have to publish every interface we get off the root object and they would have to implement every single one of them. In this case I can't imagine that anyone is doing anything other than working with a wrapped
IUnknown.Beyond that there is a composition problem. We don't register our
ComWrappersand I don't know that we ever could as users would not be able to register their own. How do we expose an RCW that either we created ourselves (with our non-registeredComWrappers) or the user provided for us (throughComWrappersor existing RCWs)?Because we can't register our own
ComWrappersI don't see how we can implement anything around COM interfaces without directly using COM pointers.AaronRobinsonMSFT commentedon Dec 15, 2022
Spoke to @JeremyKuhne offline. I get this scenario now. The implementation rather trivial since we already query for this information when constructing a
WeakReference. The proposed alternative options aren't tenable due to semantic problems. The current semantics all guarantee they return a non-null value because the runtime would generate an CCW on the fly. The expectation here is the API will only return one if it exists, hence the "try" semantics.AaronRobinsonMSFT commentedon Jan 6, 2023
@JeremyKuhne Based on a private implementation here is what I would propose for the API along with details on use.
JeremyKuhne commentedon Jan 6, 2023
@AaronRobinsonMSFT that looks good to me.
terrajobst commentedon Jan 10, 2023
Video
Looks good as proposed.