Skip to content

Commit

Permalink
Move ActivationFacory into WinRT.Runtime.dll
Browse files Browse the repository at this point in the history
  • Loading branch information
jlaanstra committed Jan 24, 2024
1 parent b207988 commit 9be15b9
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 294 deletions.
272 changes: 272 additions & 0 deletions src/WinRT.Runtime/ActivationFactory.cs
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);
}
}
}
}
}
}
}
10 changes: 10 additions & 0 deletions src/WinRT.Runtime/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion src/WinRT.Runtime/MatchingRefApiCompatBaseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,5 @@ MembersMustExist : Member 'public System.Boolean WinRT.IObjectReference.IsInCurr
MembersMustExist : Member 'public WinRT.ObjectReference<T> WinRT.ObjectReference<T>.Attach(System.IntPtr, System.Guid)' does not exist in the reference but it does exist in the implementation.
MembersMustExist : Member 'public WinRT.ObjectReference<T> WinRT.ObjectReference<T>.FromAbi(System.IntPtr, System.Guid)' does not exist in the reference but it does exist in the implementation.
MembersMustExist : Member 'public WinRT.ObjectReference<T> WinRT.ObjectReference<T>.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
Loading

0 comments on commit 9be15b9

Please sign in to comment.