diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs
index 38712ac1b724..fa30af6d297f 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs
@@ -12,6 +12,22 @@
namespace System.Runtime.InteropServices
{
+
+ ///
+ /// A delegate used to resolve native libraries via callback.
+ ///
+ /// The native library to resolve
+ /// The assembly requesting the resolution
+ ///
+ /// The DllImportSearchPathsAttribute on the PInvoke, if any.
+ /// Otherwise, the DllImportSearchPathsAttribute on the assembly, if any.
+ /// Otherwise null.
+ ///
+ /// The handle for the loaded native library on success, null on failure
+ public delegate IntPtr DllImportResolver(string libraryName,
+ Assembly assembly,
+ DllImportSearchPath? searchPath);
+
///
/// APIs for managing Native Libraries
///
@@ -58,7 +74,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
///
/// The name of the native library to be loaded
/// The assembly loading the native library
@@ -161,6 +179,74 @@ public static bool TryGetExport(IntPtr handle, string name, out IntPtr address)
return address != IntPtr.Zero;
}
+ ///
+ /// Map from assembly to native-library-resolution-callback.
+ /// Generally interop specific fields and properties are not added to assembly.
+ /// Therefore, this table uses weak assembly pointers to indirectly achieve
+ /// similar behavior.
+ ///
+ public static ConditionalWeakTable s_nativeDllResolveMap = null;
+
+ ///
+ /// Set a callback for resolving native library imports from an assembly.
+ /// This per-assembly callback is the first attempt to resolve native library loads
+ /// initiated by this assembly.
+ ///
+ /// Only one resolver callback can be registered per assembly.
+ /// Trying to register a second callback fails with InvalidOperationException.
+ ///
+ /// The assembly for which the callback is registered
+ /// The callback to register
+ /// If assembly or resolver is null
+ /// If a callback is already set for this assembly
+ 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);
+
+ DllImportResolver existingResolver = null;
+
+ if (s_nativeDllResolveMap == null)
+ {
+ s_nativeDllResolveMap = new ConditionalWeakTable();
+ }
+ else if (s_nativeDllResolveMap.TryGetValue(assembly, out existingResolver))
+ {
+ if (existingResolver != resolver)
+ throw new InvalidOperationException();
+ return;
+ }
+
+ s_nativeDllResolveMap.Add(assembly, resolver);
+ }
+
+ ///
+ /// The helper function that calls the per-assembly native-library resolver
+ /// if one is registered for this assembly.
+ ///
+ /// The native library to load
+ /// The assembly trying load the native library
+ /// If the pInvoke has DefaultDllImportSearchPathAttribute
+ /// If hasdllImportSearchPathFlags is true, the flags in
+ /// DefaultDllImportSearchPathAttribute; meaningless otherwise
+ /// The handle for the loaded library on success. Null on failure.
+ internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly,
+ bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags)
+ {
+ DllImportResolver resolver;
+
+ if (!s_nativeDllResolveMap.TryGetValue(assembly, out 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)]
diff --git a/src/vm/callhelpers.h b/src/vm/callhelpers.h
index bcd553dcc898..032cec43ced7 100644
--- a/src/vm/callhelpers.h
+++ b/src/vm/callhelpers.h
@@ -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; \
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp
index 2ce100b4b93a..d7228e198304 100644
--- a/src/vm/dllimport.cpp
+++ b/src/vm/dllimport.cpp
@@ -6144,7 +6144,7 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL thr
// static
NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly,
- BOOL hasDllImportSearchFlag, DWORD dllImportSearchFlag,
+ BOOL hasDllImportSearchFlags, DWORD dllImportSearchFlags,
BOOL throwOnError)
{
CONTRACTL
@@ -6157,15 +6157,15 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *
LoadLibErrorTracker errorTracker;
- // First checks if a default DllImportSearchPathFlag was passed in, if so, use that value.
+ // First checks if a default dllImportSearchPathFlags was passed in, if so, use that value.
// Otherwise checks if the assembly has the DefaultDllImportSearchPathsAttribute attribute. If so, use that value.
BOOL searchAssemblyDirectory = TRUE;
- DWORD dllImportSearchPathFlag = 0;
+ DWORD dllImportSearchPathFlags = 0;
- if (hasDllImportSearchFlag)
+ if (hasDllImportSearchFlags)
{
- dllImportSearchPathFlag = dllImportSearchFlag & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
- searchAssemblyDirectory = dllImportSearchFlag & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
+ dllImportSearchPathFlags = dllImportSearchFlags & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
+ searchAssemblyDirectory = dllImportSearchFlags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
}
else
@@ -6174,13 +6174,13 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *
if (pModule->HasDefaultDllImportSearchPathsAttribute())
{
- dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
+ dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
}
}
NATIVE_LIBRARY_HANDLE hmod =
- LoadLibraryModuleBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlag, &errorTracker, libraryName);
+ LoadLibraryModuleBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, &errorTracker, libraryName);
if (throwOnError && (hmod == nullptr))
{
@@ -6199,11 +6199,11 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD
// First checks if the method has DefaultDllImportSearchPathsAttribute. If so, use that value.
// Otherwise checks if the assembly has the attribute. If so, use that value.
BOOL searchAssemblyDirectory = TRUE;
- DWORD dllImportSearchPathFlag = 0;
+ DWORD dllImportSearchPathFlags = 0;
if (pMD->HasDefaultDllImportSearchPathsAttribute())
{
- dllImportSearchPathFlag = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
+ dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
}
else
@@ -6212,13 +6212,13 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD
if (pModule->HasDefaultDllImportSearchPathsAttribute())
{
- dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
+ dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
}
}
Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
- return LoadLibraryModuleBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlag, pErrorTracker, wszLibName);
+ return LoadLibraryModuleBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, pErrorTracker, wszLibName);
}
// static
@@ -6267,23 +6267,34 @@ INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR sy
return address;
}
+#ifndef PLATFORM_UNIX
+BOOL IsWindowsAPISet(PCWSTR wszLibName)
+{
+ STANDARD_VM_CONTRACT;
+
+ // This is replicating quick check from the OS implementation of api sets.
+ return SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 ||
+ SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0;
+}
+#endif // !PLATFORM_UNIX
+
// static
-NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, PCWSTR wszLibName)
+NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, PCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
//Dynamic Pinvoke Support:
//Check if we need to provide the host a chance to provide the unmanaged dll
#ifndef PLATFORM_UNIX
- // Prevent Overriding of Windows API sets.
- // This is replicating quick check from the OS implementation of api sets.
- if (SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 || SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0)
+ if (IsWindowsAPISet(wszLibName))
{
+ // Prevent Overriding of Windows API sets.
return NULL;
}
-#endif
+#endif // !PLATFORM_UNIX
LPVOID hmod = NULL;
+ AppDomain* pDomain = GetAppDomain();
CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
@@ -6349,6 +6360,49 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD,
return (NATIVE_LIBRARY_HANDLE)hmod;
}
+NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaCallback(NDirectMethodDesc * pMD, LPCWSTR wszLibName)
+{
+ STANDARD_VM_CONTRACT;
+
+ NATIVE_LIBRARY_HANDLE handle = NULL;
+
+ DWORD dllImportSearchPathFlags = 0;
+ BOOL hasDllImportSearchPathFlags = pMD->HasDefaultDllImportSearchPathsAttribute();
+ if (hasDllImportSearchPathFlags)
+ {
+ dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
+ if (pMD->DllImportSearchAssemblyDirectory())
+ dllImportSearchPathFlags |= DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
+ }
+
+ Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
+
+ GCX_COOP();
+
+ struct {
+ STRINGREF libNameRef;
+ OBJECTREF assemblyRef;
+ } gc = { NULL, NULL };
+
+ GCPROTECT_BEGIN(gc);
+
+ gc.libNameRef = StringObject::NewString(wszLibName);
+ gc.assemblyRef = pAssembly->GetExposedObject();
+
+ PREPARE_NONVIRTUAL_CALLSITE(METHOD__MARSHAL__LOADLIBRARYCALLBACKSTUB);
+ DECLARE_ARGHOLDER_ARRAY(args, 4);
+ args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.libNameRef);
+ args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.assemblyRef);
+ args[ARGNUM_2] = BOOL_TO_ARGHOLDER(hasDllImportSearchPathFlags);
+ args[ARGNUM_3] = DWORD_TO_ARGHOLDER(dllImportSearchPathFlags);
+
+ // Make the call
+ CALL_MANAGED_METHOD(handle, NATIVE_LIBRARY_HANDLE, args);
+ GCPROTECT_END();
+
+ return handle;
+}
+
// Try to load the module alongside the assembly where the PInvoke was declared.
NATIVE_LIBRARY_HANDLE NDirect::LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
{
@@ -6372,11 +6426,12 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadFromPInvokeAssemblyDirectory(Assembly *pAssem
}
// Try to load the module from the native DLL search directories
-NATIVE_LIBRARY_HANDLE NDirect::LoadFromNativeDllSearchDirectories(AppDomain* pDomain, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
+NATIVE_LIBRARY_HANDLE NDirect::LoadFromNativeDllSearchDirectories(LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
{
STANDARD_VM_CONTRACT;
NATIVE_LIBRARY_HANDLE hmod = NULL;
+ AppDomain* pDomain = GetAppDomain();
if (pDomain->HasNativeDllSearchDirectories())
{
@@ -6498,7 +6553,7 @@ static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* num
// Search for the library and variants of its name in probing directories.
//static
NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssembly,
- BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlag,
+ BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlags,
LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
@@ -6508,7 +6563,7 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssemb
#if defined(FEATURE_CORESYSTEM) && !defined(PLATFORM_UNIX)
// Try to go straight to System32 for Windows API sets. This is replicating quick check from
// the OS implementation of api sets.
- if (SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 || SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0)
+ if (IsWindowsAPISet(wszLibName))
{
hmod = LocalLoadLibraryHelper(wszLibName, LOAD_LIBRARY_SEARCH_SYSTEM32, pErrorTracker);
if (hmod != NULL)
@@ -6536,7 +6591,7 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssemb
currLibNameVariation.Printf(prefixSuffixCombinations[i], PLATFORM_SHARED_LIB_PREFIX_W, wszLibName, PLATFORM_SHARED_LIB_SUFFIX_W);
// NATIVE_DLL_SEARCH_DIRECTORIES set by host is considered well known path
- hmod = LoadFromNativeDllSearchDirectories(pDomain, currLibNameVariation, loadWithAlteredPathFlags, pErrorTracker);
+ hmod = LoadFromNativeDllSearchDirectories(currLibNameVariation, loadWithAlteredPathFlags, pErrorTracker);
if (hmod != NULL)
{
return hmod;
@@ -6545,11 +6600,11 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssemb
if (!libNameIsRelativePath)
{
DWORD flags = loadWithAlteredPathFlags;
- if ((dllImportSearchPathFlag & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0)
+ if ((dllImportSearchPathFlags & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0)
{
// LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags
// unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH.
- flags |= dllImportSearchPathFlag;
+ flags |= dllImportSearchPathFlags;
}
hmod = LocalLoadLibraryHelper(currLibNameVariation, flags, pErrorTracker);
@@ -6560,14 +6615,14 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssemb
}
else if ((callingAssembly != nullptr) && searchAssemblyDirectory)
{
- hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlag, pErrorTracker);
+ hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
if (hmod != NULL)
{
return hmod;
}
}
- hmod = LocalLoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlag, pErrorTracker);
+ hmod = LocalLoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, pErrorTracker);
if (hmod != NULL)
{
return hmod;
@@ -6597,7 +6652,7 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssemb
Assembly *pAssembly = spec.LoadAssembly(FILE_LOADED);
Module *pModule = pAssembly->FindModuleByName(szLibName);
- hmod = LocalLoadLibraryHelper(pModule->GetPath(), loadWithAlteredPathFlags | dllImportSearchPathFlag, pErrorTracker);
+ hmod = LocalLoadLibraryHelper(pModule->GetPath(), loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
}
}
@@ -6618,11 +6673,19 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke
if ( !name || !*name )
return NULL;
- ModuleHandleHolder hmod;
PREFIX_ASSUME( name != NULL );
MAKE_WIDEPTR_FROMUTF8( wszLibName, name );
+ ModuleHandleHolder hmod = LoadLibraryModuleViaCallback(pMD, wszLibName);
+ if (hmod != NULL)
+ {
+#ifdef FEATURE_PAL
+ hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
+#endif // FEATURE_PAL
+ return hmod.Extract();
+ }
+
AppDomain* pDomain = GetAppDomain();
// AssemblyLoadContext is not supported in AppX mode and thus,
@@ -6630,7 +6693,7 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke
// AppX mode.
if (!AppX::IsAppXProcess())
{
- hmod = LoadLibraryModuleViaHost(pMD, pDomain, wszLibName);
+ hmod = LoadLibraryModuleViaHost(pMD, wszLibName);
if (hmod != NULL)
{
#ifdef FEATURE_PAL
diff --git a/src/vm/dllimport.h b/src/vm/dllimport.h
index 2f25e59570e1..d0dc71f9256d 100644
--- a/src/vm/dllimport.h
+++ b/src/vm/dllimport.h
@@ -76,7 +76,7 @@ class NDirect
static LPVOID NDirectGetEntryPoint(NDirectMethodDesc *pMD, HINSTANCE hMod);
static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError);
static NATIVE_LIBRARY_HANDLE LoadLibraryByName(LPCWSTR name, Assembly *callingAssembly,
- BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag,
+ BOOL hasDllImportSearchPathFlags, DWORD dllImportSearchPathFlags,
BOOL throwOnError);
static HINSTANCE LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracker *pErrorTracker);
static void FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle);
@@ -122,11 +122,12 @@ class NDirect
private:
NDirect() {LIMITED_METHOD_CONTRACT;}; // prevent "new"'s on this class
- static NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(AppDomain* pDomain, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker);
+ static NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker);
static NATIVE_LIBRARY_HANDLE LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker);
- static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, const wchar_t* wszLibName);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, const wchar_t* wszLibName);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaCallback(NDirectMethodDesc * pMD, const wchar_t* wszLibName);
static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName);
- static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(Assembly *callingAssembly, BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlag, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(Assembly *callingAssembly, BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlags, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName);
#if !defined(FEATURE_PAL)
// Indicates if the OS supports the new secure LoadLibraryEx flags introduced in KB2533623
diff --git a/src/vm/interoputil.cpp b/src/vm/interoputil.cpp
index a21e68b27095..6854edd15773 100644
--- a/src/vm/interoputil.cpp
+++ b/src/vm/interoputil.cpp
@@ -898,8 +898,8 @@ void FillExceptionData(
//---------------------------------------------------------------------------
//returns true if pImport has DefaultDllImportSearchPathsAttribute
-//if true, also returns dllImportSearchPathFlag and searchAssemblyDirectory values.
-BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, mdToken token, DWORD * pDllImportSearchPathFlag)
+//if true, also returns dllImportSearchPathFlags and searchAssemblyDirectory values.
+BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, mdToken token, DWORD * pDllImportSearchPathFlags)
{
CONTRACTL
{
@@ -929,7 +929,7 @@ BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, md
args[0].InitEnum(SERIALIZATION_TYPE_U4, (ULONG)0);
ParseKnownCaArgs(ca, args, lengthof(args));
- *pDllImportSearchPathFlag = args[0].val.u4;
+ *pDllImportSearchPathFlags = args[0].val.u4;
return TRUE;
}
diff --git a/src/vm/interoputil.h b/src/vm/interoputil.h
index 91f6828c4950..770568a93671 100644
--- a/src/vm/interoputil.h
+++ b/src/vm/interoputil.h
@@ -137,7 +137,7 @@ void FillExceptionData(
//---------------------------------------------------------------------------
//returns true if pImport has DefaultDllImportSearchPathsAttribute
-//if true, also returns dllImportSearchPathFlag and searchAssemblyDirectory values.
+//if true, also returns dllImportSearchPathFlags and searchAssemblyDirectory values.
BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, mdToken token, DWORD * pDlImportSearchPathFlag);
//---------------------------------------------------------------------------
diff --git a/src/vm/metasig.h b/src/vm/metasig.h
index 154c91570cc2..6fd136d3ecf4 100644
--- a/src/vm/metasig.h
+++ b/src/vm/metasig.h
@@ -551,6 +551,7 @@ DEFINE_METASIG_T(IM(RefGuid_OutIntPtr_RetCustomQueryInterfaceResult, r(g(GUID))
#endif //FEATURE_COMINTEROP
DEFINE_METASIG_T(SM(IntPtr_AssemblyName_RetAssemblyBase, I C(ASSEMBLY_NAME), C(ASSEMBLYBASE)))
+DEFINE_METASIG_T(SM(Str_AssemblyBase_Bool_UInt_RetIntPtr, s C(ASSEMBLYBASE) F K, I))
// ThreadPool
DEFINE_METASIG(SM(Obj_Bool_RetVoid, j F, v))
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index 62205d39bc88..cd1b5ef210a1 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -486,7 +486,7 @@ DEFINE_METHOD(MARSHAL, GET_FUNCTION_POINTER_FOR_DELEGATE, GetFuncti
DEFINE_METHOD(MARSHAL, GET_DELEGATE_FOR_FUNCTION_POINTER, GetDelegateForFunctionPointer, SM_IntPtr_Type_RetDelegate)
DEFINE_METHOD(MARSHAL, ALLOC_CO_TASK_MEM, AllocCoTaskMem, SM_Int_RetIntPtr)
DEFINE_FIELD(MARSHAL, SYSTEM_MAX_DBCS_CHAR_SIZE, SystemMaxDBCSCharSize)
-
+DEFINE_METHOD(MARSHAL, LOADLIBRARYCALLBACKSTUB, LoadLibraryCallbackStub, SM_Str_AssemblyBase_Bool_UInt_RetIntPtr)
DEFINE_CLASS(MEMBER, Reflection, MemberInfo)
diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt
index 9784ea1c95ab..baf57c294262 100644
--- a/tests/src/Interop/CMakeLists.txt
+++ b/tests/src/Interop/CMakeLists.txt
@@ -56,6 +56,7 @@ add_subdirectory(StringMarshalling/VBByRefStr)
add_subdirectory(MarshalAPI/FunctionPointer)
add_subdirectory(MarshalAPI/IUnknown)
add_subdirectory(NativeLibrary)
+add_subdirectory(NativeLibraryResolveCallBack)
add_subdirectory(SizeConst)
add_subdirectory(DllImportAttribute/ExeFile)
add_subdirectory(DllImportAttribute/FileNameContainDot)
diff --git a/tests/src/Interop/NativeLibraryResolveCallBack/CMakeLists.txt b/tests/src/Interop/NativeLibraryResolveCallBack/CMakeLists.txt
new file mode 100644
index 000000000000..b3f3409e3e0f
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveCallBack/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required (VERSION 2.6)
+project (ResolveLib)
+include_directories(${INC_PLATFORM_DIR})
+set(SOURCES ResolveLib.cpp)
+
+# add the executable
+add_library (ResolveLib SHARED ${SOURCES})
+target_link_libraries(ResolveLib ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS ResolveLib DESTINATION bin)
+
+
diff --git a/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.cs b/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.cs
new file mode 100644
index 000000000000..22df7cfb9b38
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.cs
@@ -0,0 +1,81 @@
+// 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;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using TestLibrary;
+using DllImportUtil;
+
+using Console = Internal.Console;
+
+public class CallBackTests
+{
+ public static int Main()
+ {
+ try
+ {
+ Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
+ string testBinDir = Path.GetDirectoryName(assembly.Location);
+
+ DllImportResolver resolver =
+ (string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath) =>
+ NativeLibrary.LoadLibrary("ResolveLib", assembly, dllImportSearchPath);
+
+ DllImportResolver anotherResolver =
+ (string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath) =>
+ IntPtr.Zero;
+
+ try
+ {
+ NativeLibrary.SetDllImportResolver(null, resolver);
+
+ Console.WriteLine("Expected exception not thrown");
+ return 101;
+ }
+ catch (ArgumentNullException e){}
+
+ try
+ {
+ NativeLibrary.SetDllImportResolver(resolver, null);
+
+ Console.WriteLine("Expected exception not thrown");
+ return 102;
+ }
+ catch (ArgumentNullException e){}
+
+ // Set a resolver callback
+ NativeLibrary.SetDllImportResolver(assembly, resolver);
+
+ // Set the same resolver again.
+ NativeLibrary.SetDllImportResolver(assembly, resolver);
+
+ try
+ {
+ // Try to set another resolver on the same assembly.
+ NativeLibrary.SetDllImportResolver(assembly, anotherResolver);
+
+ Console.WriteLine("Expected Exception not thrown");
+ return 103;
+ }
+ catch (InvalidOperationException e){}
+
+ if (NativeSum(10, 10) != 20)
+ {
+ Console.WriteLine("Unexpected ReturnValue from NativeSum()");
+ return 104;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Unexpected exception: {e.Message}");
+ return 105;
+ }
+
+ return 100;
+ }
+
+ [DllImport("NativeLib")]
+ static extern int NativeSum(int arg1, int arg2);
+}
diff --git a/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.csproj b/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.csproj
new file mode 100644
index 000000000000..de2de5a9b85a
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.csproj
@@ -0,0 +1,39 @@
+
+
+
+
+ Debug
+ AnyCPU
+ CallBackTests
+ 2.0
+ {F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}
+ Exe
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ ..\..\
+ true
+ true
+
+
+
+
+
+
+
+
+ False
+
+
+
+
+
+
+
+
+
+
+ {c8c0dc74-fac4-45b1-81fe-70c4808366e0}
+ CoreCLRTestLibrary
+
+
+
+
diff --git a/tests/src/Interop/NativeLibraryResolveCallBack/ResolveLib.cpp b/tests/src/Interop/NativeLibraryResolveCallBack/ResolveLib.cpp
new file mode 100644
index 000000000000..bdf5beb4247a
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveCallBack/ResolveLib.cpp
@@ -0,0 +1,11 @@
+// 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.
+#include
+#include
+
+extern "C" DLL_EXPORT int NativeSum(int a, int b)
+{
+ return a + b;
+}
+