From 9ff6b7f1789c9fee5ee823d1e96370031e420d93 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 27 Jun 2026 20:16:23 -0700 Subject: [PATCH 1/3] Fix ActivateInstance parameter to reflect that it is not guaranteed to the default interface but rather could be IInspectable --- .../WindowsRuntimeActivationHelper.cs | 8 ++++---- src/WinRT.Runtime2/WindowsRuntimeObject.cs | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/Activation/WindowsRuntimeActivationHelper.cs b/src/WinRT.Runtime2/InteropServices/Activation/WindowsRuntimeActivationHelper.cs index c16f258c0a..c248753b52 100644 --- a/src/WinRT.Runtime2/InteropServices/Activation/WindowsRuntimeActivationHelper.cs +++ b/src/WinRT.Runtime2/InteropServices/Activation/WindowsRuntimeActivationHelper.cs @@ -20,7 +20,7 @@ internal static unsafe class WindowsRuntimeActivationHelper /// Activates a new Windows Runtime sealed instance. /// /// The for the IActivationFactory instance. - /// The resulting default interface pointer. + /// The resulting IInspectable interface pointer. /// Thrown if activating the instance fails. /// /// This shared factory helper can be used to activate Windows Runtime sealed types that have a parameterless constructor. @@ -28,15 +28,15 @@ internal static unsafe class WindowsRuntimeActivationHelper /// /// [MethodImpl(MethodImplOptions.NoInlining)] - public static void ActivateInstanceUnsafe(WindowsRuntimeObjectReference activationFactoryObjectReference, out void* defaultInterface) + public static void ActivateInstanceUnsafe(WindowsRuntimeObjectReference activationFactoryObjectReference, out void* inspectableInterface) { using WindowsRuntimeObjectReferenceValue activationFactoryValue = activationFactoryObjectReference.AsValue(); - fixed (void** defaultInterfacePtr = &defaultInterface) + fixed (void** inspectableInterfacePtr = &inspectableInterface) { HRESULT hresult = IActivationFactoryVftbl.ActivateInstanceUnsafe( thisPtr: activationFactoryValue.GetThisPtrUnsafe(), - instance: defaultInterfacePtr); + instance: inspectableInterfacePtr); RestrictedErrorInfo.ThrowExceptionForHR(hresult); } diff --git a/src/WinRT.Runtime2/WindowsRuntimeObject.cs b/src/WinRT.Runtime2/WindowsRuntimeObject.cs index 0f634f2acb..c48cdc9c31 100644 --- a/src/WinRT.Runtime2/WindowsRuntimeObject.cs +++ b/src/WinRT.Runtime2/WindowsRuntimeObject.cs @@ -88,9 +88,24 @@ protected WindowsRuntimeObject( // This constructor is only meant to be used for sealed types, so there's never a non-delegating (inner) return value. // See additional notes in the overload below for more details about how and when that parameter is necessary. + // The activation factory returns the 'IInspectable' interface for the activated instance, so we need to do a + // 'QueryInterface' for the default interface. WindowsRuntimeActivationHelper.ActivateInstanceUnsafe( activationFactoryObjectReference: activationFactoryObjectReference, - defaultInterface: out void* defaultInterface); + inspectableInterface: out void* inspectableInterface); + + void* defaultInterface; + + try + { + IUnknownVftbl.QueryInterfaceUnsafe(inspectableInterface, in iid, out defaultInterface).Assert(); + } + finally + { +#pragma warning disable IDE0058 // Return value is discarded, but can't use _ given we used it for the parameter. + IUnknownVftbl.ReleaseUnsafe(inspectableInterface); +#pragma warning restore IDE0058 + } // The inner interface pointer isn't used for non-composable types, so we just pass 'null' void* innerInterface = null; From 9c687644b31b7dbac022cc85ed073d38d7482b6a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jun 2026 17:07:41 -0700 Subject: [PATCH 2/3] Add ActivateInstanceUnsafe overload to return default interface Introduce an overload of WindowsRuntimeActivationHelper.ActivateInstanceUnsafe that accepts an IID and returns the default interface pointer. The new method activates the factory to get an IInspectable, queries for the requested interface via QueryInterface, and releases the temporary IInspectable. It preserves HRESULT handling via RestrictedErrorInfo and uses NoInlining attribute. This enables callers using the parameterless constructor path to obtain the actual default interface from activation factories. --- .../WindowsRuntimeActivationHelper.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/WinRT.Runtime2/InteropServices/Activation/WindowsRuntimeActivationHelper.cs b/src/WinRT.Runtime2/InteropServices/Activation/WindowsRuntimeActivationHelper.cs index c248753b52..cccb36366b 100644 --- a/src/WinRT.Runtime2/InteropServices/Activation/WindowsRuntimeActivationHelper.cs +++ b/src/WinRT.Runtime2/InteropServices/Activation/WindowsRuntimeActivationHelper.cs @@ -42,6 +42,45 @@ public static void ActivateInstanceUnsafe(WindowsRuntimeObjectReference activati } } + /// The IID of the default interface pointer (from the activation factory) to return. + /// The resulting default interface pointer. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ActivateInstanceUnsafe( + WindowsRuntimeObjectReference activationFactoryObjectReference, + in Guid iid, + out void* defaultInterface) + { + void* inspectableInterface; + + // Get the 'IInspectable' object from the activation factory (same as above) + using (WindowsRuntimeObjectReferenceValue activationFactoryValue = activationFactoryObjectReference.AsValue()) + { + HRESULT hresult = IActivationFactoryVftbl.ActivateInstanceUnsafe( + thisPtr: activationFactoryValue.GetThisPtrUnsafe(), + instance: &inspectableInterface); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + } + + // Query the 'IInspectable' object for the default interface, which is what callers expect. + // We only need this when using the parameterless constructor, since in this case we must + // go through 'IActivationFactory', which only declares 'IInspectable' as the return type + // for 'CreateInstance'. For other constructors instead, those would be declared on each + // specialized factory type, and would return the default interface directly. + try + { + fixed (void** defaultInterfacePtr = &defaultInterface) + { + IUnknownVftbl.QueryInterfaceUnsafe(inspectableInterface, in iid, out defaultInterface).Assert(); + } + } + finally + { + _ = IUnknownVftbl.ReleaseUnsafe(inspectableInterface); + } + } + /// /// Activates a new Windows Runtime instance. /// From 6e28ee5739fec1cc66a237fb9cbff08483db3dba Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jun 2026 17:07:52 -0700 Subject: [PATCH 3/3] Use ActivateInstanceUnsafe to return default interface Simplify sealed-type activation by having WindowsRuntimeActivationHelper.ActivateInstanceUnsafe return the default interface directly. Removes manual QueryInterface/Release logic and related temporary pointer handling, reducing complexity and potential lifetime errors during activation. --- src/WinRT.Runtime2/WindowsRuntimeObject.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/WinRT.Runtime2/WindowsRuntimeObject.cs b/src/WinRT.Runtime2/WindowsRuntimeObject.cs index c48cdc9c31..2085508333 100644 --- a/src/WinRT.Runtime2/WindowsRuntimeObject.cs +++ b/src/WinRT.Runtime2/WindowsRuntimeObject.cs @@ -88,24 +88,10 @@ protected WindowsRuntimeObject( // This constructor is only meant to be used for sealed types, so there's never a non-delegating (inner) return value. // See additional notes in the overload below for more details about how and when that parameter is necessary. - // The activation factory returns the 'IInspectable' interface for the activated instance, so we need to do a - // 'QueryInterface' for the default interface. WindowsRuntimeActivationHelper.ActivateInstanceUnsafe( activationFactoryObjectReference: activationFactoryObjectReference, - inspectableInterface: out void* inspectableInterface); - - void* defaultInterface; - - try - { - IUnknownVftbl.QueryInterfaceUnsafe(inspectableInterface, in iid, out defaultInterface).Assert(); - } - finally - { -#pragma warning disable IDE0058 // Return value is discarded, but can't use _ given we used it for the parameter. - IUnknownVftbl.ReleaseUnsafe(inspectableInterface); -#pragma warning restore IDE0058 - } + iid: in iid, + defaultInterface: out void* defaultInterface); // The inner interface pointer isn't used for non-composable types, so we just pass 'null' void* innerInterface = null;