From 9be15b91e968cdaffced9e1900f4847d62a4fabf Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Fri, 17 Nov 2023 17:43:41 -0800 Subject: [PATCH] Move ActivationFacory into WinRT.Runtime.dll --- src/WinRT.Runtime/ActivationFactory.cs | 272 ++++++++++++++++ src/WinRT.Runtime/Context.cs | 10 + .../MatchingRefApiCompatBaseline.txt | 3 +- src/cswinrt/strings/WinRT.cs | 293 ------------------ 4 files changed, 284 insertions(+), 294 deletions(-) create mode 100644 src/WinRT.Runtime/ActivationFactory.cs diff --git a/src/WinRT.Runtime/ActivationFactory.cs b/src/WinRT.Runtime/ActivationFactory.cs new file mode 100644 index 000000000..f923f360b --- /dev/null +++ b/src/WinRT.Runtime/ActivationFactory.cs @@ -0,0 +1,272 @@ +// 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 + { + 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)); + } + } + +#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; + } + } + } + } + + public static ObjectReference Get< +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] +#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) + { + 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)) + { + ObjectReference activationFactory; + (activationFactory, hr) = module.GetActivationFactory(typeName); + if (activationFactory != null) + { + using (activationFactory) + { + return activationFactory.As(iid); + } + } + } + } + } + } +} diff --git a/src/WinRT.Runtime/Context.cs b/src/WinRT.Runtime/Context.cs index 78044620b..5a5d39eed 100644 --- a/src/WinRT.Runtime/Context.cs +++ b/src/WinRT.Runtime/Context.cs @@ -9,6 +9,16 @@ namespace WinRT { 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/strings/WinRT.cs b/src/cswinrt/strings/WinRT.cs index 32e3a58cc..effbaac6e 100644 --- a/src/cswinrt/strings/WinRT.cs +++ b/src/cswinrt/strings/WinRT.cs @@ -335,299 +335,6 @@ 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) - { - // 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 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) - { -#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); - } - } - } - } - } - } internal class ComponentActivationFactory : global::WinRT.Interop.IActivationFactory {