|
9 | 9 | using System.Runtime.ConstrainedExecution;
|
10 | 10 | using Win32Native = Microsoft.Win32.Win32Native;
|
11 | 11 | using System.Diagnostics;
|
| 12 | +using System.Threading; |
12 | 13 |
|
13 | 14 | namespace System.Runtime.InteropServices
|
14 | 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="searchPath"> |
| 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 | + |
15 | 31 | /// <summary>
|
16 | 32 | /// APIs for managing Native Libraries
|
17 | 33 | /// </summary>
|
@@ -58,7 +74,9 @@ public static bool TryLoad(string libraryPath, out IntPtr handle)
|
58 | 74 | /// Otherwise, the flags specified by the DefaultDllImportSearchPaths attribute on the
|
59 | 75 | /// calling assembly (if any) are used.
|
60 | 76 | /// This LoadLibrary() method does not invoke the managed call-backs for native library resolution:
|
| 77 | + /// * The per-assembly registered callback |
61 | 78 | /// * AssemblyLoadContext.LoadUnmanagedDll()
|
| 79 | + /// * AssemblyLoadContext.ResolvingUnmanagedDllEvent |
62 | 80 | /// </summary>
|
63 | 81 | /// <param name="libraryName">The name of the native library to be loaded</param>
|
64 | 82 | /// <param name="assembly">The assembly loading the native library</param>
|
@@ -117,7 +135,6 @@ public static bool TryLoad(string libraryName, Assembly assembly, DllImportSearc
|
117 | 135 | /// No action if the input handle is null.
|
118 | 136 | /// </summary>
|
119 | 137 | /// <param name="handle">The native library handle to be freed</param>
|
120 |
| - /// <exception cref="System.InvalidOperationException">If the operation fails</exception> |
121 | 138 | public static void Free(IntPtr handle)
|
122 | 139 | {
|
123 | 140 | FreeLib(handle);
|
@@ -161,6 +178,78 @@ public static bool TryGetExport(IntPtr handle, string name, out IntPtr address)
|
161 | 178 | return address != IntPtr.Zero;
|
162 | 179 | }
|
163 | 180 |
|
| 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 | + private 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 | + Interlocked.CompareExchange(ref s_nativeDllResolveMap, |
| 213 | + new ConditionalWeakTable<Assembly, DllImportResolver>(), null); |
| 214 | + } |
| 215 | + |
| 216 | + try |
| 217 | + { |
| 218 | + s_nativeDllResolveMap.Add(assembly, resolver); |
| 219 | + } |
| 220 | + catch (ArgumentException) |
| 221 | + { |
| 222 | + // ConditionalWealTable throws ArgumentException if the Key already exists |
| 223 | + throw new InvalidOperationException(SR.InvalidOperation_CannotRegisterSecondResolver); |
| 224 | + } |
| 225 | + } |
| 226 | + |
| 227 | + /// <summary> |
| 228 | + /// The helper function that calls the per-assembly native-library resolver |
| 229 | + /// if one is registered for this assembly. |
| 230 | + /// </summary> |
| 231 | + /// <param name="libraryName">The native library to load</param> |
| 232 | + /// <param name="assembly">The assembly trying load the native library</param> |
| 233 | + /// <param name="hasDllImportSearchPathFlags">If the pInvoke has DefaultDllImportSearchPathAttribute</param> |
| 234 | + /// <param name="dllImportSearchPathFlags">If hasdllImportSearchPathFlags is true, the flags in |
| 235 | + /// DefaultDllImportSearchPathAttribute; meaningless otherwise </param> |
| 236 | + /// <returns>The handle for the loaded library on success. Null on failure.</returns> |
| 237 | + internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly, |
| 238 | + bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags) |
| 239 | + { |
| 240 | + if (s_nativeDllResolveMap == null) |
| 241 | + { |
| 242 | + return IntPtr.Zero; |
| 243 | + } |
| 244 | + |
| 245 | + if (!s_nativeDllResolveMap.TryGetValue(assembly, out DllImportResolver resolver)) |
| 246 | + { |
| 247 | + return IntPtr.Zero; |
| 248 | + } |
| 249 | + |
| 250 | + return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null); |
| 251 | + } |
| 252 | + |
164 | 253 | /// External functions that implement the NativeLibrary interface
|
165 | 254 |
|
166 | 255 | [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
|
|
0 commit comments