|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Reflection;
using System.Runtime.InteropServices;
using WinRT.Interop;
#pragma warning disable 0169 // The field 'xxx' is never used
#pragma warning disable 0649 // Field 'xxx' is never assigned to, and will always have its default value
namespace WinRT
{
internal static class DelegateExtensions
{
public static void DynamicInvokeAbi(this System.Delegate del, object[] invoke_params)
{
Marshal.ThrowExceptionForHR((int)del.DynamicInvoke(invoke_params));
}
public static T AsDelegate<T>(this MulticastDelegate del)
{
return Marshal.GetDelegateForFunctionPointer<T>(
Marshal.GetFunctionPointerForDelegate(del));
}
}
internal class Platform
{
[DllImport("api-ms-win-core-com-l1-1-0.dll")]
public static extern int CoDecrementMTAUsage(IntPtr cookie);
[DllImport("api-ms-win-core-com-l1-1-0.dll")]
public static extern unsafe int CoIncrementMTAUsage(IntPtr* cookie);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr moduleHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr moduleHandle, [MarshalAs(UnmanagedType.LPStr)] string functionName);
public static T GetProcAddress<T>(IntPtr moduleHandle)
{
IntPtr functionPtr = Platform.GetProcAddress(moduleHandle, typeof(T).Name);
if (functionPtr == IntPtr.Zero)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
return Marshal.GetDelegateForFunctionPointer<T>(functionPtr);
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibraryExW([MarshalAs(UnmanagedType.LPWStr)] string fileName, IntPtr fileHandle, uint flags);
[DllImport("api-ms-win-core-winrt-l1-1-0.dll")]
public static extern unsafe int RoGetActivationFactory(IntPtr runtimeClassId, ref Guid iid, IntPtr* factory);
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
public static extern unsafe int WindowsCreateString([MarshalAs(UnmanagedType.LPWStr)] string sourceString,
int length,
IntPtr* hstring);
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
public static extern unsafe int WindowsCreateStringReference(char* sourceString,
int length,
IntPtr* hstring_header,
IntPtr* hstring);
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int WindowsDeleteString(IntPtr hstring);
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
public static extern unsafe int WindowsDuplicateString(IntPtr sourceString,
IntPtr* hstring);
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
public static extern unsafe char* WindowsGetStringRawBuffer(IntPtr hstring, uint* length);
}
internal struct VftblPtr
{
public IntPtr Vftbl;
}
internal class DllModule
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public unsafe delegate int DllGetActivationFactory(
IntPtr activatableClassId,
out IntPtr activationFactory);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public unsafe delegate int DllCanUnloadNow();
readonly string _fileName;
readonly IntPtr _moduleHandle;
readonly DllGetActivationFactory _GetActivationFactory;
readonly DllCanUnloadNow _CanUnloadNow; // TODO: Eventually periodically call
static readonly string _currentModuleDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
static Dictionary<string, DllModule> _cache = new System.Collections.Generic.Dictionary<string, DllModule>();
public static DllModule Load(string fileName)
{
lock (_cache)
{
DllModule module;
if (!_cache.TryGetValue(fileName, out module))
{
module = new DllModule(fileName);
_cache[fileName] = module;
}
return module;
}
}
DllModule(string fileName)
{
_fileName = fileName;
// 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.
_moduleHandle = Platform.LoadLibraryExW(System.IO.Path.Combine(_currentModuleDirectory, fileName), IntPtr.Zero, /* LOAD_WITH_ALTERED_SEARCH_PATH */ 8);
#if !NETSTANDARD2_0 && !NETCOREAPP2_0
if (_moduleHandle == IntPtr.Zero)
{
try
{
// Allow runtime to find module in RID-specific relative subfolder
_moduleHandle = NativeLibrary.Load(fileName, Assembly.GetExecutingAssembly(), null);
}
catch (Exception) { }
}
#endif
if (_moduleHandle == IntPtr.Zero)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
_GetActivationFactory = Platform.GetProcAddress<DllGetActivationFactory>(_moduleHandle);
var canUnloadNow = Platform.GetProcAddress(_moduleHandle, "DllCanUnloadNow");
if (canUnloadNow != IntPtr.Zero)
{
_CanUnloadNow = Marshal.GetDelegateForFunctionPointer<DllCanUnloadNow>(canUnloadNow);
}
}
public unsafe (ObjectReference<IActivationFactoryVftbl> obj, int hr) GetActivationFactory(string runtimeClassId)
{
IntPtr instancePtr;
var hstrRuntimeClassId = MarshalString.CreateMarshaler(runtimeClassId);
int hr = _GetActivationFactory(MarshalString.GetAbi(hstrRuntimeClassId), out instancePtr);
return (hr == 0 ? ObjectReference<IActivationFactoryVftbl>.Attach(ref instancePtr) : null, hr);
}
~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 class WeakLazy<T> where T : class, new()
{
WeakReference<T> _instance = new WeakReference<T>(null);
public T Value
{
get
{
lock (_instance)
{
T value;
if (!_instance.TryGetTarget(out value))
{
value = new T();
_instance.SetTarget(value);
}
return value;
}
}
}
}
internal class WinrtModule
{
readonly IntPtr _mtaCookie;
static Lazy<WinrtModule> _instance = new Lazy<WinrtModule>();
public static WinrtModule Instance => _instance.Value;
public unsafe WinrtModule()
{
IntPtr mtaCookie;
Marshal.ThrowExceptionForHR(Platform.CoIncrementMTAUsage(&mtaCookie));
_mtaCookie = mtaCookie;
}
public static unsafe (ObjectReference<IActivationFactoryVftbl> obj, int hr) GetActivationFactory(string runtimeClassId)
{
var module = Instance; // Ensure COM is initialized
Guid iid = typeof(IActivationFactoryVftbl).GUID;
IntPtr instancePtr;
var hstrRuntimeClassId = MarshalString.CreateMarshaler(runtimeClassId);
int hr = Platform.RoGetActivationFactory(MarshalString.GetAbi(hstrRuntimeClassId), ref iid, &instancePtr);
return (hr == 0 ? ObjectReference<IActivationFactoryVftbl>.Attach(ref instancePtr) : null, hr);
}
~WinrtModule()
{
Marshal.ThrowExceptionForHR(Platform.CoDecrementMTAUsage(_mtaCookie));
}
}
internal class BaseActivationFactory
{
private ObjectReference<IActivationFactoryVftbl> _IActivationFactory;
public BaseActivationFactory(string typeNamespace, string typeFullName)
{
var runtimeClassId = TypeExtensions.RemoveNamespacePrefix(typeFullName);
// Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure
int hr;
(_IActivationFactory, hr) = WinrtModule.GetActivationFactory(runtimeClassId);
if (_IActivationFactory != null) { return; }
var moduleName = typeNamespace;
while (true)
{
try
{
(_IActivationFactory, _) = DllModule.Load($"{moduleName}.dll").GetActivationFactory(runtimeClassId);
if (_IActivationFactory != null) { return; }
}
catch (Exception) { }
var lastSegment = moduleName.LastIndexOf('.');
if (lastSegment <= 0)
{
Marshal.ThrowExceptionForHR(hr);
}
moduleName = moduleName.Remove(lastSegment);
}
}
public unsafe ObjectReference<I> _ActivateInstance<I>()
{
Marshal.ThrowExceptionForHR(_IActivationFactory.Vftbl.ActivateInstance(_IActivationFactory.ThisPtr, out IntPtr instancePtr));
try
{
return ComWrappersSupport.GetObjectReferenceForInterface(instancePtr).As<I>();
}
finally
{
MarshalInspectable.DisposeAbi(instancePtr);
}
}
public ObjectReference<I> _As<I>() => _IActivationFactory.As<I>();
}
internal class ActivationFactory<T> : BaseActivationFactory
{
public ActivationFactory() : base(typeof(T).Namespace, typeof(T).FullName) { }
static WeakLazy<ActivationFactory<T>> _factory = new WeakLazy<ActivationFactory<T>>();
public static ObjectReference<I> As<I>() => _factory.Value._As<I>();
public static ObjectReference<I> ActivateInstance<I>() => _factory.Value._ActivateInstance<I>();
}
}
|