-
Notifications
You must be signed in to change notification settings - Fork 107
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move ActivationFacory into WinRT.Runtime.dll
- Loading branch information
Showing
4 changed files
with
284 additions
and
366 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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]<IntPtr, IntPtr*, int> _GetActivationFactory; | ||
readonly delegate* unmanaged[Stdcall]<int> _CanUnloadNow; // TODO: Eventually periodically call | ||
|
||
static readonly string _currentModuleDirectory = AppContext.BaseDirectory; | ||
|
||
static Dictionary<string, DllModule> _cache = new System.Collections.Generic.Dictionary<string, DllModule>(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<byte> 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]<IntPtr, IntPtr*, int>)getActivationFactory; | ||
|
||
void* canUnloadNow = null; | ||
#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES | ||
ReadOnlySpan<byte> functionName = "DllCanUnloadNow"u8; | ||
#else | ||
string functionName = "DllCanUnloadNow"; | ||
#endif | ||
canUnloadNow = Platform.TryGetProcAddress(_moduleHandle, functionName); | ||
|
||
if (canUnloadNow != null) | ||
{ | ||
_CanUnloadNow = (delegate* unmanaged[Stdcall]<int>)canUnloadNow; | ||
} | ||
} | ||
|
||
public unsafe (ObjectReference<IActivationFactoryVftbl> 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<IActivationFactoryVftbl>.Attach(ref instancePtr); | ||
return (objRef, hr); | ||
} | ||
else | ||
{ | ||
return (null, hr); | ||
} | ||
} | ||
} | ||
finally | ||
{ | ||
MarshalInspectable<object>.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<I> obj, int hr) GetActivationFactory<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<I>.Attach(ref instancePtr); | ||
return (objRef, hr); | ||
} | ||
else | ||
{ | ||
return (null, hr); | ||
} | ||
} | ||
} | ||
finally | ||
{ | ||
MarshalInspectable<object>.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<IActivationFactoryVftbl> factory; | ||
(factory, hr) = WinrtModule.GetActivationFactory<IActivationFactoryVftbl>(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<I> 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<I> factory; | ||
(factory, hr) = WinrtModule.GetActivationFactory<I>(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<IActivationFactoryVftbl> activationFactory; | ||
(activationFactory, hr) = module.GetActivationFactory(typeName); | ||
if (activationFactory != null) | ||
{ | ||
using (activationFactory) | ||
{ | ||
return activationFactory.As<I>(iid); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
Compat issues with assembly WinRT.Runtime: | ||
TypesMustExist : Type 'WinRT.IWinRTExposedTypeDetails' does not exist in the reference but it does exist in the implementation. | ||
TypesMustExist : Type 'WinRT.WinRTExposedTypeAttribute' does not exist in the reference but it does exist in the implementation. | ||
Total Issues: 2 | ||
TypesMustExist : Type 'WinRT.ActivationFactory' does not exist in the reference but it does exist in the implementation. | ||
Total Issues: 3 |
Oops, something went wrong.