From 0020cc86b573e219b9224f5a0f327a6176f35716 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Sat, 4 Nov 2023 18:02:34 -0700 Subject: [PATCH 01/10] Include CsWinRT.exe in the UpToDateCheck. --- nuget/Microsoft.Windows.CsWinRT.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/nuget/Microsoft.Windows.CsWinRT.targets b/nuget/Microsoft.Windows.CsWinRT.targets index 7a5dcf1f8..043188691 100644 --- a/nuget/Microsoft.Windows.CsWinRT.targets +++ b/nuget/Microsoft.Windows.CsWinRT.targets @@ -199,6 +199,7 @@ $(CsWinRTInternalProjection) + From 65aabe2dafe29e6420a06865f58be33b48fb83c3 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Sat, 4 Nov 2023 18:03:15 -0700 Subject: [PATCH 02/10] Fix test that enables apartment specific object to be created from other apartmwent if the first instance was created on the STA. --- src/Tests/UnitTest/TestComponentCSharp_Tests.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index ace0ced14..57611d98d 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -2438,8 +2438,6 @@ public void AcquireObject() // Object gets proxied to the apartment. Assert.Equal(2, proxyObject.Commands.Count); agileReference.Dispose(); - - proxyObject2 = agileReference2.Get(); } public void CheckValue() @@ -2448,9 +2446,6 @@ public void CheckValue() Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState()); proxyObject = agileReference.Get(); Assert.Equal(2, proxyObject.Commands.Count); - - nonAgileObject2 = new Windows.UI.Popups.PopupMenu(); - agileReference2 = nonAgileObject2.AsAgile(); valueAcquired.Set(); } @@ -2462,8 +2457,8 @@ public void CallProxyObject() Assert.ThrowsAny(() => proxyObject.Commands); } - private Windows.UI.Popups.PopupMenu nonAgileObject, nonAgileObject2; - private Windows.UI.Popups.PopupMenu proxyObject, proxyObject2; + private Windows.UI.Popups.PopupMenu nonAgileObject; + private Windows.UI.Popups.PopupMenu proxyObject; private AgileReference agileReference, agileReference2; private readonly AutoResetEvent objectAcquired = new AutoResetEvent(false); private readonly AutoResetEvent valueAcquired = new AutoResetEvent(false); From 0258835c6ed666fc40f524c48cda488fbc6f2652 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Sat, 4 Nov 2023 19:04:37 -0700 Subject: [PATCH 03/10] Define more InterfaceIIDs. --- src/WinRT.Runtime/ComWrappersSupport.cs | 16 ++++++++-------- .../Interop/IAgileReference.net5.cs | 2 +- src/WinRT.Runtime/Interop/IContextCallback.cs | 2 +- src/WinRT.Runtime/Interop/IMarshal.cs | 2 +- .../IWeakReferenceSource.netstandard2.0.cs | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/WinRT.Runtime/ComWrappersSupport.cs b/src/WinRT.Runtime/ComWrappersSupport.cs index 42164f9fd..528859622 100644 --- a/src/WinRT.Runtime/ComWrappersSupport.cs +++ b/src/WinRT.Runtime/ComWrappersSupport.cs @@ -69,16 +69,16 @@ public static void MarshalDelegateInvoke(IntPtr thisPtr, Action invoke) // This can either be if the object implements IAgileObject or the free threaded marshaler. internal unsafe static bool IsFreeThreaded(IObjectReference objRef) { - if (objRef.TryAs(ABI.WinRT.Interop.IAgileObject.IID, out var agilePtr) >= 0) + if (objRef.TryAs(InterfaceIIDs.IAgileObject_IID, out var agilePtr) >= 0) { Marshal.Release(agilePtr); return true; } - else if (objRef.TryAs(ABI.WinRT.Interop.IMarshal.IID, out var marshalPtr) >= 0) + else if (objRef.TryAs(InterfaceIIDs.IMarshal_IID, out var marshalPtr) >= 0) { try { - Guid iid_IUnknown = IUnknownVftbl.IID; + Guid iid_IUnknown = InterfaceIIDs.IUnknown_IID; Guid iid_unmarshalClass; Marshal.ThrowExceptionForHR((**(ABI.WinRT.Interop.IMarshal.Vftbl**)marshalPtr).GetUnmarshalClass_0( marshalPtr, &iid_IUnknown, IntPtr.Zero, MSHCTX.InProc, IntPtr.Zero, MSHLFLAGS.Normal, &iid_unmarshalClass)); @@ -229,7 +229,7 @@ internal static List GetInterfaceTableEntries( Vtable = (IntPtr)ifaceAbiType.GetAbiToProjectionVftblPtr() }); - if (!hasCustomIMarshalInterface && iid == ABI.WinRT.Interop.IMarshal.IID) + if (!hasCustomIMarshalInterface && iid == InterfaceIIDs.IMarshal_IID) { hasCustomIMarshalInterface = true; } @@ -298,7 +298,7 @@ internal static List GetInterfaceTableEntries( entries.Add(new ComInterfaceEntry { - IID = ABI.WinRT.Interop.IWeakReferenceSource.IID, + IID = InterfaceIIDs.IWeakReferenceSource_IID, Vtable = ABI.WinRT.Interop.IWeakReferenceSource.AbiToProjectionVftablePtr }); @@ -308,7 +308,7 @@ internal static List GetInterfaceTableEntries( { entries.Add(new ComInterfaceEntry { - IID = ABI.WinRT.Interop.IMarshal.IID, + IID = InterfaceIIDs.IMarshal_IID, Vtable = ABI.WinRT.Interop.IMarshal.Vftbl.AbiToProjectionVftablePtr }); } @@ -316,7 +316,7 @@ internal static List GetInterfaceTableEntries( // Add IAgileObject to all CCWs entries.Add(new ComInterfaceEntry { - IID = ABI.WinRT.Interop.IAgileObject.IID, + IID = InterfaceIIDs.IAgileObject_IID, Vtable = IUnknownVftbl.AbiToProjectionVftblPtr }); @@ -329,7 +329,7 @@ internal static List GetInterfaceTableEntries( // This should be the last entry as it is included / excluded based on the flags. entries.Add(new ComInterfaceEntry { - IID = IUnknownVftbl.IID, + IID = InterfaceIIDs.IUnknown_IID, Vtable = IUnknownVftbl.AbiToProjectionVftblPtr }); diff --git a/src/WinRT.Runtime/Interop/IAgileReference.net5.cs b/src/WinRT.Runtime/Interop/IAgileReference.net5.cs index 78ff5392b..33dbee6cb 100644 --- a/src/WinRT.Runtime/Interop/IAgileReference.net5.cs +++ b/src/WinRT.Runtime/Interop/IAgileReference.net5.cs @@ -24,7 +24,7 @@ internal interface IAgileReference #endif interface IAgileObject { - public static readonly Guid IID = new(0x94ea2b94, 0xe9cc, 0x49e0, 0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90); + public static readonly Guid IID = InterfaceIIDs.IAgileObject_IID; } [WindowsRuntimeType] diff --git a/src/WinRT.Runtime/Interop/IContextCallback.cs b/src/WinRT.Runtime/Interop/IContextCallback.cs index 3e24df1fa..d98815f9d 100644 --- a/src/WinRT.Runtime/Interop/IContextCallback.cs +++ b/src/WinRT.Runtime/Interop/IContextCallback.cs @@ -39,7 +39,7 @@ namespace ABI.WinRT.Interop [Guid("000001da-0000-0000-C000-000000000046")] internal sealed unsafe class IContextCallback : global::WinRT.Interop.IContextCallback { - internal static readonly Guid IID = new(0x000001da, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IID = InterfaceIIDs.IContextCallback_IID; [Guid("000001da-0000-0000-C000-000000000046")] public struct Vftbl diff --git a/src/WinRT.Runtime/Interop/IMarshal.cs b/src/WinRT.Runtime/Interop/IMarshal.cs index dbd421014..36139aa96 100644 --- a/src/WinRT.Runtime/Interop/IMarshal.cs +++ b/src/WinRT.Runtime/Interop/IMarshal.cs @@ -36,7 +36,7 @@ namespace ABI.WinRT.Interop [Guid("00000003-0000-0000-c000-000000000046")] internal sealed class IMarshal { - internal static readonly Guid IID = new(0x00000003, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IID = InterfaceIIDs.IMarshal_IID; [DllImport("api-ms-win-core-com-l1-1-0.dll")] private static extern int CoCreateFreeThreadedMarshaler(IntPtr outer, out IntPtr marshalerPtr); diff --git a/src/WinRT.Runtime/Interop/IWeakReferenceSource.netstandard2.0.cs b/src/WinRT.Runtime/Interop/IWeakReferenceSource.netstandard2.0.cs index 15cb04999..ec1f02265 100644 --- a/src/WinRT.Runtime/Interop/IWeakReferenceSource.netstandard2.0.cs +++ b/src/WinRT.Runtime/Interop/IWeakReferenceSource.netstandard2.0.cs @@ -99,7 +99,7 @@ private static int Do_Abi_GetWeakReference(IntPtr thisPtr, IntPtr* weakReference } } - internal static readonly Guid IID = new(0x00000038, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IID = InterfaceIIDs.IWeakReferenceSource_IID; public static IntPtr AbiToProjectionVftablePtr => Vftbl.AbiToProjectionVftablePtr; public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); From a2b04df0e33037c2a30652d962924d92578dab33 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Sat, 4 Nov 2023 23:08:18 -0700 Subject: [PATCH 04/10] Simplify factory caching by using a FactoryObjectReference type that tracks the context. --- src/WinRT.Runtime/Context.cs | 12 +- .../Projections/DataErrorsChangedEventArgs.cs | 14 +- .../NotifyCollectionChangedEventArgs.cs | 16 +- .../Projections/PropertyChangedEventArgs.cs | 14 +- src/WinRT.Runtime/Projections/Uri.cs | 14 +- src/cswinrt/code_writers.h | 677 ++++++++++-------- src/cswinrt/main.cpp | 3 +- src/cswinrt/strings/WinRT.cs | 274 +++++-- src/cswinrt/strings/WinRT_Interop.cs | 3 +- 9 files changed, 587 insertions(+), 440 deletions(-) diff --git a/src/WinRT.Runtime/Context.cs b/src/WinRT.Runtime/Context.cs index 1b5dc5ffb..ba138e313 100644 --- a/src/WinRT.Runtime/Context.cs +++ b/src/WinRT.Runtime/Context.cs @@ -7,11 +7,8 @@ namespace WinRT { - static class Context + static partial class Context { - [DllImport("api-ms-win-core-com-l1-1-0.dll")] - private static extern unsafe int CoGetContextToken(IntPtr* contextToken); - [DllImport("api-ms-win-core-com-l1-1-0.dll")] private static extern int CoGetObjectContext(ref Guid riid, out IntPtr ppv); @@ -24,13 +21,6 @@ public static IntPtr GetContextCallback() return contextCallbackPtr; } - public unsafe static IntPtr GetContextToken() - { - IntPtr contextToken; - Marshal.ThrowExceptionForHR(CoGetContextToken(&contextToken)); - return contextToken; - } - // Calls the given callback in the right context. // On any exception, calls onFail callback if any set. // If not set, exception is handled due to today we don't diff --git a/src/WinRT.Runtime/Projections/DataErrorsChangedEventArgs.cs b/src/WinRT.Runtime/Projections/DataErrorsChangedEventArgs.cs index 98793d74e..b44570354 100644 --- a/src/WinRT.Runtime/Projections/DataErrorsChangedEventArgs.cs +++ b/src/WinRT.Runtime/Projections/DataErrorsChangedEventArgs.cs @@ -91,15 +91,7 @@ namespace ABI.System.ComponentModel #endif unsafe struct DataErrorsChangedEventArgs { - private sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base("Microsoft.UI.Xaml.Data", "Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs") - { - } - - internal static ABI.Microsoft.UI.Xaml.Data.WinRTDataErrorsChangedEventArgsRuntimeClassFactory Instance = - new ActivationFactory()._As(); - } + private static ABI.Microsoft.UI.Xaml.Data.WinRTDataErrorsChangedEventArgsRuntimeClassFactory Instance = ActivationFactory.Get("Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs"); public static IObjectReference CreateMarshaler(global::System.ComponentModel.DataErrorsChangedEventArgs value) { @@ -108,7 +100,7 @@ public static IObjectReference CreateMarshaler(global::System.ComponentModel.Dat return null; } - return ActivationFactory.Instance.CreateInstance(value.PropertyName); + return Instance.CreateInstance(value.PropertyName); } public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentModel.DataErrorsChangedEventArgs value) @@ -118,7 +110,7 @@ public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentMode return new ObjectReferenceValue(); } - return ActivationFactory.Instance.CreateInstanceForMarshaling(value.PropertyName); + return Instance.CreateInstanceForMarshaling(value.PropertyName); } public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; diff --git a/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventArgs.cs b/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventArgs.cs index 448d6f602..5104654be 100644 --- a/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventArgs.cs +++ b/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventArgs.cs @@ -194,16 +194,8 @@ namespace ABI.System.Collections.Specialized public #endif struct NotifyCollectionChangedEventArgs - { - private sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base("Microsoft.UI.Xaml.Interop", "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs") - { - } - - internal static WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory Instance = - new ActivationFactory()._As(); - } + { + private static WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory Instance = ActivationFactory.Get("Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs"); public static IObjectReference CreateMarshaler(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs value) { @@ -212,7 +204,7 @@ public static IObjectReference CreateMarshaler(global::System.Collections.Specia return null; } - return ActivationFactory.Instance.CreateInstanceWithAllParameters(value.Action, value.NewItems, value.OldItems, value.NewStartingIndex, value.OldStartingIndex, null, out _); + return Instance.CreateInstanceWithAllParameters(value.Action, value.NewItems, value.OldItems, value.NewStartingIndex, value.OldStartingIndex, null, out _); } public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs value) @@ -222,7 +214,7 @@ public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.S return new ObjectReferenceValue(); } - return ActivationFactory.Instance.CreateInstanceWithAllParameters(value.Action, value.NewItems, value.OldItems, value.NewStartingIndex, value.OldStartingIndex); + return Instance.CreateInstanceWithAllParameters(value.Action, value.NewItems, value.OldItems, value.NewStartingIndex, value.OldStartingIndex); } public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; diff --git a/src/WinRT.Runtime/Projections/PropertyChangedEventArgs.cs b/src/WinRT.Runtime/Projections/PropertyChangedEventArgs.cs index a29aafc68..0831eb895 100644 --- a/src/WinRT.Runtime/Projections/PropertyChangedEventArgs.cs +++ b/src/WinRT.Runtime/Projections/PropertyChangedEventArgs.cs @@ -102,15 +102,7 @@ namespace ABI.System.ComponentModel #endif unsafe struct PropertyChangedEventArgs { - private sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base("Microsoft.UI.Xaml.Data", "Microsoft.UI.Xaml.Data.PropertyChangedEventArgs") - { - } - - internal static ABI.Microsoft.UI.Xaml.Data.WinRTPropertyChangedEventArgsRuntimeClassFactory Instance = - new ActivationFactory()._As(); - } + private static ABI.Microsoft.UI.Xaml.Data.WinRTPropertyChangedEventArgsRuntimeClassFactory Instance = ActivationFactory.Get("Microsoft.UI.Xaml.Data.PropertyChangedEventArgs"); public static IObjectReference CreateMarshaler(global::System.ComponentModel.PropertyChangedEventArgs value) { @@ -119,7 +111,7 @@ public static IObjectReference CreateMarshaler(global::System.ComponentModel.Pro return null; } - return ActivationFactory.Instance.CreateInstance(value.PropertyName, null, out _); + return Instance.CreateInstance(value.PropertyName, null, out _); } public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentModel.PropertyChangedEventArgs value) @@ -129,7 +121,7 @@ public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentMode return new ObjectReferenceValue(); } - return ActivationFactory.Instance.CreateInstance(value.PropertyName); + return Instance.CreateInstance(value.PropertyName); } public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; diff --git a/src/WinRT.Runtime/Projections/Uri.cs b/src/WinRT.Runtime/Projections/Uri.cs index d375c4541..a830084f5 100644 --- a/src/WinRT.Runtime/Projections/Uri.cs +++ b/src/WinRT.Runtime/Projections/Uri.cs @@ -96,15 +96,7 @@ public unsafe ObjectReferenceValue CreateUriForMarshaling(string uri) #endif unsafe struct Uri { - private sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base("Windows.Foundation", "Windows.Foundation.Uri") - { - } - - internal static WinRTUriRuntimeClassFactory Instance = - new ActivationFactory()._As(); - } + private static WinRTUriRuntimeClassFactory Instance = ActivationFactory.Get("Windows.Foundation.Uri"); public static IObjectReference CreateMarshaler(global::System.Uri value) { @@ -113,7 +105,7 @@ public static IObjectReference CreateMarshaler(global::System.Uri value) return null; } - return ActivationFactory.Instance.CreateUri(value.OriginalString); + return Instance.CreateUri(value.OriginalString); } public static ObjectReferenceValue CreateMarshaler2(global::System.Uri value) @@ -123,7 +115,7 @@ public static ObjectReferenceValue CreateMarshaler2(global::System.Uri value) return new ObjectReferenceValue(); } - return ActivationFactory.Instance.CreateUriForMarshaling(value.OriginalString); + return Instance.CreateUriForMarshaling(value.OriginalString); } public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index f9826a891..ac8955040 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -950,7 +950,7 @@ namespace cswinrt if (paramsForStaticMethodCall.has_value()) { w.write("%", bind(paramsForStaticMethodCall.value().first, paramsForStaticMethodCall.value().second, - w.write_temp("%", bind(paramsForStaticMethodCall.value().first)))); + w.write_temp("%", method_target))); } else { @@ -1117,8 +1117,9 @@ namespace cswinrt auto static_method_params = call_static_method.has_value() ? std::optional(std::pair(call_static_method.value(), method)) : std::nullopt; if (!is_private) { - write_method(w, signature, method.Name(), return_type, interface_member, access_spec, method_spec, platform_attribute, - static_method_params); + write_method(w, signature, method.Name(), return_type, + static_method_params.has_value() ? w.write_temp("%", bind(static_method_params.value().first)) : interface_member, + access_spec, method_spec, platform_attribute, static_method_params); } if (is_overridable || !is_exclusive_to(method.Parent())) @@ -1174,7 +1175,7 @@ namespace cswinrt if (params_for_static_getter.has_value()) { w.write("%", bind(params_for_static_getter.value().first, params_for_static_getter.value().second, - w.write_temp("%", bind(params_for_static_getter.value().first)))); + w.write_temp("%", getter_target))); } else { @@ -1209,7 +1210,7 @@ namespace cswinrt if (params_for_static_getter.has_value()) { w.write("%", bind(params_for_static_getter.value().first, params_for_static_getter.value().second, - w.write_temp("%", bind(params_for_static_getter.value().first)))); + w.write_temp("%", getter_target))); } else { @@ -1221,7 +1222,7 @@ namespace cswinrt if (params_for_static_setter.has_value()) { w.write("%", bind(params_for_static_setter.value().first, params_for_static_setter.value().second, - w.write_temp("%", bind(params_for_static_setter.value().first)))); + w.write_temp("%", setter_target))); } else { @@ -1359,7 +1360,7 @@ remove => %; { auto&& [iface_type_semantics, _, is_static] = paramsForStaticMethodCall.value(); w.write("%", bind(iface_type_semantics, event, true, - w.write_temp("%", bind(iface_type_semantics)), is_static)); + w.write_temp("%", event_target), is_static)); } else { @@ -1371,7 +1372,7 @@ remove => %; { auto&& [iface_type_semantics, _, is_static] = paramsForStaticMethodCall.value(); w.write("%", bind(iface_type_semantics, event, false, - w.write_temp("%", bind(iface_type_semantics)), is_static)); + w.write_temp("%", event_target), is_static)); } else { @@ -1403,7 +1404,9 @@ remove => %; bool is_private = is_implemented_as_private_method(w, class_type, add); if (!is_private) { - write_event(w, event.Name(), event, interface_member, visibility, ""sv, platform_attribute, call_static_method.has_value() ? std::optional(std::tuple(call_static_method.value(), event, false)) : std::nullopt); + write_event(w, event.Name(), event, + call_static_method.has_value() ? w.write_temp("%", bind(call_static_method.value())) : interface_member, + visibility, ""sv, platform_attribute, call_static_method.has_value() ? std::optional(std::tuple(call_static_method.value(), event, false)) : std::nullopt); } if (is_overridable || !is_exclusive_to(event.Parent())) @@ -1805,87 +1808,187 @@ remove => %; return result; } - void write_composing_factory_method(writer& w, MethodDef const& method); + void write_guid_bytes(writer& w, TypeDef const& type) + { + auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); + if (!attribute) + { + throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); + } - void write_abi_method_with_raw_return_type(writer& w, MethodDef const& method); + auto args = attribute.Value().FixedArgs(); - template - std::string write_factory_cache_object(writer& w, TypeDef const& factory_type, TypeDef const& class_type); + using std::get; + + auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; + + w.write_printf(R"(0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X)", + (get(get_arg(0)) >> 0) & 0xFF, + (get(get_arg(0)) >> 8) & 0xFF, + (get(get_arg(0)) >> 16) & 0xFF, + (get(get_arg(0)) >> 24) & 0xFF, + (get(get_arg(1)) >> 0) & 0xFF, + (get(get_arg(1)) >> 8) & 0xFF, + (get(get_arg(2)) >> 0) & 0xFF, + (get(get_arg(2)) >> 8) & 0xFF, + get(get_arg(3)), + get(get_arg(4)), + get(get_arg(5)), + get(get_arg(6)), + get(get_arg(7)), + get(get_arg(8)), + get(get_arg(9)), + get(get_arg(10))); + } - std::string write_static_cache_object(writer& w, std::string_view cache_type_name, TypeDef const& class_type) + void write_class_static_cache_definition(writer& w, TypeDef const& staticsType, TypeDef const& classType) { - auto instance = - w.write_temp( - "private static readonly _% _instance = new _%();", - cache_type_name, - cache_type_name); - auto factoryAs = w.write_temp("%", - bind([&](writer& w) - { - if (is_static(class_type)) - { - w.write("_factory._As"); - } - else - { - w.write("ActivationFactory<%>.As", class_type.TypeName()); - } - }) - ); + if (staticsType) + { + auto factory_class_name = settings.netstandard_compat ? + w.write_temp("BaseFactory<%.Vftbl>", bind(staticsType, typedef_name_type::ABI, true)) : + w.write_temp("BaseFactory"); + + auto statics_type_name = staticsType.TypeName(); + w.write(R"( +private static % _% = new %("%.%", %.IID); +)", + factory_class_name, + statics_type_name, + factory_class_name, + classType.TypeNamespace(), + classType.TypeName(), + bind(staticsType, typedef_name_type::StaticAbiClass, true)); + } + } + void write_activation_factory_objref_definition(writer& w, TypeDef const& classType) + { + auto objrefname = w.write_temp("%", bind(classType)); + w.write(R"( +private static volatile FactoryObjectReference __%; +private static FactoryObjectReference Make__%() +{ + global::System.Threading.Interlocked.CompareExchange(ref __%, ActivationFactory.Get("%.%"), null); + return __%; +} +private static FactoryObjectReference % +{ + get + { + var factory = __%; + if (factory != null && factory.IsObjectInContext()) + { + return factory; + } + else + { + return Make__%(); + } + } +} +)", + objrefname, + objrefname, + objrefname, + classType.TypeNamespace(), + classType.TypeName(), + objrefname, + objrefname, + objrefname, + objrefname); + } + + void write_static_objref_definition(writer& w, std::string_view const& vftblType, TypeDef const& staticsType, TypeDef const& classType) + { if (settings.netstandard_compat) - { - auto cache_vftbl_type = w.write_temp("ABI.%.%.Vftbl", - class_type.TypeNamespace(), - cache_type_name); - auto cache_interface = - w.write_temp( - R"(%<%>)", - factoryAs, - cache_vftbl_type); + { + auto objrefname = w.write_temp("%", bind(staticsType)); w.write(R"( -internal sealed class _% : ABI.%.% +private static volatile ObjectReference<%> __%; +private static ObjectReference<%> Make__%() { -public _%() : base(%()) { } -% -internal static % Instance => _instance; + global::System.Threading.Interlocked.CompareExchange(ref __%, ActivationFactory.Get<%>("%.%", %.IID), null); + return __%; } +private static ObjectReference<%> % => __% ?? Make__%(); )", - cache_type_name, - class_type.TypeNamespace(), - cache_type_name, - cache_type_name, - cache_interface, - instance, - cache_type_name); + vftblType, + objrefname, + vftblType, + objrefname, + objrefname, + vftblType, + classType.TypeNamespace(), + classType.TypeName(), + bind(staticsType, typedef_name_type::StaticAbiClass, true), + objrefname, + vftblType, + objrefname, + objrefname, + objrefname); } else { + auto objrefname = w.write_temp("%", bind(staticsType)); w.write(R"( -internal sealed class _% +private static volatile FactoryObjectReference<%> __%; +private static FactoryObjectReference<%> Make__%() { -private IObjectReference _obj; -public _%() -{ -_obj = %(%.IID); + global::System.Threading.Interlocked.CompareExchange(ref __%, ActivationFactory.Get<%>("%.%", %.IID), null); + return __%; } - -% -internal static % Instance => (%)_instance; +private static FactoryObjectReference<%> % +{ + get + { + var factory = __%; + if (factory != null && factory.IsObjectInContext()) + { + return factory; + } + else + { + return Make__%(); + } + } } )", - cache_type_name, - cache_type_name, - factoryAs, - bind(class_type, typedef_name_type::StaticAbiClass, true), - instance, - cache_type_name, - cache_type_name); + vftblType, + objrefname, + vftblType, + objrefname, + objrefname, + vftblType, + classType.TypeNamespace(), + classType.TypeName(), + bind(staticsType, typedef_name_type::StaticAbiClass, true), + objrefname, + vftblType, + objrefname, + objrefname, + objrefname); } + } - return w.write_temp("_%.Instance", cache_type_name); + template + void write_static_abi_class_raw(writer& w, TypeDef const& factory_type) + { + w.write(R"( +private static class _% +{%} +)", + bind(factory_type, typedef_name_type::StaticAbiClass, false), + bind_each([&](writer& w, MethodDef const& method) + { + method_writer(w, factory_type, method); + }, factory_type.MethodList())); } + void write_static_composing_factory_method(writer& w, TypeDef const& iface, MethodDef const& method); + + void write_static_abi_method_with_raw_return_type(writer& w, TypeDef const& iface, MethodDef const& method); + static std::string get_default_interface_name(writer& w, TypeDef const& type, bool abiNamespace = true, bool forceCCW = false) { return w.write_temp("%", bind(get_type_semantics(get_default_interface(type)), abiNamespace ? typedef_name_type::ABI : forceCCW ? typedef_name_type::CCW : typedef_name_type::Projected, false)); @@ -1896,9 +1999,14 @@ internal static % Instance => (%)_instance; auto default_interface_name = get_default_interface_name(w, class_type); if (factory_type) { - auto cache_object = write_factory_cache_object(w, factory_type, class_type); - auto platform_attribute = write_platform_attribute_temp(w, factory_type); + write_static_abi_class_raw(w, factory_type); + auto vftblType = settings.netstandard_compat ? + w.write_temp("%.Vftbl", bind(factory_type, typedef_name_type::ABI, true)) : + "IUnknownVftbl"; + write_static_objref_definition(w, vftblType, factory_type, class_type); + auto cache_object = w.write_temp("%", bind(factory_type)); + auto platform_attribute = write_platform_attribute_temp(w, factory_type); for (auto&& method : factory_type.MethodList()) { method_signature signature{ method }; @@ -1906,7 +2014,7 @@ internal static % Instance => (%)_instance; { w.write(R"( %public %(%) : this(((Func<%>)(() => { -IntPtr ptr = (%.%(%)); +IntPtr ptr = (_%.%(%%%)); try { return %(ComWrappersSupport.GetObjectReferenceForInterface(ptr)); @@ -1922,8 +2030,10 @@ MarshalInspectable.DisposeAbi(ptr); class_type.TypeName(), bind_list(", ", signature.params()), default_interface_name, - cache_object, + bind(factory_type, typedef_name_type::StaticAbiClass, false), method.Name(), + cache_object, + signature.has_params() ? ", " : "", bind_list(", ", signature.params()), "new " + default_interface_name ); @@ -1938,7 +2048,7 @@ MarshalInspectable.DisposeAbi(ptr); w.write(R"( %public %(%) % { -IntPtr ptr = (%.%(%)); +IntPtr ptr = (_%.%(%%%)); try { _inner = ComWrappersSupport.GetObjectReferenceForInterface(ptr); @@ -1953,8 +2063,10 @@ MarshalInspectable.DisposeAbi(ptr); class_type.TypeName(), bind_list(", ", signature.params()), has_base_type ? ":base(global::WinRT.DerivedComposed.Instance)" : "", - cache_object, + bind(factory_type, typedef_name_type::StaticAbiClass, false), method.Name(), + cache_object, + signature.has_params() ? ", " : "", bind_list(", ", signature.params()), bind([&](writer& w) { @@ -1975,8 +2087,11 @@ ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); } else { + write_activation_factory_objref_definition(w, class_type); + auto objrefname = w.write_temp("%", bind(class_type)); + w.write(R"( -public %() : this(%(ActivationFactory<%>.ActivateInstance())) +public %() : this(%(WinRT.IActivationFactoryMethods.ActivateInstance(%))) { ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); % @@ -1984,7 +2099,7 @@ ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); )", class_type.TypeName(), settings.netstandard_compat ? "new " + default_interface_name : "", - class_type.TypeName(), + objrefname, settings.netstandard_compat ? "" : "ComWrappersHelper.Init(_inner, false);"); } } @@ -2080,39 +2195,15 @@ private IObjectReference % => __% ?? Make__%(); } } - void write_class_static_objrefs_definition(writer& w, std::string const& target, TypeDef const& classType) - { - for (auto&& [interface_name, factory] : get_attributed_types(w, classType)) - { - if (factory.statics) - { - auto objrefname = bind(factory.type); - w.write(R"( -private static volatile IObjectReference __%; -private static IObjectReference Make__%() -{ - global::System.Threading.Interlocked.CompareExchange(ref __%, %As(%.IID), null); - return __%; -} -private static IObjectReference % => __% ?? Make__%(); - -)", - objrefname, - objrefname, - objrefname, - target, - bind(factory.type, typedef_name_type::StaticAbiClass, true), - objrefname, - objrefname, - objrefname, - objrefname); - } - } - } - void write_composable_constructors(writer& w, TypeDef const& composable_type, TypeDef const& class_type, std::string_view visibility) { - auto cache_object = write_factory_cache_object(w, composable_type, class_type); + write_static_abi_class_raw(w, composable_type); + auto vftblType = settings.netstandard_compat ? + w.write_temp("%.Vftbl", bind(composable_type, typedef_name_type::ABI, true)) : + "IUnknownVftbl"; + write_static_objref_definition(w, vftblType, composable_type, class_type); + auto cache_object = bind(composable_type); + auto default_interface_name = get_default_interface_name(w, class_type, false); auto default_interface_abi_name = get_default_interface_name(w, class_type); auto default_interface_typedef = for_typedef(w, get_type_semantics(get_default_interface(class_type)), [&](auto&& iface) { return iface; }); @@ -2132,7 +2223,7 @@ private static IObjectReference % => __% ?? Make__%(); % %(%)% { object baseInspectable = this.GetType() != typeof(%) ? this : null; -IntPtr composed = %.%(%%baseInspectable, out IntPtr ptr); +IntPtr composed = _%.%(%, %%baseInspectable, out IntPtr ptr); using IObjectReference composedRef = ObjectReference.Attach(ref composed); try { @@ -2152,8 +2243,9 @@ MarshalInspectable.DisposeAbi(ptr); bind_list(", ", params_without_objects), has_base_type ? ":base(global::WinRT.DerivedComposed.Instance)" : "", bind(class_type, typedef_name_type::Projected, false), - cache_object, + bind(composable_type, typedef_name_type::StaticAbiClass, false), method.Name(), + cache_object, bind_list(", ", params_without_objects), [&](writer& w) {w.write("%", params_without_objects.empty() ? " " : ", "); }, default_interface_abi_name, @@ -2168,7 +2260,7 @@ MarshalInspectable.DisposeAbi(ptr); { bool isAggregation = this.GetType() != typeof(%); object baseInspectable = isAggregation ? this : null; -IntPtr composed = %.%(%%baseInspectable, out IntPtr inner); +IntPtr composed = _%.%(%, %%baseInspectable, out IntPtr inner); try { ComWrappersHelper.Init(isAggregation, this, composed, inner, out _inner); @@ -2186,8 +2278,9 @@ Marshal.Release(inner); bind_list(", ", params_without_objects), has_base_type ? ":base(global::WinRT.DerivedComposed.Instance)" : "", bind(class_type, typedef_name_type::Projected, false), - cache_object, + bind(composable_type, typedef_name_type::StaticAbiClass, false), method.Name(), + cache_object, bind_list(", ", params_without_objects), [&](writer& w) {w.write("%", params_without_objects.empty() ? " " : ", "); }, bind([&](writer& w) @@ -2201,7 +2294,20 @@ Marshal.Release(inner); } } - void write_static_method(writer& w, MethodDef const& method, std::string_view method_target, bool factory_class = false, std::string_view platform_attribute = ""sv) + void write_static_factory_method(writer& w, MethodDef const& method, std::string_view method_target, std::string_view platform_attribute = ""sv) + { + if (method.SpecialName()) + { + return; + } + method_signature signature{ method }; + auto return_type = w.write_temp("%", [&](writer& w) { + write_projection_return_type(w, signature); + }); + write_method(w, signature, method.Name(), return_type, method_target, "public "sv, ""sv, platform_attribute, std::nullopt); + } + + void write_static_method(writer& w, MethodDef const& method, std::string_view method_target, std::string_view platform_attribute = ""sv) { if (method.SpecialName()) { @@ -2211,38 +2317,59 @@ Marshal.Release(inner); auto return_type = w.write_temp("%", [&](writer& w) { write_projection_return_type(w, signature); }); - write_method(w, signature, method.Name(), return_type, method_target, "public "sv, factory_class ? ""sv : "static "sv, platform_attribute, settings.netstandard_compat || factory_class ? std::nullopt : std::optional(std::pair(method.Parent(), method))); + write_method(w, signature, method.Name(), return_type, method_target, "public "sv, "static "sv, platform_attribute, std::optional(std::pair(method.Parent(), method))); } - void write_static_property(writer& w, Property const& prop, std::string_view prop_target, bool factory_class = false, std::string_view platform_attribute = ""sv) + void write_static_factory_property(writer& w, Property const& prop, std::string_view prop_target, std::string_view platform_attribute = ""sv) { auto [getter, setter] = get_property_methods(prop); auto getter_target = getter ? prop_target : ""; auto setter_target = setter ? prop_target : ""; write_property(w, prop.Name(), prop.Name(), write_prop_type(w, prop), - getter_target, setter_target, "public "sv, factory_class ? ""sv : "static "sv, platform_attribute, platform_attribute, - settings.netstandard_compat || factory_class || !getter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop)), - settings.netstandard_compat || factory_class || !setter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop))); + getter_target, setter_target, "public "sv, ""sv, platform_attribute, platform_attribute, + std::nullopt, + std::nullopt); } - void write_static_event(writer& w, Event const& event, std::string_view event_target, bool factory_class = false, std::string_view platform_attribute = ""sv) + void write_static_property(writer& w, Property const& prop, std::string_view prop_target, std::string_view platform_attribute = ""sv) { - write_event(w, event.Name(), event, event_target, "public "sv, factory_class ? ""sv : "static "sv, platform_attribute, - settings.netstandard_compat || factory_class ? std::nullopt : std::optional(std::tuple(event.Parent(), event, true))); + auto [getter, setter] = get_property_methods(prop); + auto getter_target = getter ? prop_target : ""; + auto setter_target = setter ? prop_target : ""; + write_property(w, prop.Name(), prop.Name(), write_prop_type(w, prop), + getter_target, setter_target, "public "sv, "static "sv, platform_attribute, platform_attribute, + !getter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop)), + !setter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop))); + } + + void write_static_factory_event(writer& w, Event const& event, std::string_view event_target, std::string_view platform_attribute = ""sv) + { + write_event(w, event.Name(), event, event_target, "public "sv, ""sv, platform_attribute, std::nullopt); + } + + void write_static_event(writer& w, Event const& event, std::string_view event_target, std::string_view platform_attribute = ""sv) + { + write_event(w, event.Name(), event, event_target, "public "sv, "static "sv, platform_attribute, std::optional(std::tuple(event.Parent(), event, true))); } void write_static_members(writer& w, TypeDef const& static_type, TypeDef const& class_type) { - auto cache_object = settings.netstandard_compat ? write_static_cache_object(w, static_type.TypeName(), class_type) : ""; + auto vftblType = settings.netstandard_compat ? + w.write_temp("%.Vftbl", bind(static_type, typedef_name_type::ABI, true)) : + "IUnknownVftbl"; + write_static_objref_definition(w, vftblType, static_type, class_type); + auto cache_object = w.write_temp("%", bind(static_type)); + auto platform_attribute = write_platform_attribute_temp(w, static_type); - w.write_each(static_type.MethodList(), cache_object, false, platform_attribute); - w.write_each(static_type.PropertyList(), cache_object, false, platform_attribute); - w.write_each(static_type.EventList(), cache_object, false, platform_attribute); + w.write_each(static_type.MethodList(), cache_object, platform_attribute); + w.write_each(static_type.PropertyList(), cache_object, platform_attribute); + w.write_each(static_type.EventList(), cache_object, platform_attribute); } void write_attributed_types(writer& w, TypeDef const& type) { bool factory_written{}; + for (auto&& [interface_name, factory] : get_attributed_types(w, type)) { if (factory.activatable) @@ -2282,44 +2409,12 @@ Marshal.Release(inner); }); } - if (is_static(type)) - { - w.write(R"( -internal static %BaseActivationFactory _factory = new BaseActivationFactory("%", "%.%"); -public static %I As() => _factory.AsInterface(); -% + w.write(R"( +public static %I As() => ActivationFactory.Get("%.%").AsInterface(); )", - has_base_factory ? "new " : "", - type.TypeNamespace(), - type.TypeNamespace(), - type.TypeName(), - has_base_factory ? "new " : "", - bind([&](writer& w) - { - if (!settings.netstandard_compat) - { - write_class_static_objrefs_definition(w, "_factory._", type); - } - })); - } - else - { - w.write(R"( -public static %I As() => ActivationFactory<%>.AsInterface(); -% -)", - has_base_factory ? "new " : "", - type.TypeName(), - bind([&](writer& w) - { - if (!settings.netstandard_compat) - { - write_class_static_objrefs_definition(w, - w.write_temp("ActivationFactory<%>.", type.TypeName()), - type); - } - })); - } + has_base_factory ? "new " : "", + type.TypeNamespace(), + type.TypeName()); } write_static_members(w, factory.type, type); @@ -3077,7 +3172,10 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); if (is_private) continue; std::string_view access_spec = is_public ? "public "sv : "protected "sv; std::string_view method_spec = is_overridable ? "virtual "sv : ""sv; - write_property(w, prop_name, prop_name, prop_type, getter_target, setter_target, access_spec, method_spec, getter_platform, setter_platform, getter_prop, setter_prop); + write_property(w, prop_name, prop_name, prop_type, + getter_prop.has_value() ? w.write_temp("%", bind(getter_prop.value().first)) : getter_target, + setter_prop.has_value() ? w.write_temp("%", bind(setter_prop.value().first)) : setter_target, + access_spec, method_spec, getter_platform, setter_platform, getter_prop, setter_prop); } } @@ -3133,7 +3231,8 @@ db_path.stem().string()); { w.write(R"(%%% static class % { -%})", +%} +)", bind(type), bind(type, true), internal_accessibility(), @@ -3144,7 +3243,7 @@ db_path.stem().string()); void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics); - void write_event_source_ctor(writer& w, Event const& evt, int index, uint32_t const& abi_methods_start_index) + void write_event_source_ctor(writer& w, Event const& evt, int index, uint32_t const& abi_methods_start_index = 6) { if (for_typedef(w, get_type_semantics(evt.EventType()), [&](TypeDef const& eventType) { @@ -3768,10 +3867,10 @@ event % %;)", if (auto ret = signature.return_signature()) { abi_marshaler m{ - "retval", - is_generic ? param_index : -1, - ret.Type().is_szarray() && !raw_return_type ? param_category::receive_array : param_category::out, - true + "retval", + is_generic ? param_index : -1, + ret.Type().is_szarray() && !raw_return_type ? param_category::receive_array : param_category::out, + true }; param_index += m.is_array() ? 2 : 1; if (!raw_return_type) @@ -3895,13 +3994,22 @@ finally write_abi_method_call_marshalers(w, invoke_target, is_generic, get_abi_marshalers(w, signature, is_generic, "", raw_return_type), has_noexcept_attr); } - void write_abi_method_with_raw_return_type(writer& w, MethodDef const& method) + void write_static_abi_method_with_raw_return_type(writer& w, TypeDef const& iface, MethodDef const& method) { if (is_special(method)) { return; } + bool generic_type = distance(iface.GenericParam()) > 0; + auto init_call_variables = [&](writer& w) + { + if (generic_type) + { + w.write("\nvar _obj = (ObjectReference<%.Vftbl>)_genericObj;", bind(iface, typedef_name_type::ABI, false)); + } + w.write("\nvar ThisPtr = _obj.ThisPtr;\n"); + }; auto write_raw_return_type = [](writer& w, method_signature const& sig) { if (auto return_sig = sig.return_signature()) @@ -3917,25 +4025,36 @@ finally method_signature signature{ method }; auto [invoke_target, is_generic] = get_invoke_info(w, method); w.write(R"( -public unsafe %% %(%) -{%} +public static unsafe % %(% %%%) +{%%} )", - // In the .NET Standard 2.0 code-gen, the fully-projected signature will be available in the base class, so we need to specify new to hide it - settings.netstandard_compat ? "new " : "", bind(write_raw_return_type, signature), method.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", + generic_type ? "_genericObj" : "_obj", + signature.has_params() ? ", " : "", bind_list(", ", signature.params()), + bind(init_call_variables), bind(signature, invoke_target, is_generic, true, is_noexcept(method))); } - void write_composing_factory_method(writer& w, MethodDef const& method) + void write_static_composing_factory_method(writer& w, TypeDef const& iface, MethodDef const& method) { if (is_special(method)) { return; } + bool generic_type = distance(iface.GenericParam()) > 0; + auto init_call_variables = [&](writer& w) + { + if (generic_type) + { + w.write("\nvar _obj = (ObjectReference<%.Vftbl>)_genericObj;", bind(iface, typedef_name_type::ABI, false)); + } + w.write("\nvar ThisPtr = _obj.ThisPtr;\n"); + }; auto write_composable_constructor_params = [&](writer& w, method_signature const& method_sig) { auto const& params = method_sig.params(); @@ -3987,77 +4106,18 @@ public unsafe %% %(%) }; w.write(R"( -public unsafe % %(%) -{%} +public static unsafe % %(% %%%) +{%%} )", bind(write_raw_return_type, signature), method.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", + generic_type ? "_genericObj" : "_obj", + signature.has_params() ? ", " : "", bind(write_composable_constructor_params, signature), + bind(init_call_variables), bind(invoke_target, is_generic, abi_marshalers, is_noexcept(method))); } - - template - std::string write_factory_cache_object(writer& w, TypeDef const& factory_type, TypeDef const& class_type) - { - std::string_view cache_type_name = factory_type.TypeName(); - if (settings.netstandard_compat) - { - auto cache_vftbl_type = w.write_temp("ABI.%.%.Vftbl", class_type.TypeNamespace(), cache_type_name); - auto cache_interface = - w.write_temp( - R"(ActivationFactory<%>.As<%>)", - class_type.TypeName(), - cache_vftbl_type); - - w.write(R"( -internal sealed class _% : ABI.%.% -{ -public _%() : base(%()) { } -private static _% _instance = new _%(); -internal static _% Instance => _instance; -% -} -)", - cache_type_name, - class_type.TypeNamespace(), - cache_type_name, - cache_type_name, - cache_interface, - cache_type_name, - cache_type_name, - cache_type_name, - bind_each(factory_type.MethodList()) - ); - } - else - { - w.write(R"( -internal sealed class _% -{ -private IObjectReference _obj; -private IntPtr ThisPtr => _obj.ThisPtr; -public _%() -{ -_obj = ActivationFactory<%>.As(%.IID); -} - -private static _% _instance = new _%(); -internal static _% Instance => _instance; -% -} -)", - cache_type_name, - cache_type_name, - class_type.TypeName(), - bind(factory_type, typedef_name_type::StaticAbiClass, true), - cache_type_name, - cache_type_name, - cache_type_name, - bind_each(factory_type.MethodList())); - } - - return w.write_temp("_%.Instance", cache_type_name); - } void write_interface_members_netstandard(writer& w, TypeDef const& type) { @@ -4308,7 +4368,7 @@ remove } } - void write_static_abi_class_members(writer& w, TypeDef const& iface, uint32_t const& abi_methods_start_index) + void write_static_abi_class_members(writer& w, TypeDef const& iface, uint32_t const& abi_methods_start_index = 6) { bool generic_type = distance(iface.GenericParam()) > 0; auto init_call_variables = [&](writer& w) @@ -4328,13 +4388,14 @@ remove } method_signature signature{ method }; auto [invoke_target, is_generic] = get_invoke_info(w, method, abi_methods_start_index); + w.write(R"( -public static unsafe %% %(IObjectReference %%%) +public static unsafe % %(% %%%) {%%} )", - (settings.netstandard_compat && method.Name() == "ToString"sv) ? "override " : "", bind(signature), method.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", generic_type ? "_genericObj" : "_obj", signature.has_params() ? ", " : "", bind_list(", ", signature.params()), @@ -4351,11 +4412,12 @@ public static unsafe %% %(IObjectReference %%%) auto [invoke_target, is_generic] = get_invoke_info(w, getter, abi_methods_start_index); auto signature = method_signature(getter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); - w.write(R"(public static unsafe % get_%(IObjectReference %) + w.write(R"(public static unsafe % get_%(% %) {%%} )", write_prop_type(w, prop), prop.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", generic_type ? "_genericObj" : "_obj", bind(init_call_variables), bind(invoke_target, is_generic, marshalers, is_noexcept(prop))); @@ -4366,10 +4428,11 @@ public static unsafe %% %(IObjectReference %%%) auto signature = method_signature(setter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); marshalers[0].param_name = "value"; - w.write(R"(public static unsafe void set_%(IObjectReference %, % value) + w.write(R"(public static unsafe void set_%(% %, % value) {%%} )", prop.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", generic_type ? "_genericObj" : "_obj", write_prop_type(w, prop), bind(init_call_variables), @@ -4382,7 +4445,7 @@ public static unsafe %% %(IObjectReference %%%) for (auto&& evt : iface.EventList()) { w.write(R"(% -public static unsafe (Action<%>, Action<%>) Get_%(IObjectReference %, object _thisObj) +public static unsafe (Action<%>, Action<%>) Get_%(% %, object _thisObj) { var eventSource = _%.GetValue(_thisObj, (key) => { @@ -4396,6 +4459,7 @@ return eventSource.EventActions; bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), evt.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", generic_type ? "_genericObj" : "_obj", evt.Name(), bind(init_call_variables), @@ -4631,39 +4695,6 @@ return eventSource.EventActions; get(get_arg(10))); } - void write_guid_bytes(writer& w, TypeDef const& type) - { - auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); - if (!attribute) - { - throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); - } - - auto args = attribute.Value().FixedArgs(); - - using std::get; - - auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; - - w.write_printf(R"(0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X)", - (get(get_arg(0)) >> 0) & 0xFF, - (get(get_arg(0)) >> 8) & 0xFF, - (get(get_arg(0)) >> 16) & 0xFF, - (get(get_arg(0)) >> 24) & 0xFF, - (get(get_arg(1)) >> 0) & 0xFF, - (get(get_arg(1)) >> 8) & 0xFF, - (get(get_arg(2)) >> 0) & 0xFF, - (get(get_arg(2)) >> 8) & 0xFF, - get(get_arg(3)), - get(get_arg(4)), - get(get_arg(5)), - get(get_arg(6)), - get(get_arg(7)), - get(get_arg(8)), - get(get_arg(9)), - get(get_arg(10))); - } - void write_type_inheritance(writer& w, TypeDef const& type, type_semantics base_semantics, bool add_custom_qi, bool include_exclusive_interface) { auto delimiter{ " : " }; @@ -5993,30 +6024,60 @@ public static Guid PIID = Vftbl.PIID; } } - w.write(R"(% static class % + if (settings.netstandard_compat) + { + w.write(R"(% static class % +{ +internal static global::System.Guid IID { get; } = new Guid(new byte[] { % }); + +% +} +)", + (is_exclusive_to(iface) || is_projection_internal(iface)) ? "internal" : internal_accessibility(), + bind(iface, typedef_name_type::StaticAbiClass, false), + bind(iface), + [&](writer& w) { + if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { + write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT); + return; + } + auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; + write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index); + abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); + for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) + { + write_static_abi_class_members(w, other_iface, abi_methods_start_index); + abi_methods_start_index += distance(other_iface.MethodList()); + } + }); + } + else + { + w.write(R"(% static class % { internal static global::System.Guid IID { get; } = new Guid(new global::System.ReadOnlySpan(new byte[] { % })); % } -)", - (is_exclusive_to(iface) || is_projection_internal(iface)) ? "internal" : internal_accessibility(), - bind(iface, typedef_name_type::StaticAbiClass, false), - bind(iface), - [&](writer& w) { - if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { - write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT); - return; - } - auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; - write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index); - abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); - for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) - { - write_static_abi_class_members(w, other_iface, abi_methods_start_index); - abi_methods_start_index += distance(other_iface.MethodList()); - } - }); +)", + (is_exclusive_to(iface) || is_projection_internal(iface)) ? "internal" : internal_accessibility(), + bind(iface, typedef_name_type::StaticAbiClass, false), + bind(iface), + [&](writer& w) { + if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { + write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT); + return; + } + auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; + write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index); + abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); + for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) + { + write_static_abi_class_members(w, other_iface, abi_methods_start_index); + abi_methods_start_index += distance(other_iface.MethodList()); + } + }); + } } bool write_abi_interface(writer& w, TypeDef const& type) @@ -6058,10 +6119,9 @@ internal interface % : % { } )", -bind(type), -type_name, -bind(type, typedef_name_type::CCW, false) -); + bind(type), + type_name, + bind(type, typedef_name_type::CCW, false)); return true; } } @@ -6306,7 +6366,7 @@ private readonly % _comp; { public %IntPtr ThisPtr => _default.ThisPtr; -private IObjectReference _inner = null; +private readonly IObjectReference _inner = null; private readonly Lazy<%> _defaultLazy; % @@ -6475,7 +6535,7 @@ _defaultLazy = new Lazy<%>(() => GetDefaultReference<%.Vftbl>()); { private IntPtr ThisPtr => _inner == null ? (((IWinRTObject)this).NativeObject).ThisPtr : _inner.ThisPtr; -private IObjectReference _inner = null; +private readonly IObjectReference _inner = null; % % @@ -6524,7 +6584,10 @@ private struct InterfaceTag{}; bind(type, type.Flags().Sealed()), bind([&](writer& w) { - w.write("private % _default => %;", default_interface_name, is_manually_gen_default_interface ? "_defaultLazy.Value" : "null"); + if (is_manually_gen_default_interface) + { + w.write("private % _default => %;", default_interface_name, "_defaultLazy.Value"); + } }), bind(type), // FromAbi @@ -7525,9 +7588,9 @@ bind_list(", ", signature.params()) } else if (factory.statics) { - w.write_each(factory.type.MethodList(), projected_type_name, true, ""sv); - w.write_each(factory.type.PropertyList(), projected_type_name, true, ""sv); - w.write_each(factory.type.EventList(), projected_type_name, true, ""sv); + w.write_each(factory.type.MethodList(), projected_type_name, ""sv); + w.write_each(factory.type.PropertyList(), projected_type_name, ""sv); + w.write_each(factory.type.EventList(), projected_type_name, ""sv); } } } diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index 55204925d..2ee65a021 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -286,10 +286,11 @@ Where is one or more of: case category::interface_type: if (settings.netstandard_compat) { + write_static_abi_classes(w, type); write_abi_interface_netstandard(w, type); } else - { + { write_static_abi_classes(w, type); write_abi_interface(w, type); } diff --git a/src/cswinrt/strings/WinRT.cs b/src/cswinrt/strings/WinRT.cs index 16bbfc486..7d6a134bb 100644 --- a/src/cswinrt/strings/WinRT.cs +++ b/src/cswinrt/strings/WinRT.cs @@ -262,6 +262,18 @@ internal struct VftblPtr { public IntPtr Vftbl; } + internal static partial class Context + { + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + private static extern unsafe int CoGetContextToken(IntPtr* contextToken); + + public unsafe static IntPtr GetContextToken() + { + IntPtr contextToken; + Marshal.ThrowExceptionForHR(CoGetContextToken(&contextToken)); + return contextToken; + } + } internal unsafe sealed class DllModule { @@ -321,11 +333,11 @@ private static unsafe bool TryCreate(string fileName, out DllModule module) { module = null; return false; - } - + } + module = new DllModule( - fileName, - moduleHandle, + fileName, + moduleHandle, getActivationFactory); return true; } @@ -350,7 +362,7 @@ private DllModule(string fileName, IntPtr moduleHandle, void* getActivationFacto } } - public unsafe (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId) + public unsafe (FactoryObjectReference obj, int hr) GetActivationFactory(string runtimeClassId) { IntPtr instancePtr = IntPtr.Zero; try @@ -361,7 +373,7 @@ public unsafe (ObjectReference obj, int hr) GetActivati int hr = _GetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &instancePtr); if (hr == 0) { - var objRef = ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); + var objRef = FactoryObjectReference.FromAbi(instancePtr); return (objRef, hr); } else @@ -408,28 +420,46 @@ public unsafe WinrtModule() _mtaCookie = mtaCookie; } - public static unsafe (IntPtr instancePtr, int hr) GetActivationFactory(IntPtr hstrRuntimeClassId) + public static unsafe (FactoryObjectReference obj, int hr) GetActivationFactory(string runtimeClassId, Guid iid) { var module = Instance; // Ensure COM is initialized - Guid iid = IActivationFactoryVftbl.IID; - IntPtr instancePtr; - int hr = Platform.RoGetActivationFactory(hstrRuntimeClassId, &iid, &instancePtr); - return (hr == 0 ? instancePtr : IntPtr.Zero, hr); - } - - public static unsafe (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId) + IntPtr instancePtr = IntPtr.Zero; + try + { + MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); + fixed (void* ___runtimeClassId = __runtimeClassId) + { + int hr = Platform.RoGetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &iid, &instancePtr); + if (hr == 0) + { + var objRef = FactoryObjectReference.FromAbi(instancePtr); + return (objRef, hr); + } + else + { + return (null, hr); + } + } + } + finally + { + MarshalInspectable.DisposeAbi(instancePtr); + } + } + + public static unsafe (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId, Guid iid) { + var module = Instance; // Ensure COM is initialized IntPtr instancePtr = IntPtr.Zero; try { MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); fixed (void* ___runtimeClassId = __runtimeClassId) - { - int hr; - (instancePtr, hr) = GetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId)); + { + int hr = Platform.RoGetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &iid, &instancePtr); if (hr == 0) { - var objRef = ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); + var objRef = ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); return (objRef, hr); } else @@ -450,48 +480,64 @@ public static unsafe (ObjectReference obj, int hr) GetA } } - internal class BaseActivationFactory + internal sealed class FactoryObjectReference< +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] +#endif + T> : IObjectReference { - private readonly ObjectReference _IActivationFactory; - - public ObjectReference Value { get => _IActivationFactory; } - - public I AsInterface() => _IActivationFactory.AsInterface(); - - public BaseActivationFactory(string typeNamespace, string typeFullName) + private readonly IntPtr _contextToken; + + internal FactoryObjectReference(IntPtr thisPtr) : + base(thisPtr) + { + if (!IsFreeThreaded(this)) + { + _contextToken = Context.GetContextToken(); + } + } + + internal FactoryObjectReference(IntPtr thisPtr, IntPtr contextToken) + : base(thisPtr) { - // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure - int hr; - (_IActivationFactory, hr) = WinrtModule.GetActivationFactory(typeFullName); - if (_IActivationFactory != null) { return; } - - var moduleName = typeNamespace; - while (true) + _contextToken = contextToken; + } + + public static new unsafe FactoryObjectReference FromAbi(IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) { - DllModule module = null; - if (DllModule.TryLoad(moduleName + ".dll", out module)) - { - (_IActivationFactory, _) = module.GetActivationFactory(typeFullName); - if (_IActivationFactory != null) { return; } - } - - var lastSegment = moduleName.LastIndexOf(".", StringComparison.Ordinal); - if (lastSegment <= 0) - { - Marshal.ThrowExceptionForHR(hr); - } - moduleName = moduleName.Remove(lastSegment); + return null; } + var obj = new FactoryObjectReference(thisPtr); + obj.VftblIUnknown.AddRef(obj.ThisPtr); + return obj; } -#if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:RequiresUnreferencedCode", - Justification = "No members of the generic type are dynamically accessed in this code path.")] -#endif - public unsafe ObjectReference _ActivateInstance() + public bool IsObjectInContext() + { + return _contextToken == IntPtr.Zero || _contextToken == Context.GetContextToken(); + } + + // If we are free threaded, we do not need to keep track of context. + // This can either be if the object implements IAgileObject or the free threaded marshaler. + private unsafe static bool IsFreeThreaded(IObjectReference objRef) + { + if (objRef.TryAs(InterfaceIIDs.IAgileObject_IID, out var agilePtr) >= 0) + { + Marshal.Release(agilePtr); + return true; + } + return false; + } + } + + internal static class IActivationFactoryMethods + { + public static unsafe ObjectReference ActivateInstance(IObjectReference obj) { IntPtr instancePtr; - Marshal.ThrowExceptionForHR(_IActivationFactory.Vftbl.ActivateInstance(_IActivationFactory.ThisPtr, &instancePtr)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)obj.ThisPtr)[6](obj.ThisPtr, out instancePtr)); try { return ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); @@ -500,34 +546,98 @@ public unsafe ObjectReference _ActivateInstance() { MarshalInspectable.DisposeAbi(instancePtr); } - } - -#if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:RequiresUnreferencedCode", - Justification = "No members of the generic type are dynamically accessed in this code path.")] -#endif - public ObjectReference _As() => _IActivationFactory.As(); - public IObjectReference _As(Guid iid) => _IActivationFactory.As(iid); + } } - internal sealed class ActivationFactory : BaseActivationFactory + internal static class ActivationFactory { - public ActivationFactory() : base(typeof(T).Namespace, typeof(T).FullName) { } + public static FactoryObjectReference Get(string typeName) + { + // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure + int hr; + FactoryObjectReference factory; + (factory, hr) = WinrtModule.GetActivationFactory(typeName, InterfaceIIDs.IActivationFactory_IID); + if (factory != null) + { + return factory; + } - static readonly ActivationFactory _factory = new ActivationFactory(); - public static new I AsInterface() => _factory.Value.AsInterface(); - public static ObjectReference As() => _factory._As(); - public static IObjectReference As(Guid iid) => _factory._As(iid); + var moduleName = typeName; + while (true) + { + var lastSegment = moduleName.LastIndexOf(".", StringComparison.Ordinal); + if (lastSegment <= 0) + { + Marshal.ThrowExceptionForHR(hr); + } + moduleName = moduleName.Remove(lastSegment); + DllModule module = null; + if (DllModule.TryLoad(moduleName + ".dll", out module)) + { + (factory, hr) = module.GetActivationFactory(typeName); + if (factory != null) + { + return factory; + } + } + } + } + #if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:RequiresUnreferencedCode", - Justification = "No members of the generic type are dynamically accessed in this code path.")] -#endif - public static ObjectReference ActivateInstance< + public static FactoryObjectReference Get< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] +#else + public static ObjectReference Get< +#endif + I>(string typeName, Guid iid) + { + // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure + int hr; + FactoryObjectReference factory; + (factory, hr) = WinrtModule.GetActivationFactory(typeName, iid); + if (factory != null) + { #if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.None)] -#endif - I>() => _factory._ActivateInstance(); + return factory; +#else + using (factory) + { + return factory.As(iid); + } +#endif + } + + var moduleName = typeName; + while (true) + { + var lastSegment = moduleName.LastIndexOf(".", StringComparison.Ordinal); + if (lastSegment <= 0) + { + Marshal.ThrowExceptionForHR(hr); + } + moduleName = moduleName.Remove(lastSegment); + + DllModule module = null; + if (DllModule.TryLoad(moduleName + ".dll", out module)) + { + FactoryObjectReference activationFactory; + (activationFactory, hr) = module.GetActivationFactory(typeName); + if (activationFactory != null) + { + using (activationFactory) +#if NET + using (var objRef = activationFactory.As(iid)) + { + return FactoryObjectReference.FromAbi(objRef.ThisPtr); + } +#else + return activationFactory.As(iid); +#endif + } + } + } + } } internal class ComponentActivationFactory : global::WinRT.Interop.IActivationFactory @@ -1045,10 +1155,26 @@ private void RemoveEventHandlerNoLock(EventRegistrationToken token) } internal static class InterfaceIIDs - { + { +#if NET + internal static readonly Guid IInspectable_IID = new Guid(new global::System.ReadOnlySpan(new byte[] { 0xE0, 0xE2, 0x86, 0xAF, 0x2D, 0xB1, 0x6A, 0x4C, 0x9C, 0x5A, 0xD7, 0xAA, 0x65, 0x10, 0x1E, 0x90 })); + internal static readonly Guid IUnknown_IID = new Guid(new global::System.ReadOnlySpan(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 })); + internal static readonly Guid IWeakReferenceSource_IID = new Guid(new global::System.ReadOnlySpan(new byte[] { 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 })); + internal static readonly Guid IWeakReference_IID = new Guid(new global::System.ReadOnlySpan(new byte[] { 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 })); + internal static readonly Guid IActivationFactory_IID = new Guid(new global::System.ReadOnlySpan(new byte[] { 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 })); + internal static readonly Guid IAgileObject_IID = new Guid(new global::System.ReadOnlySpan(new byte[] { 0x94, 0x2B, 0xEA, 0x94, 0xCC, 0xE9, 0xE0, 0x49, 0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90 })); + internal static readonly Guid IMarshal_IID = new Guid(new global::System.ReadOnlySpan(new byte[] { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 })); + internal static readonly Guid IContextCallback_IID = new Guid(new global::System.ReadOnlySpan(new byte[] { 0xDA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 })); +#else internal static readonly Guid IInspectable_IID = new(0xAF86E2E0, 0xB12D, 0x4c6a, 0x9C, 0x5A, 0xD7, 0xAA, 0x65, 0x10, 0x1E, 0x90); internal static readonly Guid IUnknown_IID = new(0, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); internal static readonly Guid IWeakReferenceSource_IID = new(0x00000038, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IWeakReference_IID = new(0x00000037, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IActivationFactory_IID = new (0x00000035, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IAgileObject_IID = new(0x94ea2b94, 0xe9cc, 0x49e0, 0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90); + internal static readonly Guid IMarshal_IID = new(0x00000003, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IContextCallback_IID = new(0x000001da, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); +#endif } } diff --git a/src/cswinrt/strings/WinRT_Interop.cs b/src/cswinrt/strings/WinRT_Interop.cs index 38266aed5..a74668155 100644 --- a/src/cswinrt/strings/WinRT_Interop.cs +++ b/src/cswinrt/strings/WinRT_Interop.cs @@ -40,6 +40,5 @@ internal unsafe struct IReferenceTrackerTargetVftbl private void* _Unpeg; internal static readonly Guid IID = new(0x64BD43F8, 0xbFEE, 0x4EC4, 0xB7, 0xEB, 0x29, 0x35, 0x15, 0x8D, 0xAE, 0x21); - } - + } } From 50255b91cd443de677f4c9c7fdb361ab75c8f8f4 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Mon, 6 Nov 2023 10:15:53 -0800 Subject: [PATCH 05/10] Add test. --- .../UnitTest/TestComponentCSharp_Tests.cs | 134 +++++++++++++----- 1 file changed, 100 insertions(+), 34 deletions(-) diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index 57611d98d..a0135ddf3 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -30,6 +30,7 @@ using Windows.Devices.Enumeration.Pnp; using System.Diagnostics; using Windows.Devices.Enumeration; +using Windows.UI.Notifications; #if NET using WeakRefNS = System; @@ -76,12 +77,12 @@ public void TestLongClassNameEventSource() [Fact] public void TestEventArgsVector() - { + { var eventArgsVector = TestObject.GetEventArgsVector(); Assert.Equal(1, eventArgsVector.Count); foreach (var dataErrorChangedEventArgs in eventArgsVector) { - var propName = dataErrorChangedEventArgs.PropertyName; + var propName = dataErrorChangedEventArgs.PropertyName; Assert.Equal("name", propName); } } @@ -92,7 +93,7 @@ public void TestNonGenericDelegateVector() var provideUriVector = TestObject.GetNonGenericDelegateVector(); Assert.Equal(1, provideUriVector.Count); - + foreach (var provideUri in provideUriVector) { Uri delegateTarget = provideUri.Invoke(); @@ -197,11 +198,11 @@ public void TestManyBufferExtensionMethods() Assert.True(buffLen4.Length == 4); Assert.Throws(() => buffLen4.GetByte(5)); // shouldn't have a 5th element Assert.True(buffLen4.GetByte(0) == 0x02); // make sure we got the 2nd element of the array - + arrayLen3.CopyTo(buffLen4); // Array to Buffer copying Assert.True(buffLen4.Length == 4); Assert.True(buffLen4.GetByte(0) == 0x01); // make sure we updated the first few - Assert.True(buffLen4.GetByte(1) == 0x02); + Assert.True(buffLen4.GetByte(1) == 0x02); Assert.True(buffLen4.GetByte(2) == 0x03); Assert.True(buffLen4.GetByte(3) == 0x14); // and kept the last one @@ -263,14 +264,14 @@ public void TestIsSameDataUsingToArray() public void TestBufferAsStreamUsingAsBuffer() { var arr = new byte[] { 0x01, 0x02 }; - Stream stream = arr.AsBuffer().AsStream(); + Stream stream = arr.AsBuffer().AsStream(); Assert.True(stream != null); Assert.True(stream.Length == 2); } [Fact] public void TestBufferAsStreamWithEmptyBuffer1() - { + { var buffer = new Windows.Storage.Streams.Buffer(0); Stream stream = buffer.AsStream(); Assert.True(stream != null); @@ -392,7 +393,7 @@ public void TestWinRTBufferWithZeroLength() [Fact] public void TestEmptyBufferCopyTo() - { + { var buffer = new Windows.Storage.Streams.Buffer(0); byte[] array = { }; buffer.CopyTo(array); @@ -527,7 +528,7 @@ public void TestBuffer() { var arr1 = new byte[] { 0x01, 0x02 }; var buff = arr1.AsBuffer(); - var arr2 = buff.ToArray(0,2); + var arr2 = buff.ToArray(0, 2); Assert.True(arr1[0] == arr2[0]); Assert.True(arr1[1] == arr2[1]); } @@ -576,7 +577,7 @@ async Task InvokeWriteBufferAsync() [Fact] public void TestWriteBuffer() { - Assert.True(InvokeWriteBufferAsync().Wait(1000)); + Assert.True(InvokeWriteBufferAsync().Wait(1000)); } [Fact] @@ -900,10 +901,10 @@ public void TestValueSet() public void TestValueSetArrays() { var map = new Dictionary - { + { ["foo"] = new long[] { 1, 2, 3 }, - ["hello"] = new long[0], - ["world"] = new long[] { 1, 2, 3 }, + ["hello"] = new long[0], + ["world"] = new long[] { 1, 2, 3 }, ["bar"] = new long[0] }; var valueSet = new Windows.Foundation.Collections.ValueSet(); @@ -924,10 +925,10 @@ public void TestFactories() var cls1 = new Class(); var cls2 = new Class(42); - Assert.Equal(42, cls2.IntProperty); + Assert.Equal(42, cls2.IntProperty); var cls3 = new Class(42, "foo"); - Assert.Equal(42, cls3.IntProperty); + Assert.Equal(42, cls3.IntProperty); Assert.Equal("foo", cls3.StringProperty); } @@ -1975,11 +1976,25 @@ public void TestRepeatBehaviorTypeMapping() [Fact] public void TestMatrix3DTypeMapping() { - var matrix3D = new Matrix3D { - M11 = 11, M12 = 12, M13 = 13, M14 = 14, - M21 = 21, M22 = 22, M23 = 23, M24 = 24, - M31 = 31, M32 = 32, M33 = 33, M34 = 34, - OffsetX = 41, OffsetY = 42, OffsetZ = 43,M44 = 44 }; + var matrix3D = new Matrix3D + { + M11 = 11, + M12 = 12, + M13 = 13, + M14 = 14, + M21 = 21, + M22 = 22, + M23 = 23, + M24 = 24, + M31 = 31, + M32 = 32, + M33 = 33, + M34 = 34, + OffsetX = 41, + OffsetY = 42, + OffsetZ = 43, + M44 = 44 + }; TestObject.Matrix3DProperty = matrix3D; Assert.Equal(matrix3D.M11, TestObject.Matrix3DProperty.M11); @@ -2028,10 +2043,22 @@ public void TestMatrix4x4TypeMapping() { var matrix4x4 = new Matrix4x4 { - M11 = 11, M12 = 12, M13 = 13, M14 = 14, - M21 = 21, M22 = 22, M23 = 23, M24 = 24, - M31 = 31, M32 = 32, M33 = 33, M34 = 34, - M41 = 41, M42 = 42, M43 = 43, M44 = 44 + M11 = 11, + M12 = 12, + M13 = 13, + M14 = 14, + M21 = 21, + M22 = 22, + M23 = 23, + M24 = 24, + M31 = 31, + M32 = 32, + M33 = 33, + M34 = 34, + M41 = 41, + M42 = 42, + M43 = 43, + M44 = 44 }; TestObject.Matrix4x4Property = matrix4x4; Assert.Equal(matrix4x4.M11, TestObject.Matrix4x4Property.M11); @@ -2307,7 +2334,7 @@ public void TestDelegateUnboxing() { var del = Class.BoxedDelegate; Assert.IsType(del); - var provideUriDel = (ProvideUri) del; + var provideUriDel = (ProvideUri)del; Assert.Equal(new Uri("http://microsoft.com"), provideUriDel()); } @@ -2528,8 +2555,8 @@ static Object MakeObject() static void TestObject() => MakeObject(); - static (IInitializeWithWindow, IWindowNative) MakeImports() - { + static (IInitializeWithWindow, IWindowNative) MakeImports() + { var obj = MakeObject(); var initializeWithWindow = obj.As(); var windowNative = obj.As(); @@ -2539,7 +2566,7 @@ static Object MakeObject() static void TestImports() { var (initializeWithWindow, windowNative) = MakeImports(); - + GC.Collect(); GC.WaitForPendingFinalizers(); @@ -2851,7 +2878,7 @@ public void TestProxiedDelegate() { proc.Kill(); } - catch(Exception) + catch (Exception) { } } @@ -2930,7 +2957,7 @@ private void TestSupportedOSPlatformWarnings() // Types var a = new WarningAttribute(); // warning CA1416 Assert.NotNull(a); - var w = new WarningStruct{ i32 = 0 }; // warning CA1416 + var w = new WarningStruct { i32 = 0 }; // warning CA1416 Assert.Equal(0, w.i32); // warning CA1416 var v = WarningEnum.Value; Assert.NotEqual(WarningEnum.WarningValue, v); // warning CA1416 @@ -3041,8 +3068,8 @@ public void TestWeakReferenceEventsFromMultipleContexts() Assert.True(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA); watcher = DeviceInformation.CreateWatcher(); - var exception = Record.Exception(() => { - watcher.Added += OnDeviceAdded; + var exception = Record.Exception(() => { + watcher.Added += OnDeviceAdded; }); Assert.Null(exception); @@ -3050,8 +3077,8 @@ public void TestWeakReferenceEventsFromMultipleContexts() { Assert.True(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA); - exception = Record.Exception(() => { - watcher.Updated += OnDeviceUpdated; + exception = Record.Exception(() => { + watcher.Updated += OnDeviceUpdated; }); Assert.Null(exception); }); @@ -3064,6 +3091,45 @@ public void TestWeakReferenceEventsFromMultipleContexts() staThread.Join(); } + [Fact] + public void TestActivationFactoriesFromMultipleContexts() + { + Exception exception = null; + + Thread staThread = new Thread(() => + { + Assert.True(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA); + + exception = Record.Exception(() => + { + var xmlDoc = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01); + _ = new ToastNotification(xmlDoc); + }); + + }); + staThread.SetApartmentState(ApartmentState.STA); + staThread.Start(); + staThread.Join(); + + Assert.Null(exception); + + Thread mtaThread = new Thread(() => + { + Assert.True(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA); + + exception = Record.Exception(() => + { + var xmlDoc = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01); + _ = new ToastNotification(xmlDoc); + }); + }); + mtaThread.SetApartmentState(ApartmentState.MTA); + mtaThread.Start(); + mtaThread.Join(); + + Assert.Null(exception); + } + [Fact] public void TestDictionary() { From 2445e5b5a2eab7270c73ad7a5d4f8dea194aa260 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Mon, 6 Nov 2023 13:07:09 -0800 Subject: [PATCH 06/10] Avoid some addref/release. --- src/cswinrt/strings/WinRT.cs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/cswinrt/strings/WinRT.cs b/src/cswinrt/strings/WinRT.cs index 7d6a134bb..85a486b58 100644 --- a/src/cswinrt/strings/WinRT.cs +++ b/src/cswinrt/strings/WinRT.cs @@ -488,7 +488,18 @@ internal sealed class FactoryObjectReference< { private readonly IntPtr _contextToken; - internal FactoryObjectReference(IntPtr thisPtr) : + public static FactoryObjectReference Attach(ref IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + var obj = new FactoryObjectReference(thisPtr); + thisPtr = IntPtr.Zero; + return obj; + } + + internal FactoryObjectReference(IntPtr thisPtr) : base(thisPtr) { if (!IsFreeThreaded(this)) @@ -626,14 +637,16 @@ public static ObjectReference Get< if (activationFactory != null) { using (activationFactory) -#if NET - using (var objRef = activationFactory.As(iid)) { - return FactoryObjectReference.FromAbi(objRef.ThisPtr); - } +#if NET + if (activationFactory.TryAs(iid, out IntPtr iidPtr) >= 0) + { + return FactoryObjectReference.Attach(ref iidPtr); + } #else - return activationFactory.As(iid); + return activationFactory.As(iid); #endif + } } } } From 5bcbdc2216f65ba5d24d753d8244e0b0f1ab961e Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Mon, 6 Nov 2023 14:56:49 -0800 Subject: [PATCH 07/10] Fix test failure. --- src/cswinrt/code_writers.h | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index ac8955040..7f183af20 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -1867,11 +1867,6 @@ private static % _% = new %("%.%", %.IID); auto objrefname = w.write_temp("%", bind(classType)); w.write(R"( private static volatile FactoryObjectReference __%; -private static FactoryObjectReference Make__%() -{ - global::System.Threading.Interlocked.CompareExchange(ref __%, ActivationFactory.Get("%.%"), null); - return __%; -} private static FactoryObjectReference % { get @@ -1883,7 +1878,7 @@ private static FactoryObjectReference % } else { - return Make__%(); + return __% = ActivationFactory.Get("%.%"); } } } @@ -1891,12 +1886,9 @@ private static FactoryObjectReference % objrefname, objrefname, objrefname, - classType.TypeNamespace(), - classType.TypeName(), - objrefname, - objrefname, objrefname, - objrefname); + classType.TypeNamespace(), + classType.TypeName()); } void write_static_objref_definition(writer& w, std::string_view const& vftblType, TypeDef const& staticsType, TypeDef const& classType) @@ -1933,11 +1925,6 @@ private static ObjectReference<%> % => __% ?? Make__%(); auto objrefname = w.write_temp("%", bind(staticsType)); w.write(R"( private static volatile FactoryObjectReference<%> __%; -private static FactoryObjectReference<%> Make__%() -{ - global::System.Threading.Interlocked.CompareExchange(ref __%, ActivationFactory.Get<%>("%.%", %.IID), null); - return __%; -} private static FactoryObjectReference<%> % { get @@ -1949,7 +1936,7 @@ private static FactoryObjectReference<%> % } else { - return Make__%(); + return __% = ActivationFactory.Get<%>("%.%", %.IID); } } } @@ -1959,15 +1946,11 @@ private static FactoryObjectReference<%> % vftblType, objrefname, objrefname, + objrefname, vftblType, classType.TypeNamespace(), classType.TypeName(), - bind(staticsType, typedef_name_type::StaticAbiClass, true), - objrefname, - vftblType, - objrefname, - objrefname, - objrefname); + bind(staticsType, typedef_name_type::StaticAbiClass, true)); } } From 0008648ad2937b9df27e7ad47ce42db2ce5a4485 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Mon, 6 Nov 2023 17:41:33 -0800 Subject: [PATCH 08/10] Only on .NET --- src/Tests/UnitTest/TestComponentCSharp_Tests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index a0135ddf3..e7e8cbcca 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -3091,6 +3091,7 @@ public void TestWeakReferenceEventsFromMultipleContexts() staThread.Join(); } +#if NET [Fact] public void TestActivationFactoriesFromMultipleContexts() { @@ -3129,6 +3130,7 @@ public void TestActivationFactoriesFromMultipleContexts() Assert.Null(exception); } +#endif [Fact] public void TestDictionary() From 12ba6569fbadaabb83638140a2c7028a42081bce Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Mon, 6 Nov 2023 21:08:30 -0800 Subject: [PATCH 09/10] Use Attach to avoid extra AddRef. --- src/cswinrt/strings/WinRT.cs | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/cswinrt/strings/WinRT.cs b/src/cswinrt/strings/WinRT.cs index 85a486b58..08000d5e7 100644 --- a/src/cswinrt/strings/WinRT.cs +++ b/src/cswinrt/strings/WinRT.cs @@ -373,7 +373,7 @@ public unsafe (FactoryObjectReference obj, int hr) GetA int hr = _GetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &instancePtr); if (hr == 0) { - var objRef = FactoryObjectReference.FromAbi(instancePtr); + var objRef = FactoryObjectReference.Attach(ref instancePtr); return (objRef, hr); } else @@ -432,34 +432,7 @@ public static unsafe (FactoryObjectReference obj, int hr) GetActivationFactor int hr = Platform.RoGetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &iid, &instancePtr); if (hr == 0) { - var objRef = FactoryObjectReference.FromAbi(instancePtr); - return (objRef, hr); - } - else - { - return (null, hr); - } - } - } - finally - { - MarshalInspectable.DisposeAbi(instancePtr); - } - } - - public static unsafe (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId, Guid iid) - { - var module = Instance; // Ensure COM is initialized - IntPtr instancePtr = IntPtr.Zero; - try - { - MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); - fixed (void* ___runtimeClassId = __runtimeClassId) - { - int hr = Platform.RoGetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &iid, &instancePtr); - if (hr == 0) - { - var objRef = ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); + var objRef = FactoryObjectReference.Attach(ref instancePtr); return (objRef, hr); } else From 7d917ac82ec0d2a83453888bb3b40bdc612fb7a9 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Wed, 8 Nov 2023 11:02:10 -0800 Subject: [PATCH 10/10] Comments. --- src/cswinrt/strings/WinRT.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cswinrt/strings/WinRT.cs b/src/cswinrt/strings/WinRT.cs index 08000d5e7..6f703ac6e 100644 --- a/src/cswinrt/strings/WinRT.cs +++ b/src/cswinrt/strings/WinRT.cs @@ -505,6 +505,8 @@ public bool IsObjectInContext() // If we are free threaded, we do not need to keep track of context. // This can either be if the object implements IAgileObject or the free threaded marshaler. + // We only check IAgileObject for now as the necessary code to check the + // free threaded marshaler is not exposed from WinRT.Runtime. private unsafe static bool IsFreeThreaded(IObjectReference objRef) { if (objRef.TryAs(InterfaceIIDs.IAgileObject_IID, out var agilePtr) >= 0) @@ -521,7 +523,7 @@ internal static class IActivationFactoryMethods public static unsafe ObjectReference ActivateInstance(IObjectReference obj) { IntPtr instancePtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)obj.ThisPtr)[6](obj.ThisPtr, out instancePtr)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)obj.ThisPtr)[6](obj.ThisPtr, &instancePtr)); try { return ComWrappersSupport.GetObjectReferenceForInterface(instancePtr);