diff --git a/src/WinRT.Runtime/ActivationFactory.cs b/src/WinRT.Runtime/ActivationFactory.cs new file mode 100644 index 000000000..1c3132095 --- /dev/null +++ b/src/WinRT.Runtime/ActivationFactory.cs @@ -0,0 +1,289 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices; +using WinRT.Interop; + +namespace WinRT +{ + internal unsafe sealed class DllModule + { + private static readonly string _currentModuleDirectory = AppContext.BaseDirectory; + + private static readonly Dictionary _cache = new Dictionary(StringComparer.Ordinal); + + private readonly string _fileName; + private readonly IntPtr _moduleHandle; + private readonly delegate* unmanaged[Stdcall] _GetActivationFactory; + private readonly delegate* unmanaged[Stdcall] _CanUnloadNow; // TODO: Eventually periodically call + + public static bool TryLoad(string fileName, out DllModule module) + { + lock (_cache) + { + if (_cache.TryGetValue(fileName, out module)) + { + return true; + } + else if (TryCreate(fileName, out module)) + { + _cache[fileName] = module; + return true; + } + return false; + } + } + + private static bool TryCreate(string fileName, out DllModule module) + { + // Explicitly look for module in the same directory as this one, and + // use altered search path to ensure any dependencies in the same directory are found. + IntPtr moduleHandle = IntPtr.Zero; + moduleHandle = Platform.LoadLibraryExW(System.IO.Path.Combine(_currentModuleDirectory, fileName), IntPtr.Zero, /* LOAD_WITH_ALTERED_SEARCH_PATH */ 8); +#if NET + if (moduleHandle == IntPtr.Zero) + { + NativeLibrary.TryLoad(fileName, Assembly.GetExecutingAssembly(), null, out moduleHandle); + } +#endif + if (moduleHandle == IntPtr.Zero) + { + module = null; + return false; + } + + void* getActivationFactory = null; + +#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES + ReadOnlySpan functionName = "DllGetActivationFactory"u8; +#else + string functionName = "DllGetActivationFactory"; +#endif + getActivationFactory = Platform.TryGetProcAddress(moduleHandle, functionName); + if (getActivationFactory == null) + { + module = null; + return false; + } + + module = new DllModule( + fileName, + moduleHandle, + getActivationFactory); + return true; + } + + private DllModule(string fileName, IntPtr moduleHandle, void* getActivationFactory) + { + _fileName = fileName; + _moduleHandle = moduleHandle; + _GetActivationFactory = (delegate* unmanaged[Stdcall])getActivationFactory; + + void* canUnloadNow = null; +#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES + ReadOnlySpan functionName = "DllCanUnloadNow"u8; +#else + string functionName = "DllCanUnloadNow"; +#endif + canUnloadNow = Platform.TryGetProcAddress(_moduleHandle, functionName); + + if (canUnloadNow != null) + { + _CanUnloadNow = (delegate* unmanaged[Stdcall])canUnloadNow; + } + } + + public (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId) + { + IntPtr instancePtr = IntPtr.Zero; + try + { + MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); + fixed (void* ___runtimeClassId = __runtimeClassId) + { + int hr = _GetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &instancePtr); + if (hr == 0) + { + var objRef = ObjectReference.Attach(ref instancePtr); + return (objRef, hr); + } + else + { + return (null, hr); + } + } + } + finally + { + MarshalInspectable.DisposeAbi(instancePtr); + } + } + + ~DllModule() + { + System.Diagnostics.Debug.Assert(_CanUnloadNow == null || _CanUnloadNow() == 0); // S_OK + lock (_cache) + { + _cache.Remove(_fileName); + } + if ((_moduleHandle != IntPtr.Zero) && !Platform.FreeLibrary(_moduleHandle)) + { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + } + } + } + + internal sealed class WinRTModule + { + private static volatile WinRTModule _instance; + private readonly IntPtr _mtaCookie; + private static WinRTModule MakeWinRTModule() + { + global::System.Threading.Interlocked.CompareExchange(ref _instance, new WinRTModule(), null); + return _instance; + } + public static WinRTModule Instance => _instance ?? MakeWinRTModule(); + + public unsafe WinRTModule() + { + IntPtr mtaCookie; + Marshal.ThrowExceptionForHR(Platform.CoIncrementMTAUsage(&mtaCookie)); + _mtaCookie = mtaCookie; + } + + public static unsafe (ObjectReference obj, int hr) GetActivationFactory< +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] +#endif + I>(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 = ObjectReference.Attach(ref instancePtr); + return (objRef, hr); + } + else + { + return (null, hr); + } + } + } + finally + { + MarshalInspectable.DisposeAbi(instancePtr); + } + } + + ~WinRTModule() + { + Marshal.ThrowExceptionForHR(Platform.CoDecrementMTAUsage(_mtaCookie)); + } + } + +#if EMBED + internal +#else + public +#endif + static class ActivationFactory + { + public static IObjectReference Get(string typeName) + { + // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure + int hr; + ObjectReference factory; + (factory, hr) = WinRTModule.GetActivationFactory(typeName, InterfaceIIDs.IActivationFactory_IID); + if (factory != null) + { + return factory; + } + + 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 + public static IObjectReference Get(string typeName, Guid iid) +#else + public static ObjectReference Get(string typeName, Guid iid) +#endif + { + // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure + int hr; +#if NET + ObjectReference factory; + (factory, hr) = WinRTModule.GetActivationFactory(typeName, iid); + if (factory != null) + { + return factory; + } +#else + ObjectReference factory; + (factory, hr) = WinRTModule.GetActivationFactory(typeName, iid); + if (factory != null) + { + return factory; + } +#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)) + { + ObjectReference activationFactory; + (activationFactory, hr) = module.GetActivationFactory(typeName); + if (activationFactory != null) + { + using (activationFactory) + { +#if NET + return activationFactory.As(iid); +#else + return activationFactory.As(iid); +#endif + } + } + } + } + } + } +} diff --git a/src/WinRT.Runtime/Context.cs b/src/WinRT.Runtime/Context.cs index 78044620b..3c7c8d650 100644 --- a/src/WinRT.Runtime/Context.cs +++ b/src/WinRT.Runtime/Context.cs @@ -7,8 +7,18 @@ namespace WinRT { - static partial class Context + 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; + } + [DllImport("api-ms-win-core-com-l1-1-0.dll")] private static extern unsafe int CoGetObjectContext(Guid* riid, IntPtr* ppv); diff --git a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.txt b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.txt index 887755b32..ecaea5c28 100644 --- a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.txt +++ b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.txt @@ -136,4 +136,5 @@ MembersMustExist : Member 'public System.Boolean WinRT.IObjectReference.IsInCurr MembersMustExist : Member 'public WinRT.ObjectReference WinRT.ObjectReference.Attach(System.IntPtr, System.Guid)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public WinRT.ObjectReference WinRT.ObjectReference.FromAbi(System.IntPtr, System.Guid)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public WinRT.ObjectReference WinRT.ObjectReference.FromAbi(System.IntPtr, T, System.Guid)' does not exist in the reference but it does exist in the implementation. -Total Issues: 137 +TypesMustExist : Type 'WinRT.ActivationFactory' does not exist in the reference but it does exist in the implementation. +Total Issues: 138 diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 53dc2aca5..ca172c758 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -1935,8 +1935,8 @@ private static % _% = new %("%.%", %.IID); { auto objrefname = w.write_temp("%", bind(classType)); w.write(R"( -private static volatile ObjectReference __%; -private static ObjectReference % +private static volatile IObjectReference __%; +private static IObjectReference % { get { @@ -1960,10 +1960,11 @@ private static ObjectReference % classType.TypeName()); } - void write_static_objref_definition(writer& w, std::string_view const& vftblType, TypeDef const& staticsType, TypeDef const& classType) + void write_static_objref_definition(writer& w, TypeDef const& staticsType, TypeDef const& classType) { if (settings.netstandard_compat) { + auto vftblType = w.write_temp("%.Vftbl", bind(staticsType, typedef_name_type::ABI, true)); auto objrefname = w.write_temp("%", bind(staticsType)); w.write(R"( private static volatile ObjectReference<%> __%; @@ -1993,8 +1994,8 @@ private static ObjectReference<%> % => __% ?? Make__%(); { auto objrefname = w.write_temp("%", bind(staticsType)); w.write(R"( -private static volatile ObjectReference<%> __%; -private static ObjectReference<%> % +private static volatile IObjectReference __%; +private static IObjectReference % { get { @@ -2005,18 +2006,15 @@ private static ObjectReference<%> % } else { - return __% = ActivationFactory.Get<%>("%.%", %.IID); + return __% = ActivationFactory.Get("%.%", %.IID); } } } )", - vftblType, objrefname, - vftblType, objrefname, objrefname, objrefname, - vftblType, classType.TypeNamespace(), classType.TypeName(), bind(staticsType, typedef_name_type::StaticAbiClass, true)); @@ -2052,10 +2050,7 @@ private static class _% if (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); + write_static_objref_definition(w, factory_type, class_type); auto cache_object = w.write_temp("%", bind(factory_type)); auto platform_attribute = write_platform_attribute_temp(w, factory_type); @@ -2277,10 +2272,8 @@ private IObjectReference % => __% ?? Make__%(); void write_composable_constructors(writer& w, TypeDef const& composable_type, TypeDef const& class_type, std::string_view visibility) { 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); + + write_static_objref_definition(w, composable_type, class_type); auto cache_object = bind(composable_type); auto default_interface_name = get_default_interface_name(w, class_type, false); @@ -2428,10 +2421,7 @@ Marshal.Release(inner); { if (factory.statics) { - 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); + write_static_objref_definition(w, factory.type, class_type); auto cache_object = w.write_temp("%", bind(factory.type)); auto platform_attribute = write_platform_attribute_temp(w, factory.type); diff --git a/src/cswinrt/strings/WinRT.cs b/src/cswinrt/strings/WinRT.cs index 32e3a58cc..da7ab1b6c 100644 --- a/src/cswinrt/strings/WinRT.cs +++ b/src/cswinrt/strings/WinRT.cs @@ -335,298 +335,22 @@ 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 - { - readonly string _fileName; - readonly IntPtr _moduleHandle; - readonly delegate* unmanaged[Stdcall] _GetActivationFactory; - readonly delegate* unmanaged[Stdcall] _CanUnloadNow; // TODO: Eventually periodically call - - static readonly string _currentModuleDirectory = AppContext.BaseDirectory; - - static Dictionary _cache = new System.Collections.Generic.Dictionary(StringComparer.Ordinal); - - public static bool TryLoad(string fileName, out DllModule module) - { - lock (_cache) - { - if (_cache.TryGetValue(fileName, out module)) - { - return true; - } - else if (TryCreate(fileName, out module)) - { - _cache[fileName] = module; - return true; - } - return false; - } - } - - private static unsafe bool TryCreate(string fileName, out DllModule module) - { - // Explicitly look for module in the same directory as this one, and - // use altered search path to ensure any dependencies in the same directory are found. - IntPtr moduleHandle = IntPtr.Zero; - moduleHandle = Platform.LoadLibraryExW(System.IO.Path.Combine(_currentModuleDirectory, fileName), IntPtr.Zero, /* LOAD_WITH_ALTERED_SEARCH_PATH */ 8); -#if NET - if (moduleHandle == IntPtr.Zero) - { - NativeLibrary.TryLoad(fileName, Assembly.GetExecutingAssembly(), null, out moduleHandle); - } -#endif - if (moduleHandle == IntPtr.Zero) - { - module = null; - return false; - } - - void* getActivationFactory = null; - -#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES - ReadOnlySpan functionName = "DllGetActivationFactory"u8; -#else - string functionName = "DllGetActivationFactory"; -#endif - getActivationFactory = Platform.TryGetProcAddress(moduleHandle, functionName); - if (getActivationFactory == null) - { - module = null; - return false; - } - - module = new DllModule( - fileName, - moduleHandle, - getActivationFactory); - return true; - } - - private DllModule(string fileName, IntPtr moduleHandle, void* getActivationFactory) - { - _fileName = fileName; - _moduleHandle = moduleHandle; - _GetActivationFactory = (delegate* unmanaged[Stdcall])getActivationFactory; - - void* canUnloadNow = null; -#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES - ReadOnlySpan functionName = "DllCanUnloadNow"u8; -#else - string functionName = "DllCanUnloadNow"; -#endif - canUnloadNow = Platform.TryGetProcAddress(_moduleHandle, functionName); - - if (canUnloadNow != null) - { - _CanUnloadNow = (delegate* unmanaged[Stdcall])canUnloadNow; - } - } - - public unsafe (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId) - { - IntPtr instancePtr = IntPtr.Zero; - try - { - MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); - fixed (void* ___runtimeClassId = __runtimeClassId) - { - int hr = _GetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &instancePtr); - if (hr == 0) - { - var objRef = ObjectReference.Attach(ref instancePtr); - return (objRef, hr); - } - else - { - return (null, hr); - } - } - } - finally - { - MarshalInspectable.DisposeAbi(instancePtr); - } - } - - ~DllModule() - { - System.Diagnostics.Debug.Assert(_CanUnloadNow == null || _CanUnloadNow() == 0); // S_OK - lock (_cache) - { - _cache.Remove(_fileName); - } - if ((_moduleHandle != IntPtr.Zero) && !Platform.FreeLibrary(_moduleHandle)) - { - Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); - } - } - } - - internal sealed class WinrtModule - { - readonly IntPtr _mtaCookie; - volatile static WinrtModule _instance; - private static WinrtModule MakeWinRTModule() - { - global::System.Threading.Interlocked.CompareExchange(ref _instance, new WinrtModule(), null); - return _instance; - } - public static WinrtModule Instance => _instance ?? MakeWinRTModule(); - - public unsafe WinrtModule() - { - IntPtr mtaCookie; - Marshal.ThrowExceptionForHR(Platform.CoIncrementMTAUsage(&mtaCookie)); - _mtaCookie = mtaCookie; - } - - 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 = ObjectReference.Attach(ref instancePtr); - return (objRef, hr); - } - else - { - return (null, hr); - } - } - } - finally - { - MarshalInspectable.DisposeAbi(instancePtr); - } - } - - ~WinrtModule() - { - Marshal.ThrowExceptionForHR(Platform.CoDecrementMTAUsage(_mtaCookie)); - } - } 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, &instancePtr)); - try - { - return ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); - } - finally - { - MarshalInspectable.DisposeAbi(instancePtr); - } - } - } - - internal static class ActivationFactory - { - public static ObjectReference Get(string typeName) + { + public static unsafe ObjectReference ActivateInstance(IObjectReference obj) { - // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure - int hr; - ObjectReference factory; - (factory, hr) = WinrtModule.GetActivationFactory(typeName, InterfaceIIDs.IActivationFactory_IID); - if (factory != null) - { - return factory; - } - - 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; - } - } + IntPtr instancePtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)obj.ThisPtr)[6](obj.ThisPtr, &instancePtr)); + try + { + return ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); } - } - -#if NET - public static ObjectReference 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; - ObjectReference factory; - (factory, hr) = WinrtModule.GetActivationFactory(typeName, iid); - if (factory != null) + finally { -#if NET - 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)) - { - ObjectReference activationFactory; - (activationFactory, hr) = module.GetActivationFactory(typeName); - if (activationFactory != null) - { - using (activationFactory) - { - return activationFactory.As(iid); - } - } - } + MarshalInspectable.DisposeAbi(instancePtr); } - } + } } internal class ComponentActivationFactory : global::WinRT.Interop.IActivationFactory