Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Add Per-assembly Load Native Library callbacks #21555

Merged
merged 2 commits into from
Jan 18, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/System.Private.CoreLib/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2428,7 +2428,10 @@
<data name="InvalidOperation_CannotImportGlobalFromDifferentModule" xml:space="preserve">
<value>Unable to import a global method or field from a different module.</value>
</data>
<data name="InvalidOperation_CannotRemoveLastFromEmptyCollection" xml:space="preserve">
<data name="InvalidOperation_CannotRegisterSecondResolver" xml:space="preserve">
<value>A resolver is already set for the assembly.</value>
</data>
<data name="InvalidOperation_CannotRemoveLastFromEmptyCollection" xml:space="preserve">
<value>Cannot remove the last element from an empty collection.</value>
</data>
<data name="InvalidOperation_CannotRestoreUnsupressedFlow" xml:space="preserve">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,26 @@
using System.Runtime.ConstrainedExecution;
using Win32Native = Microsoft.Win32.Win32Native;
using System.Diagnostics;
using System.Threading;

namespace System.Runtime.InteropServices
{

AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
/// <summary>
/// A delegate used to resolve native libraries via callback.
/// </summary>
/// <param name="libraryName">The native library to resolve</param>
/// <param name="assembly">The assembly requesting the resolution</param>
/// <param name="DllImportSearchPath?">
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
/// The DllImportSearchPathsAttribute on the PInvoke, if any.
/// Otherwise, the DllImportSearchPathsAttribute on the assembly, if any.
/// Otherwise null.
/// </param>
/// <returns>The handle for the loaded native library on success, null on failure</returns>
public delegate IntPtr DllImportResolver(string libraryName,
Assembly assembly,
DllImportSearchPath? searchPath);

/// <summary>
/// APIs for managing Native Libraries
/// </summary>
Expand Down Expand Up @@ -58,7 +75,9 @@ public static bool TryLoad(string libraryPath, out IntPtr handle)
/// Otherwise, the flags specified by the DefaultDllImportSearchPaths attribute on the
/// calling assembly (if any) are used.
/// This LoadLibrary() method does not invoke the managed call-backs for native library resolution:
/// * The per-assembly registered callback
/// * AssemblyLoadContext.LoadUnmanagedDll()
/// * AssemblyLoadContext.ResolvingUnmanagedDllEvent
/// </summary>
/// <param name="libraryName">The name of the native library to be loaded</param>
/// <param name="assembly">The assembly loading the native library</param>
Expand Down Expand Up @@ -117,7 +136,6 @@ public static bool TryLoad(string libraryName, Assembly assembly, DllImportSearc
/// No action if the input handle is null.
/// </summary>
/// <param name="handle">The native library handle to be freed</param>
/// <exception cref="System.InvalidOperationException">If the operation fails</exception>
public static void Free(IntPtr handle)
{
FreeLib(handle);
Expand Down Expand Up @@ -161,6 +179,78 @@ public static bool TryGetExport(IntPtr handle, string name, out IntPtr address)
return address != IntPtr.Zero;
}

/// <summary>
/// Map from assembly to native-library resolver.
/// Interop specific fields and properties are generally not added to Assembly class.
/// Therefore, this table uses weak assembly pointers to indirectly achieve
/// similar behavior.
/// </summary>
private static ConditionalWeakTable<Assembly, DllImportResolver> s_nativeDllResolveMap = null;

/// <summary>
/// Set a callback for resolving native library imports from an assembly.
/// This per-assembly resolver is the first attempt to resolve native library loads
/// initiated by this assembly.
///
/// Only one resolver can be registered per assembly.
/// Trying to register a second resolver fails with InvalidOperationException.
/// </summary>
/// <param name="assembly">The assembly for which the resolver is registered</param>
/// <param name="resolver">The resolver callback to register</param>
/// <exception cref="System.ArgumentNullException">If assembly or resolver is null</exception>
/// <exception cref="System.ArgumentException">If a resolver is already set for this assembly</exception>
public static void SetDllImportResolver(Assembly assembly, DllImportResolver resolver)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
if (resolver == null)
throw new ArgumentNullException(nameof(resolver));
if (!(assembly is RuntimeAssembly))
throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);

if (s_nativeDllResolveMap == null)
{
Interlocked.CompareExchange(ref s_nativeDllResolveMap,
new ConditionalWeakTable<Assembly, DllImportResolver>(), null);
}

try
{
s_nativeDllResolveMap.Add(assembly, resolver);
}
catch (ArgumentException e)
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
{
// ConditionalWealTable throws ArgumentException if the Key already exists
throw new InvalidOperationException(SR.InvalidOperation_CannotRegisterSecondResolver);
}
}

/// <summary>
/// The helper function that calls the per-assembly native-library resolver
/// if one is registered for this assembly.
/// </summary>
/// <param name="libraryName">The native library to load</param>
/// <param name="assembly">The assembly trying load the native library</param>
/// <param name="hasDllImportSearchPathFlags">If the pInvoke has DefaultDllImportSearchPathAttribute</param>
/// <param name="dllImportSearchPathFlags">If hasdllImportSearchPathFlags is true, the flags in
/// DefaultDllImportSearchPathAttribute; meaningless otherwise </param>
/// <returns>The handle for the loaded library on success. Null on failure.</returns>
internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly,
bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags)
{
if (s_nativeDllResolveMap == null)
{
return IntPtr.Zero;
}

if (!s_nativeDllResolveMap.TryGetValue(assembly, out DllImportResolver resolver))
{
return IntPtr.Zero;
}

return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null);
}

/// External functions that implement the NativeLibrary interface

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
Expand Down
1 change: 1 addition & 0 deletions src/vm/callhelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ enum DispatchCallSimpleFlags
#define STRINGREF_TO_ARGHOLDER(x) (LPVOID)STRINGREFToObject(x)
#define PTR_TO_ARGHOLDER(x) (LPVOID)x
#define DWORD_TO_ARGHOLDER(x) (LPVOID)(SIZE_T)x
#define BOOL_TO_ARGHOLDER(x) DWORD_TO_ARGHOLDER(!!(x))

#define INIT_VARIABLES(count) \
DWORD __numArgs = count; \
Expand Down
Loading