From ea092251fc98aaea3f188a985b04bd243d309a9a Mon Sep 17 00:00:00 2001 From: swaroop-sridhar Date: Wed, 21 Nov 2018 12:22:19 -0800 Subject: [PATCH] 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 public static bool RegisterDllImportResolver( Assembly assembly, Func callback ); This API is not yet approved, and the API contracts in CoreFX will not be added until the API approval is complete. In the meantime, we want to have the code reviewed, tested, and avaiable in CoreCLR. --- .../System/Runtime/InteropServices/Marshal.cs | 82 +++++- src/vm/callhelpers.h | 1 + src/vm/dllimport.cpp | 124 +++++++-- src/vm/dllimport.h | 9 +- src/vm/interoputil.cpp | 6 +- src/vm/interoputil.h | 2 +- src/vm/marshalnative.cpp | 4 +- src/vm/marshalnative.h | 2 +- src/vm/metasig.h | 1 + src/vm/mscorlib.h | 2 +- tests/src/Interop/CMakeLists.txt | 1 + .../MarshalAPI/DllImportUtil/CMakeLists.txt | 13 + .../MarshalAPI/DllImportUtil/DllImportUtil.cs | 110 ++++++++ .../DllImportUtil/DllImportUtil.csproj | 47 ++++ .../DllImportUtil/NativeLibrary.cpp | 11 + .../MarshalAPI/DllResolve/DllResolveTests.cs | 56 ++++ .../DllResolve/DllResolveTests.csproj | 42 +++ .../NativeLibrary/NativeLibraryTests.cs | 243 +++++------------- .../NativeLibrary/NativeLibraryTests.csproj | 3 + 19 files changed, 546 insertions(+), 213 deletions(-) create mode 100644 tests/src/Interop/MarshalAPI/DllImportUtil/CMakeLists.txt create mode 100644 tests/src/Interop/MarshalAPI/DllImportUtil/DllImportUtil.cs create mode 100644 tests/src/Interop/MarshalAPI/DllImportUtil/DllImportUtil.csproj create mode 100644 tests/src/Interop/MarshalAPI/DllImportUtil/NativeLibrary.cpp create mode 100644 tests/src/Interop/MarshalAPI/DllResolve/DllResolveTests.cs create mode 100644 tests/src/Interop/MarshalAPI/DllResolve/DllResolveTests.csproj diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index eeef8ebdfe92..f32af7881bb1 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -32,6 +32,11 @@ public static partial class Marshal internal static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046"); #endif //FEATURE_COMINTEROP + static Marshal() + { + nativeDllResolveMap = new ConditionalWeakTable>(); + } + private const int LMEM_FIXED = 0; private const int LMEM_MOVEABLE = 2; #if !FEATURE_PAL @@ -1805,6 +1810,7 @@ public static bool TryLoadLibrary(string libraryPath, out IntPtr handle) /// calling assembly (if any) are used. /// This LoadLibrary() method does not invoke the managed call-backs for native library resolution: /// * AssemblyLoadContext.LoadUnmanagedDll() + /// * The per-assembly registered callback /// /// The name of the native library to be loaded /// The assembly loading the native library @@ -1907,13 +1913,87 @@ public static bool TryGetLibraryExport(IntPtr handle, string name, out IntPtr ad 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> nativeDllResolveMap; + + /// + /// Register 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 callback can be registered per assembly. Trying to register a second + /// callback fails (with the return value false). + /// + /// The callback method itself takes the following parameters + /// - The name of the library to be loaded (string) + /// - The assembly initiating the native library load + /// - The DllImportSearchPath flags from the attributes on the PInvoke, if any (null otherwise). + /// This argument doesn't include the flags from any attributes on the assembly itself. + /// and returns + /// - The handle to the loaded native library (on success) or null (on failure) + /// The parameters on this callback are such that they can be directly passed to + /// Marhall.LoadLibrary(libraryName, assembly, dllImportSearchPath) to approximately achieve + /// the default load behavior. + /// + /// + /// The assembly for which the callback is registered + /// The callback to register + /// If assembly or callback is null + /// True on success, false otherwise + public static bool RegisterDllImportResolver(Assembly assembly, Func callBack) + { + if (assembly == null) + throw new ArgumentNullException(nameof(assembly)); + if (callBack == null) + throw new ArgumentNullException(nameof(callBack)); + if (!(assembly is RuntimeAssembly)) + throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); + + Func existingCallback; + if (nativeDllResolveMap.TryGetValue(assembly, out existingCallback)) + { + return false; + } + + nativeDllResolveMap.Add(assembly, callBack); + return true; + } + + /// + /// 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) + { + Func callBack; + + if (!nativeDllResolveMap.TryGetValue(assembly, out callBack)) + { + return IntPtr.Zero; + } + + return callBack(libraryName, assembly, hasDllImportSearchPathFlags? (DllImportSearchPath?)dllImportSearchPathFlags : null); + } + /// External functions that implement the NativeLibrary interface [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern IntPtr LoadLibraryFromPath(string libraryName, bool throwOnError); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern IntPtr LoadLibraryByName(string libraryName, RuntimeAssembly callingAssembly, - bool hasDllImportSearchPathFlag, uint dllImportSearchPathFlag, + bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags, bool throwOnError); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern void FreeNativeLibrary(IntPtr handle); diff --git a/src/vm/callhelpers.h b/src/vm/callhelpers.h index bcd553dcc898..6cd2e44bd6b2 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 9dda48f3ee13..ba2b4b857c9f 100644 --- a/src/vm/dllimport.cpp +++ b/src/vm/dllimport.cpp @@ -6157,7 +6157,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 @@ -6170,15 +6170,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 @@ -6187,13 +6187,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)) { @@ -6212,11 +6212,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 @@ -6225,13 +6225,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 @@ -6280,23 +6280,32 @@ INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR sy return address; } +#ifndef PLATFORM_UNIX +BOOL IsWindowsAPI(PCWSTR wszLibName) +{ + // 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 (IsWindowsAPI(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(); @@ -6362,6 +6371,55 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, return (NATIVE_LIBRARY_HANDLE)hmod; } +NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaCallBack(NDirectMethodDesc * pMD, LPCWSTR wszLibName) +{ +#ifndef PLATFORM_UNIX + if (IsWindowsAPI(wszLibName)) + { + // Prevent Overriding of Windows API sets. + return NULL; + } +#endif // !PLATFORM_UNIX + + DWORD dllImportSearchPathFlags = 0; + BOOL hasDllImportSearchPathFlags = pMD->HasDefaultDllImportSearchPathsAttribute(); + if (hasDllImportSearchPathFlags) + { + dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue(); + if (pMD->DllImportSearchAssemblyDirectory()) + dllImportSearchPathFlags |= DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY; + } + + GCX_COOP(); + + struct { + STRINGREF libNameRef; + OBJECTREF assemblyRef; + } protect; + + + Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); + protect.libNameRef = StringObject::NewString(wszLibName); + protect.assemblyRef = pAssembly->GetExposedObject(); + + NATIVE_LIBRARY_HANDLE handle = NULL; + + GCPROTECT_BEGIN(protect); + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__MARSHAL__LOADLIBRARYCALLBACKSTUB); + DECLARE_ARGHOLDER_ARRAY(args, 4); + args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(protect.libNameRef); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(protect.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) { @@ -6385,11 +6443,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()) { @@ -6511,7 +6570,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; @@ -6521,7 +6580,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 (IsWindowsAPI(wszLibName)) { hmod = LocalLoadLibraryHelper(wszLibName, LOAD_LIBRARY_SEARCH_SYSTEM32, pErrorTracker); if (hmod != NULL) @@ -6549,7 +6608,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; @@ -6558,11 +6617,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); @@ -6573,14 +6632,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; @@ -6610,7 +6669,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); } } @@ -6631,11 +6690,20 @@ 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 + // Register the system library handle with PAL and get a PAL library handle + hmod = PAL_RegisterLibraryDirect(hmod, wszLibName); +#endif // FEATURE_PAL + return hmod.Extract(); + } + AppDomain* pDomain = GetAppDomain(); // AssemblyLoadContext is not supported in AppX mode and thus, @@ -6643,7 +6711,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..93c42a78a3b5 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/marshalnative.cpp b/src/vm/marshalnative.cpp index 7614ddd6a0d1..8b77f5c652c7 100644 --- a/src/vm/marshalnative.cpp +++ b/src/vm/marshalnative.cpp @@ -941,7 +941,7 @@ INT_PTR QCALLTYPE MarshalNative::LoadLibraryFromPath(LPCWSTR path, BOOL throwOnE // static INT_PTR QCALLTYPE MarshalNative::LoadLibraryByName(LPCWSTR name, QCall::AssemblyHandle callingAssembly, - BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag, + BOOL hasDllImportSearchPathFlags, DWORD dllImportSearchPathFlags, BOOL throwOnError) { QCALL_CONTRACT; @@ -951,7 +951,7 @@ INT_PTR QCALLTYPE MarshalNative::LoadLibraryByName(LPCWSTR name, QCall::Assembly BEGIN_QCALL; - handle = NDirect::LoadLibraryByName(name, pAssembly, hasDllImportSearchPathFlag, dllImportSearchPathFlag, throwOnError); + handle = NDirect::LoadLibraryByName(name, pAssembly, hasDllImportSearchPathFlags, dllImportSearchPathFlags, throwOnError); END_QCALL; diff --git a/src/vm/marshalnative.h b/src/vm/marshalnative.h index ddc835109402..f4fef93599b8 100644 --- a/src/vm/marshalnative.h +++ b/src/vm/marshalnative.h @@ -91,7 +91,7 @@ class MarshalNative //==================================================================== static INT_PTR QCALLTYPE LoadLibraryFromPath(LPCWSTR path, BOOL throwOnError); static INT_PTR QCALLTYPE LoadLibraryByName(LPCWSTR name, QCall::AssemblyHandle callingAssembly, - BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag, + BOOL hasDllImportSearchPathFlags, DWORD dllImportSearchPathFlags, BOOL throwOnError); static void QCALLTYPE FreeNativeLibrary(INT_PTR handle); static INT_PTR QCALLTYPE GetNativeLibraryExport(INT_PTR handle, LPCWSTR symbolName, BOOL throwOnError); diff --git a/src/vm/metasig.h b/src/vm/metasig.h index 0897afcff003..4e2416edb3f6 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 f257ecd2f349..c2d114174f1b 100644 --- a/src/vm/mscorlib.h +++ b/src/vm/mscorlib.h @@ -478,7 +478,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 5685e79762c6..e9d35d38e3a1 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(MarshalAPI/NativeLibrary) +add_subdirectory(MarshalAPI/DllImportUtil) add_subdirectory(SizeConst) add_subdirectory(DllImportAttribute/ExeFile) add_subdirectory(DllImportAttribute/FileNameContainDot) diff --git a/tests/src/Interop/MarshalAPI/DllImportUtil/CMakeLists.txt b/tests/src/Interop/MarshalAPI/DllImportUtil/CMakeLists.txt new file mode 100644 index 000000000000..93b6277e579f --- /dev/null +++ b/tests/src/Interop/MarshalAPI/DllImportUtil/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required (VERSION 2.6) +project (NativeLib) +include_directories(${INC_PLATFORM_DIR}) +set(SOURCES NativeLibrary.cpp) + +# add the executable +add_library (NativeLib SHARED ${SOURCES}) +target_link_libraries(NativeLib ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS NativeLib DESTINATION bin) + + diff --git a/tests/src/Interop/MarshalAPI/DllImportUtil/DllImportUtil.cs b/tests/src/Interop/MarshalAPI/DllImportUtil/DllImportUtil.cs new file mode 100644 index 000000000000..08bfc4ca4b77 --- /dev/null +++ b/tests/src/Interop/MarshalAPI/DllImportUtil/DllImportUtil.cs @@ -0,0 +1,110 @@ +// 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 Console = Internal.Console; + +namespace DllImportUtil +{ + public enum TestResult { + Success, + ReturnFailure, + ReturnNull, + IncorrectEvaluation, + ArgumentNull, + ArgumentBad, + + DllNotFound, + BadImage, + InvalidOperation, + EntryPointNotFound, + GenericException + }; + + public class Test + { + public bool EXPECT(TestResult actualValue, TestResult expectedValue = TestResult.Success) + { + if (actualValue == expectedValue) + { + if (Verbose) + Console.WriteLine(String.Format("{0} : {1} : [OK]", CurrentTest, actualValue)); + return true; + } + else + { + Console.WriteLine(String.Format(" {0} : {1} : [FAIL]", CurrentTest, actualValue)); + return false; + } + } + public TestResult Run (Func test) + { + TestResult result; + + try + { + result = test(); + } + catch (ArgumentNullException e) + { + return TestResult.ArgumentNull; + } + catch (ArgumentException e) + { + return TestResult.ArgumentBad; + } + catch (DllNotFoundException e) + { + return TestResult.DllNotFound; + } + catch (BadImageFormatException e) + { + return TestResult.BadImage; + } + catch (InvalidOperationException e) + { + return TestResult.InvalidOperation; + } + catch (EntryPointNotFoundException e) + { + return TestResult.EntryPointNotFound; + } + catch (Exception e) + { + return TestResult.GenericException; + } + + return result; + } + + public static string GetDecoratedName(string baseName) + { + if (TestLibrary.Utilities.IsWindows) + { + return baseName + ".dll"; + } + if (TestLibrary.Utilities.IsLinux) + { + return "lib" + baseName + ".so"; + } + if (TestLibrary.Utilities.IsMacOSX) + { + return "lib" + baseName + ".dylib"; + } + + return "ERROR"; + } + public bool Verbose; + public string CurrentTest; + public string LocalLibPlainName; + public string LocalLibName => GetDecoratedName(LocalLibPlainName); + public string Win32LibName => "msi.dll"; + public static string CommonLibPlainName => "NativeLib"; + public string CommonLibName => GetDecoratedName(CommonLibPlainName); + } +} \ No newline at end of file diff --git a/tests/src/Interop/MarshalAPI/DllImportUtil/DllImportUtil.csproj b/tests/src/Interop/MarshalAPI/DllImportUtil/DllImportUtil.csproj new file mode 100644 index 000000000000..65632b71c8b5 --- /dev/null +++ b/tests/src/Interop/MarshalAPI/DllImportUtil/DllImportUtil.csproj @@ -0,0 +1,47 @@ + + + + + Debug + AnyCPU + DllImportUtil + 2.0 + {C8C0DC74-FAC4-45B1-81FE-70C4808366E1} + Library + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + true + true + false + + + + + + + + + False + + + + + + + + + + + PreserveNewest + + + + + {c8c0dc74-fac4-45b1-81fe-70c4808366e0} + CoreCLRTestLibrary + + + + + diff --git a/tests/src/Interop/MarshalAPI/DllImportUtil/NativeLibrary.cpp b/tests/src/Interop/MarshalAPI/DllImportUtil/NativeLibrary.cpp new file mode 100644 index 000000000000..bdf5beb4247a --- /dev/null +++ b/tests/src/Interop/MarshalAPI/DllImportUtil/NativeLibrary.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; +} + diff --git a/tests/src/Interop/MarshalAPI/DllResolve/DllResolveTests.cs b/tests/src/Interop/MarshalAPI/DllResolve/DllResolveTests.cs new file mode 100644 index 000000000000..bb6cef094738 --- /dev/null +++ b/tests/src/Interop/MarshalAPI/DllResolve/DllResolveTests.cs @@ -0,0 +1,56 @@ +// 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 NativeLibraryTest +{ + static Test test; + + public static int Main() + { + bool success = true; + + Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly(); + string testBinDir = Path.GetDirectoryName(assembly.Location); + + success &= test.EXPECT(RegisterResolver(null, LoadLibraryCallBack), TestResult.ArgumentNull); + success &= test.EXPECT(RegisterResolver(assembly, null), TestResult.ArgumentNull); + success &= test.EXPECT(RegisterResolver(assembly, LoadLibraryCallBack)); + success &= test.EXPECT(RegisterResolver(assembly, LoadLibraryCallBack), TestResult.ReturnFailure); + + success &= NativeSum(10, 10) == 20; + + return (success) ? 100 : -100; + } + public static IntPtr LoadLibraryCallBack(string libraryName, Assembly assembly, + DllImportSearchPath? dllImportSearchPath) + { + return Marshal.LoadLibrary(libraryName, assembly, dllImportSearchPath); + } + + static TestResult RegisterResolver(Assembly assembly, Func callBack) + { + test.CurrentTest = String.Format("RegisterDllImportResolver({0}, {1})", assembly.FullName, callBack); + + TestResult result = test.Run(() => { + bool success = Marshal.RegisterDllImportResolver(assembly, callBack); + if (!success) + return TestResult.ReturnFailure; + return TestResult.Success; + }); + + return result; + } + + + [DllImport("NativeLib")] + static extern int NativeSum(int arg1, int arg2); +} diff --git a/tests/src/Interop/MarshalAPI/DllResolve/DllResolveTests.csproj b/tests/src/Interop/MarshalAPI/DllResolve/DllResolveTests.csproj new file mode 100644 index 000000000000..edc20c0f6faf --- /dev/null +++ b/tests/src/Interop/MarshalAPI/DllResolve/DllResolveTests.csproj @@ -0,0 +1,42 @@ + + + + + Debug + AnyCPU + DllResolveTests + 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 + + + DllImportUtil + + + + diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs index dfc7335b3024..4739a7c566cd 100644 --- a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs +++ b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs @@ -6,29 +6,14 @@ using System.Reflection; using System.Runtime.InteropServices; using TestLibrary; +using DllImportUtil; using Console = Internal.Console; -enum TestResult { - Success, - ReturnFailure, - ReturnNull, - IncorrectEvaluation, - ArgumentNull, - ArgumentBad, - - DllNotFound, - BadImage, - InvalidOperation, - EntryPointNotFound, - GenericException - }; - public class NativeLibraryTest { - static string CurrentTest; - static bool Verbose = false; - + static Test test; + public static int Main() { bool success = true; @@ -37,31 +22,32 @@ public static int Main() string testBinDir = Path.GetDirectoryName(assembly.Location); string libName; IntPtr handle; + test.LocalLibPlainName = "NativeLibrary"; // ----------------------------------------------- // Simple LoadLibrary() API Tests // ----------------------------------------------- // Calls on correct full-path to native lib - libName = Path.Combine(testBinDir, GetNativeLibraryName()); - success &= EXPECT(LoadLibrarySimple(libName)); - success &= EXPECT(TryLoadLibrarySimple(libName)); + libName = Path.Combine(testBinDir, test.LocalLibName); + success &= test.EXPECT(LoadLibrarySimple(libName)); + success &= test.EXPECT(TryLoadLibrarySimple(libName)); // Calls on non-existant file libName = Path.Combine(testBinDir, "notfound"); - success &= EXPECT(LoadLibrarySimple(libName), TestResult.DllNotFound); - success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ReturnFailure); + success &= test.EXPECT(LoadLibrarySimple(libName), TestResult.DllNotFound); + success &= test.EXPECT(TryLoadLibrarySimple(libName), TestResult.ReturnFailure); // Calls on an invalid file libName = Path.Combine(testBinDir, "NativeLibrary.cpp"); - success &= EXPECT(LoadLibrarySimple(libName), + success &= test.EXPECT(LoadLibrarySimple(libName), (TestLibrary.Utilities.IsWindows) ? TestResult.BadImage : TestResult.DllNotFound); - success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ReturnFailure); + success &= test.EXPECT(TryLoadLibrarySimple(libName), TestResult.ReturnFailure); // Calls on null input libName = null; - success &= EXPECT(LoadLibrarySimple(libName), TestResult.ArgumentNull); - success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ArgumentNull); + success &= test.EXPECT(LoadLibrarySimple(libName), TestResult.ArgumentNull); + success &= test.EXPECT(TryLoadLibrarySimple(libName), TestResult.ArgumentNull); // ----------------------------------------------- // Advanced LoadLibrary() API Tests @@ -69,206 +55,119 @@ public static int Main() // Advanced LoadLibrary() API Tests // Calls on correct full-path to native lib - libName = Path.Combine(testBinDir, GetNativeLibraryName()); - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null)); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); + libName = Path.Combine(testBinDir, test.LocalLibName); + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, null)); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); // Calls on non-existant file libName = Path.Combine(testBinDir, "notfound"); - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null), TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), TestResult.ReturnFailure); + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, null), TestResult.DllNotFound); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), TestResult.ReturnFailure); // Calls on an invalid file libName = Path.Combine(testBinDir, "NativeLibrary.cpp"); // The VM can only distinguish BadImageFormatException from DllNotFoundException on Windows. - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null), + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, null), (TestLibrary.Utilities.IsWindows) ? TestResult.BadImage : TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), TestResult.ReturnFailure); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), TestResult.ReturnFailure); // Calls on just Native Library name - libName = GetNativeLibraryPlainName(); - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null)); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); + libName = test.LocalLibPlainName; + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, null)); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); // Calls on Native Library name with correct prefix-suffix - libName = GetNativeLibraryName(); - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null)); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); + libName = test.LocalLibName; + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, null)); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); // Calls on full path without prefix-siffix - libName = Path.Combine(testBinDir, GetNativeLibraryPlainName()); + libName = Path.Combine(testBinDir, test.LocalLibPlainName); // DllImport doesn't add a prefix if the name is preceeded by a path specification. // Windows only needs a suffix, but Linux and Mac need both prefix and suffix - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null), + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, null), (TestLibrary.Utilities.IsWindows) ? TestResult.Success : TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), (TestLibrary.Utilities.IsWindows) ? TestResult.Success : TestResult.ReturnFailure); if (TestLibrary.Utilities.IsWindows) { - libName = GetWin32LibName(); + libName = test.Win32LibName; // Calls on a valid library from System32 directory - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.System32)); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.System32)); + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.System32)); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.System32)); // Calls on a valid library from application directory - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.ReturnFailure); + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.DllNotFound); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.ReturnFailure); } // Calls with null libName input - success &= EXPECT(LoadLibraryAdvanced(null, assembly, null), TestResult.ArgumentNull); - success &= EXPECT(TryLoadLibraryAdvanced(null, assembly, null), TestResult.ArgumentNull); + success &= test.EXPECT(LoadLibraryAdvanced(null, assembly, null), TestResult.ArgumentNull); + success &= test.EXPECT(TryLoadLibraryAdvanced(null, assembly, null), TestResult.ArgumentNull); // Calls with null assembly - libName = GetNativeLibraryPlainName(); - success &= EXPECT(LoadLibraryAdvanced(libName, null, null), TestResult.ArgumentNull); - success &= EXPECT(TryLoadLibraryAdvanced(libName, null, null), TestResult.ArgumentNull); + libName = test.LocalLibPlainName; + success &= test.EXPECT(LoadLibraryAdvanced(libName, null, null), TestResult.ArgumentNull); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, null, null), TestResult.ArgumentNull); // Ensure that a lib is not picked up from current directory when // a different full-path is specified. - libName = Path.Combine(testBinDir, Path.Combine("lib", GetNativeLibraryPlainName())); - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.ReturnFailure); + libName = Path.Combine(testBinDir, Path.Combine("lib", test.LocalLibPlainName)); + success &= test.EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.DllNotFound); + success &= test.EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.ReturnFailure); // ----------------------------------------------- // FreeLibrary Tests // ----------------------------------------------- - libName = Path.Combine(testBinDir, GetNativeLibraryName()); - handle = Marshal.LoadLibrary(libName); + libName = Path.Combine(testBinDir, test.LocalLibName); + Marshal.TryLoadLibrary(libName, out handle); // Valid Free - success &= EXPECT(FreeLibrary(handle)); + success &= test.EXPECT(FreeLibrary(handle)); // Double Free - success &= EXPECT(FreeLibrary(handle), TestResult.InvalidOperation); + success &= test.EXPECT(FreeLibrary(handle), TestResult.InvalidOperation); // Null Free - success &= EXPECT(FreeLibrary(IntPtr.Zero)); + success &= test.EXPECT(FreeLibrary(IntPtr.Zero)); // ----------------------------------------------- // GetLibraryExport Tests // ----------------------------------------------- - libName = Path.Combine(testBinDir, GetNativeLibraryName()); - handle = Marshal.LoadLibrary(libName); + libName = Path.Combine(testBinDir, test.LocalLibName); + Marshal.TryLoadLibrary(libName, out handle); // Valid Call (with some hard-coded name mangling) - success &= EXPECT(GetLibraryExport(handle, TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum")); - success &= EXPECT(TryGetLibraryExport(handle, TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum")); + success &= test.EXPECT(GetLibraryExport(handle, TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum")); + success &= test.EXPECT(TryGetLibraryExport(handle, TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum")); // Call with null handle - success &= EXPECT(GetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); - success &= EXPECT(TryGetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); + success &= test.EXPECT(GetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); + success &= test.EXPECT(TryGetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); // Call with null string - success &= EXPECT(GetLibraryExport(handle, null), TestResult.ArgumentNull); - success &= EXPECT(TryGetLibraryExport(handle, null), TestResult.ArgumentNull); + success &= test.EXPECT(GetLibraryExport(handle, null), TestResult.ArgumentNull); + success &= test.EXPECT(TryGetLibraryExport(handle, null), TestResult.ArgumentNull); // Call with wrong string - success &= EXPECT(GetLibraryExport(handle, "NonNativeSum"), TestResult.EntryPointNotFound); - success &= EXPECT(TryGetLibraryExport(handle, "NonNativeSum"), TestResult.ReturnFailure); + success &= test.EXPECT(GetLibraryExport(handle, "NonNativeSum"), TestResult.EntryPointNotFound); + success &= test.EXPECT(TryGetLibraryExport(handle, "NonNativeSum"), TestResult.ReturnFailure); Marshal.FreeLibrary(handle); return (success) ? 100 : -100; } - static string GetNativeLibraryPlainName() - { - return "NativeLibrary"; - } - - static string GetWin32LibName() - { - return "msi.dll"; - } - - static string GetNativeLibraryName() - { - string baseName = GetNativeLibraryPlainName(); - - if (TestLibrary.Utilities.IsWindows) - { - return baseName + ".dll"; - } - if (TestLibrary.Utilities.IsLinux) - { - return "lib" + baseName + ".so"; - } - if (TestLibrary.Utilities.IsMacOSX) - { - return "lib" + baseName + ".dylib"; - } - - return "ERROR"; - } - - static bool EXPECT(TestResult actualValue, TestResult expectedValue = TestResult.Success) - { - if (actualValue == expectedValue) - { - if (Verbose) - Console.WriteLine(String.Format("{0} : {1} : [OK]", CurrentTest, actualValue)); - return true; - } - else - { - Console.WriteLine(String.Format(" {0} : {1} : [FAIL]", CurrentTest, actualValue)); - return false; - } - } - - static TestResult Run (Func test) - { - - TestResult result; - - try - { - result = test(); - } - catch (ArgumentNullException e) - { - return TestResult.ArgumentNull; - } - catch (ArgumentException e) - { - return TestResult.ArgumentBad; - } - catch (DllNotFoundException e) - { - return TestResult.DllNotFound; - } - catch (BadImageFormatException e) - { - return TestResult.BadImage; - } - catch (InvalidOperationException e) - { - return TestResult.InvalidOperation; - } - catch (EntryPointNotFoundException e) - { - return TestResult.EntryPointNotFound; - } - catch (Exception e) - { - //Console.WriteLine(e.ToString()); - return TestResult.GenericException; - } - - return result; - } - static TestResult LoadLibrarySimple(string libPath) { - CurrentTest = String.Format("LoadLibrary({0})", libPath); + test.CurrentTest = String.Format("LoadLibrary({0})", libPath); IntPtr handle = IntPtr.Zero; - TestResult result = Run(() => { + TestResult result = test.Run(() => { handle = Marshal.LoadLibrary(libPath); if (handle == IntPtr.Zero) return TestResult.ReturnNull; @@ -282,11 +181,11 @@ static TestResult LoadLibrarySimple(string libPath) static TestResult TryLoadLibrarySimple(string libPath) { - CurrentTest = String.Format("TryLoadLibrary({0})", libPath); + test.CurrentTest = String.Format("TryLoadLibrary({0})", libPath); IntPtr handle = IntPtr.Zero; - TestResult result = Run(() => { + TestResult result = test.Run(() => { bool success = Marshal.TryLoadLibrary(libPath, out handle); if(!success) return TestResult.ReturnFailure; @@ -303,11 +202,11 @@ static TestResult TryLoadLibrarySimple(string libPath) static TestResult LoadLibraryAdvanced(string libName, Assembly assembly, DllImportSearchPath? searchPath) { - CurrentTest = String.Format("LoadLibrary({0}, {1}, {2})", libName, assembly, searchPath); + test.CurrentTest = String.Format("LoadLibrary({0}, {1}, {2})", libName, assembly, searchPath); IntPtr handle = IntPtr.Zero; - TestResult result = Run(() => { + TestResult result = test.Run(() => { handle = Marshal.LoadLibrary(libName, assembly, searchPath); if (handle == IntPtr.Zero) return TestResult.ReturnNull; @@ -321,11 +220,11 @@ static TestResult LoadLibraryAdvanced(string libName, Assembly assembly, DllImpo static TestResult TryLoadLibraryAdvanced(string libName, Assembly assembly, DllImportSearchPath? searchPath) { - CurrentTest = String.Format("TryLoadLibrary({0}, {1}, {2})", libName, assembly, searchPath); + test.CurrentTest = String.Format("TryLoadLibrary({0}, {1}, {2})", libName, assembly, searchPath); IntPtr handle = IntPtr.Zero; - TestResult result = Run(() => { + TestResult result = test.Run(() => { bool success = Marshal.TryLoadLibrary(libName, assembly, searchPath, out handle); if (!success) return TestResult.ReturnFailure; @@ -341,9 +240,9 @@ static TestResult TryLoadLibraryAdvanced(string libName, Assembly assembly, DllI static TestResult FreeLibrary(IntPtr handle) { - CurrentTest = String.Format("FreeLibrary({0})", handle); + test.CurrentTest = String.Format("FreeLibrary({0})", handle); - return Run(() => { + return test.Run(() => { Marshal.FreeLibrary(handle); return TestResult.Success; }); @@ -351,9 +250,9 @@ static TestResult FreeLibrary(IntPtr handle) static TestResult GetLibraryExport(IntPtr handle, string name) { - CurrentTest = String.Format("GetLibraryExport({0}, {1})", handle, name); + test.CurrentTest = String.Format("GetLibraryExport({0}, {1})", handle, name); - return Run(() => { + return test.Run(() => { IntPtr address = Marshal.GetLibraryExport(handle, name); if (address == null) return TestResult.ReturnNull; @@ -365,9 +264,9 @@ static TestResult GetLibraryExport(IntPtr handle, string name) static TestResult TryGetLibraryExport(IntPtr handle, string name) { - CurrentTest = String.Format("TryGetLibraryExport({0}, {1})", handle, name); + test.CurrentTest = String.Format("TryGetLibraryExport({0}, {1})", handle, name); - return Run(() => { + return test.Run(() => { IntPtr address = IntPtr.Zero; bool success = Marshal.TryGetLibraryExport(handle, name, out address); if (!success) diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj index d3d7eb2be78c..d7958024f685 100644 --- a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj +++ b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj @@ -39,6 +39,9 @@ {c8c0dc74-fac4-45b1-81fe-70c4808366e0} CoreCLRTestLibrary + + DllImportUtil +