Skip to content

Commit 9462e00

Browse files
Add Per-assembly Load Native Library callbacks
This Change implements the Native Library resolution Call-backs proposed in https://github.com/dotnet/corefx/issues/32015
1 parent 8ede2d4 commit 9462e00

File tree

13 files changed

+394
-65
lines changed

13 files changed

+394
-65
lines changed

src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs

+86-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@
1212

1313
namespace System.Runtime.InteropServices
1414
{
15+
16+
/// <summary>
17+
/// A delegate used to resolve native libraries via callback.
18+
/// </summary>
19+
/// <param name="libraryName">The native library to resolve</param>
20+
/// <param name="assembly">The assembly requesting the resolution</param>
21+
/// <param name="DllImportSearchPath?">
22+
/// The DllImportSearchPathsAttribute on the PInvoke, if any.
23+
/// Otherwise, the DllImportSearchPathsAttribute on the assembly, if any.
24+
/// Otherwise null.
25+
/// </param>
26+
/// <returns>The handle for the loaded native library on success, null on failure</returns>
27+
public delegate IntPtr DllImportResolver(string libraryName,
28+
Assembly assembly,
29+
DllImportSearchPath? searchPath);
30+
1531
/// <summary>
1632
/// APIs for managing Native Libraries
1733
/// </summary>
@@ -58,7 +74,9 @@ public static bool TryLoad(string libraryPath, out IntPtr handle)
5874
/// Otherwise, the flags specified by the DefaultDllImportSearchPaths attribute on the
5975
/// calling assembly (if any) are used.
6076
/// This LoadLibrary() method does not invoke the managed call-backs for native library resolution:
77+
/// * The per-assembly registered callback
6178
/// * AssemblyLoadContext.LoadUnmanagedDll()
79+
/// * AssemblyLoadContext.ResolvingUnmanagedDllEvent
6280
/// </summary>
6381
/// <param name="libraryName">The name of the native library to be loaded</param>
6482
/// <param name="assembly">The assembly loading the native library</param>
@@ -117,7 +135,6 @@ public static bool TryLoad(string libraryName, Assembly assembly, DllImportSearc
117135
/// No action if the input handle is null.
118136
/// </summary>
119137
/// <param name="handle">The native library handle to be freed</param>
120-
/// <exception cref="System.InvalidOperationException">If the operation fails</exception>
121138
public static void Free(IntPtr handle)
122139
{
123140
FreeLib(handle);
@@ -161,6 +178,74 @@ public static bool TryGetExport(IntPtr handle, string name, out IntPtr address)
161178
return address != IntPtr.Zero;
162179
}
163180

181+
/// <summary>
182+
/// Map from assembly to native-library resolver.
183+
/// Interop specific fields and properties are generally not added to Assembly class.
184+
/// Therefore, this table uses weak assembly pointers to indirectly achieve
185+
/// similar behavior.
186+
/// </summary>
187+
public static ConditionalWeakTable<Assembly, DllImportResolver> s_nativeDllResolveMap = null;
188+
189+
/// <summary>
190+
/// Set a callback for resolving native library imports from an assembly.
191+
/// This per-assembly resolver is the first attempt to resolve native library loads
192+
/// initiated by this assembly.
193+
///
194+
/// Only one resolver can be registered per assembly.
195+
/// Trying to register a second resolver fails with InvalidOperationException.
196+
/// </summary>
197+
/// <param name="assembly">The assembly for which the resolver is registered</param>
198+
/// <param name="resolver">The resolver callback to register</param>
199+
/// <exception cref="System.ArgumentNullException">If assembly or resolver is null</exception>
200+
/// <exception cref="System.ArgumentException">If a resolver is already set for this assembly</exception>
201+
public static void SetDllImportResolver(Assembly assembly, DllImportResolver resolver)
202+
{
203+
if (assembly == null)
204+
throw new ArgumentNullException(nameof(assembly));
205+
if (resolver == null)
206+
throw new ArgumentNullException(nameof(resolver));
207+
if (!(assembly is RuntimeAssembly))
208+
throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
209+
210+
if (s_nativeDllResolveMap == null)
211+
{
212+
s_nativeDllResolveMap = new ConditionalWeakTable<Assembly, DllImportResolver>();
213+
}
214+
215+
try
216+
{
217+
s_nativeDllResolveMap.Add(assembly, resolver);
218+
}
219+
catch (ArgumentException e)
220+
{
221+
// ConditionalWealTable throws ArgumentException if the Key already exists
222+
throw new InvalidOperationException("Resolver is alerady Set for the Assembly");
223+
}
224+
}
225+
226+
/// <summary>
227+
/// The helper function that calls the per-assembly native-library resolver
228+
/// if one is registered for this assembly.
229+
/// </summary>
230+
/// <param name="libraryName">The native library to load</param>
231+
/// <param name="assembly">The assembly trying load the native library</param>
232+
/// <param name="hasDllImportSearchPathFlags">If the pInvoke has DefaultDllImportSearchPathAttribute</param>
233+
/// <param name="dllImportSearchPathFlags">If hasdllImportSearchPathFlags is true, the flags in
234+
/// DefaultDllImportSearchPathAttribute; meaningless otherwise </param>
235+
/// <returns>The handle for the loaded library on success. Null on failure.</returns>
236+
internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly,
237+
bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags)
238+
{
239+
DllImportResolver resolver;
240+
241+
if (!s_nativeDllResolveMap.TryGetValue(assembly, out resolver))
242+
{
243+
return IntPtr.Zero;
244+
}
245+
246+
return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null);
247+
}
248+
164249
/// External functions that implement the NativeLibrary interface
165250

166251
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]

src/vm/callhelpers.h

+1
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ enum DispatchCallSimpleFlags
566566
#define STRINGREF_TO_ARGHOLDER(x) (LPVOID)STRINGREFToObject(x)
567567
#define PTR_TO_ARGHOLDER(x) (LPVOID)x
568568
#define DWORD_TO_ARGHOLDER(x) (LPVOID)(SIZE_T)x
569+
#define BOOL_TO_ARGHOLDER(x) DWORD_TO_ARGHOLDER(!!(x))
569570

570571
#define INIT_VARIABLES(count) \
571572
DWORD __numArgs = count; \

0 commit comments

Comments
 (0)