From 175ba1c0794958bb7d006544b87e00675de742fc Mon Sep 17 00:00:00 2001 From: Swaroop Sridhar Date: Wed, 21 Nov 2018 11:42:01 -0800 Subject: [PATCH] Introduce Marshall.LoadLibrary API (#20871) Implement Native LoadLibrary API This change commits the following changes: 1) Refactoring DllImport code to reuse LodLibrary by search for pInvoke and LoadLibrary cases 2) Implement the new Native Library API in System.Runtime.Interop.Marshall 3) Add tests for the new APIs --- .../src/System/Runtime/InteropServices/Marshal.cs | 128 +++++++ src/dlls/mscorrc/mscorrc.rc | 2 + src/dlls/mscorrc/resource.h | 5 + src/pal/inc/pal.h | 19 +- src/pal/src/include/pal/module.h | 14 +- src/pal/src/loader/module.cpp | 55 ++- src/vm/assemblynative.cpp | 2 +- src/vm/dllimport.cpp | 190 ++++++++--- src/vm/dllimport.h | 12 +- src/vm/ecalllist.h | 6 + src/vm/marshalnative.cpp | 62 ++++ src/vm/marshalnative.h | 11 + tests/src/Common/CoreCLRTestLibrary/Utilities.cs | 11 +- tests/src/Interop/CMakeLists.txt | 1 + .../MarshalAPI/NativeLibrary/CMakeLists.txt | 13 + .../MarshalAPI/NativeLibrary/NativeLibrary.cpp | 18 + .../MarshalAPI/NativeLibrary/NativeLibraryTests.cs | 379 +++++++++++++++++++++ .../NativeLibrary/NativeLibraryTests.csproj | 45 +++ 18 files changed, 911 insertions(+), 62 deletions(-) create mode 100644 tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt create mode 100644 tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp create mode 100644 tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs create mode 100644 tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.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 b4f4dfa..e6a2937 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -1760,5 +1760,133 @@ namespace System.Runtime.InteropServices RuntimeImports.RhZeroMemory(s, (UIntPtr)(Win32Native.lstrlenW(s) * 2)); FreeHGlobal(s); } + + /// APIs for managing Native Libraries + + /// + /// NativeLibrary Loader: Simple API + /// This method is a wrapper around OS loader, using "default" flags. + /// + /// The name of the native library to be loaded + /// The handle for the loaded native library + /// If libraryPath is null + /// Thrown if the library can't be found. + /// Thrown if the library is not valid. + public static IntPtr LoadLibrary(string libraryPath) + { + bool throwOnError = true; + return LoadLibraryFromPath(libraryPath, throwOnError); + } + + /// + /// NativeLibrary Loader: Simple API that doesn't throw + /// + /// The name of the native library to be loaded + /// The out-parameter for the loaded native library handle + /// True on successful load, false otherwise + /// If libraryPath is null + public static bool TryLoadLibrary(string libraryPath, out IntPtr handle) + { + bool throwOnError = false; + handle = LoadLibraryFromPath(libraryPath, throwOnError); + return handle != IntPtr.Zero; + } + + /// + /// NativeLibrary Loader: High-level API + /// Given a library name, this function searches specific paths based on the + /// runtime configuration and attributes of the calling module. + /// This LoadLibrary() method does not invoke the managed call-backs for native library resolution: + /// * AssemblyLoadContext.LoadUnmanagedDll() + /// + /// The name of the native library to be loaded + /// The search path + /// The assembly loading the native library + /// The handle for the loaded library + /// If libraryPath is null + /// If assembly is null + /// Thrown if the library can't be found. + /// Thrown if the library is not valid. + public static IntPtr LoadLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) + { + RuntimeAssembly runtimeAssembly = (assembly != null) ? ((RuntimeAssembly)assembly).GetNativeHandle() : null; + bool throwOnError = true; + uint searchPathFlag = searchPath.HasValue ? (uint)searchPath.Value : 0; + return LoadLibraryByName(libraryName, runtimeAssembly, searchPath.HasValue, searchPathFlag, throwOnError); + } + + /// + /// NativeLibrary Loader: High-level API that doesn't throw. + /// + /// The name of the native library to be loaded + /// The search path + /// The assembly loading the native library + /// The out-parameter for the loaded native library handle + /// True on successful load, false otherwise + /// If libraryPath is null + /// If assembly is null and dllImportSearchPath includes AssemblyDirectory + public static bool TryLoadLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle) + { + RuntimeAssembly runtimeAssembly = (assembly != null) ? ((RuntimeAssembly)assembly).GetNativeHandle() : null; + bool throwOnError = false; + uint searchPathFlag = searchPath.HasValue ? (uint)searchPath.Value : 0; + handle = LoadLibraryByName(libraryName, runtimeAssembly, searchPath.HasValue, searchPathFlag, throwOnError); + return handle != IntPtr.Zero; + } + + /// + /// Free a loaded library + /// Given a library handle, free it. + /// No action if the input handle is null. + /// + /// The native library handle to be freed + /// If the operation fails + public static void FreeLibrary(IntPtr handle) + { + FreeNativeLibrary(handle); + } + + /// + /// Get the address of an exported Symbol + /// This is a simple wrapper around OS calls, and does not perform any name mangling. + /// + /// The native library handle + /// The name of the exported symbol + /// The address of the symbol + /// If handle or name is null + /// If name cannot be converted to UTF8 + /// If the symbol is not found + public static IntPtr GetLibraryExport(IntPtr handle, string name) + { + bool throwOnError = true; + return GetNativeLibraryExport(handle, name, throwOnError); + } + + /// + /// Get the address of an exported Symbol, but do not throw + /// + /// The native library handle + /// The name of the exported symbol + /// The out-parameter for the symbol address, if it exists + /// True on success, false otherwise + public static bool TryGetLibraryExport(IntPtr handle, string name, out IntPtr address) + { + bool throwOnError = false; + address = GetNativeLibraryExport(handle, name, throwOnError); + return address != IntPtr.Zero; + } + + /// 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 throwOnError); + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + internal static extern void FreeNativeLibrary(IntPtr handle); + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + internal static extern IntPtr GetNativeLibraryExport(IntPtr handle, string symbolName, bool throwOnError); } } diff --git a/src/dlls/mscorrc/mscorrc.rc b/src/dlls/mscorrc/mscorrc.rc index bb39c9a..59aa7c3 100644 --- a/src/dlls/mscorrc/mscorrc.rc +++ b/src/dlls/mscorrc/mscorrc.rc @@ -541,6 +541,8 @@ BEGIN IDS_EE_NDIRECT_LOADLIB_MAC "Unable to load shared library '%1' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: %2" IDS_EE_NDIRECT_GETPROCADDRESS_WIN "Unable to find an entry point named '%2' in DLL '%1'." IDS_EE_NDIRECT_GETPROCADDRESS_UNIX "Unable to find an entry point named '%2' in shared library '%1'." + IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL "Unable to find an entry point named '%1' in DLL." + IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO "Unable to find an entry point named '%1' in shared library." IDS_EE_NDIRECT_GETPROCADDRESS_NONAME "A library name must be specified in a DllImport attribute applied to non-IJW methods." IDS_EE_CLASS_CONSTRAINTS_VIOLATION "GenericArguments[%1], '%2', on '%3' violates the constraint of type parameter '%4'." IDS_EE_METHOD_CONSTRAINTS_VIOLATION "Method %1.%2: type argument '%3' violates the constraint of type parameter '%4'." diff --git a/src/dlls/mscorrc/resource.h b/src/dlls/mscorrc/resource.h index 8c315e1..735f315 100644 --- a/src/dlls/mscorrc/resource.h +++ b/src/dlls/mscorrc/resource.h @@ -718,3 +718,8 @@ #define IDS_INVOKE_NULLREF_RETURNED 0x2642 #define IDS_EE_CANNOT_SET_INITONLY_STATIC_FIELD 0x2643 + +#define IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL 0x2644 +#define IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO 0x2645 + + diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 4598999..e304d85 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -2552,6 +2552,8 @@ OpenFileMappingW( #define OpenFileMapping OpenFileMappingA #endif +typedef INT_PTR (PALAPI *FARPROC)(); + PALIMPORT LPVOID PALAPI @@ -2604,9 +2606,22 @@ PALIMPORT HMODULE PALAPI PAL_RegisterLibraryDirect( - IN void *dl_handle, + IN NATIVE_LIBRARY_HANDLE dl_handle, IN LPCWSTR lpLibFileName); +PALIMPORT +BOOL +PALAPI +PAL_FreeLibraryDirect( + IN NATIVE_LIBRARY_HANDLE dl_handle); + +PALIMPORT +FARPROC +PALAPI +PAL_GetProcAddressDirect( + IN NATIVE_LIBRARY_HANDLE dl_handle, + IN LPCSTR lpProcName); + /*++ Function: PAL_LOADLoadPEFile @@ -2650,8 +2665,6 @@ PAL_LOADUnloadPEFile(void * ptr); #define LoadLibraryEx LoadLibraryExA #endif -typedef INT_PTR (PALAPI *FARPROC)(); - PALIMPORT FARPROC PALAPI diff --git a/src/pal/src/include/pal/module.h b/src/pal/src/include/pal/module.h index 72df268..aacc326 100644 --- a/src/pal/src/include/pal/module.h +++ b/src/pal/src/include/pal/module.h @@ -31,13 +31,13 @@ typedef VOID (PALAPI *PUNREGISTER_MODULE)(HINSTANCE); /* used to clean typedef struct _MODSTRUCT { - HMODULE self; /* circular reference to this module */ - void *dl_handle; /* handle returned by dlopen() */ - HINSTANCE hinstance; /* handle returned by PAL_RegisterLibrary */ - LPWSTR lib_name; /* full path of module */ - INT refcount; /* reference count */ - /* -1 means infinite reference count - module is never released */ - BOOL threadLibCalls; /* TRUE for DLL_THREAD_ATTACH/DETACH notifications enabled, FALSE if they are disabled */ + HMODULE self; /* circular reference to this module */ + NATIVE_LIBRARY_HANDLE dl_handle; /* handle returned by dlopen() */ + HINSTANCE hinstance; /* handle returned by PAL_RegisterLibrary */ + LPWSTR lib_name; /* full path of module */ + INT refcount; /* reference count */ + /* -1 means infinite reference count - module is never released */ + BOOL threadLibCalls; /* TRUE for DLL_THREAD_ATTACH/DETACH notifications enabled, FALSE if they are disabled */ #if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN ino_t inode; diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp index 04e41c3..3f24689 100644 --- a/src/pal/src/loader/module.cpp +++ b/src/pal/src/loader/module.cpp @@ -630,7 +630,7 @@ PAL_LoadLibraryDirect( dl_handle = LOADLoadLibraryDirect(lpcstr); done: - LOGEXIT("LoadLibraryDirect returns HMODULE %p\n", dl_handle); + LOGEXIT("LoadLibraryDirect returns NATIVE_LIBRARY_HANDLE %p\n", dl_handle); PERF_EXIT(LoadLibraryDirect); return dl_handle; } @@ -689,6 +689,59 @@ done: return hModule; } +/* +Function: + PAL_FreeLibraryDirect + + Free a loaded library + + Returns true on success, false on failure. +*/ +BOOL +PALAPI +PAL_FreeLibraryDirect( + IN NATIVE_LIBRARY_HANDLE dl_handle) +{ + BOOL retValue = 0; + PERF_ENTRY(PAL_FreeLibraryDirect); + ENTRY("PAL_FreeLibraryDirect (dl_handle=%p) \n", dl_handle); + + retValue = dlclose(dl_handle) == 0; + + LOGEXIT("PAL_FreeLibraryDirect returns BOOL %p\n", retValue); + PERF_EXIT(PAL_FreeLibraryDirect); + return retValue; +} + +/* +Function: + PAL_GetProcAddressDirect + + Get the address corresponding to a symbol in a loaded native library. + + Returns the address of the sumbol loaded in memory. +*/ +FARPROC +PALAPI +PAL_GetProcAddressDirect( + IN NATIVE_LIBRARY_HANDLE dl_handle, + IN LPCSTR lpProcName) +{ + INT name_length; + FARPROC address = nullptr; + + PERF_ENTRY(PAL_GetProcAddressDirect); + ENTRY("PAL_GetProcAddressDirect (lpLibFileName=%p (%S)) \n", + lpProcName ? lpProcName : "NULL", + lpProcName ? lpProcName : "NULL"); + + address = (FARPROC) dlsym(dl_handle, lpProcName); + + LOGEXIT("PAL_GetProcAddressDirect returns FARPROC %p\n", address); + PERF_EXIT(PAL_GetProcAddressDirect); + return address; +} + /*++ Function: PAL_RegisterModule diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp index f95a181..afa4645 100644 --- a/src/vm/assemblynative.cpp +++ b/src/vm/assemblynative.cpp @@ -314,7 +314,7 @@ INT_PTR QCALLTYPE AssemblyNative::InternalLoadUnmanagedDllFromPath(LPCWSTR unman BEGIN_QCALL; - moduleHandle = NDirect::LoadLibraryFromPath(unmanagedLibraryPath); + moduleHandle = NDirect::LoadLibraryFromPath(unmanagedLibraryPath, true); END_QCALL; diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp index 9ad41c5..18f4049 100644 --- a/src/vm/dllimport.cpp +++ b/src/vm/dllimport.cpp @@ -48,11 +48,14 @@ #endif // FEATURE_PREJIT #include "eventtrace.h" - - #include "clr/fs/path.h" using namespace clr::fs; +// The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags. +// In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR. +// Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary() +#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2 + // remove when we get an updated SDK #define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 #define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 @@ -6069,7 +6072,6 @@ private: SString m_message; }; // class LoadLibErrorTracker -// Local helper function to load a library. // Load the library directly. On Unix systems, don't register it yet with PAL. // * External callers like AssemblyNative::InternalLoadUnmanagedDllFromPath() and the upcoming // System.Runtime.Interop.Marshall.LoadLibrary() need the raw system handle @@ -6132,14 +6134,18 @@ bool NDirect::s_fSecureLoadLibrarySupported = false; #endif // !FEATURE_PAL // static -NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath) +NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError) { STANDARD_VM_CONTRACT; + if (libraryPath == NULL) + COMPlusThrowArgumentNull(W("libraryPath"), W("ArgumentNull_String")); + LoadLibErrorTracker errorTracker; const NATIVE_LIBRARY_HANDLE hmod = LocalLoadLibraryHelper(libraryPath, GetLoadWithAlteredSearchPathFlag(), &errorTracker); - if (hmod == nullptr) + + if (throwOnError && (hmod == nullptr)) { SString libraryPathSString(libraryPath); errorTracker.Throw(libraryPathSString); @@ -6147,8 +6153,133 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath) return hmod; } -/* static */ -NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, const wchar_t* wszLibName) +// static +NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly, + BOOL hasDllImportSearchFlag, DWORD dllImportSearchFlag, + BOOL throwOnError) +{ + STANDARD_VM_CONTRACT; + + LoadLibErrorTracker errorTracker; + + if (libraryName == NULL) + COMPlusThrowArgumentNull(W("libraryName"), W("ArgumentNull_String")); + + if (callingAssembly == NULL) + COMPlusThrowArgumentNull(W("callingAssembly"), W("ArgumentNull_Assembly")); + + // First checks if a default DllImportSearchPathFlag 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; + + if (hasDllImportSearchFlag) + { + dllImportSearchPathFlag = dllImportSearchFlag & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY; + searchAssemblyDirectory = dllImportSearchFlag & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY; + + } + else + { + Module * pModule = callingAssembly->GetManifestModule(); + + if (pModule->HasDefaultDllImportSearchPathsAttribute()) + { + dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue(); + searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory(); + } + } + + NATIVE_LIBRARY_HANDLE hmod = + LoadLibraryModuleBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlag, &errorTracker, libraryName); + + if (throwOnError && (hmod == nullptr)) + { + SString libraryPathSString(libraryName); + errorTracker.Throw(libraryPathSString); + } + + return hmod; +} + +// static +NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, PCWSTR wszLibName) +{ + STANDARD_VM_CONTRACT; + + // 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; + + if (pMD->HasDefaultDllImportSearchPathsAttribute()) + { + dllImportSearchPathFlag = pMD->DefaultDllImportSearchPathsAttributeCachedValue(); + searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory(); + } + else + { + Module * pModule = pMD->GetModule(); + + if (pModule->HasDefaultDllImportSearchPathsAttribute()) + { + dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue(); + searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory(); + } + } + + Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); + return LoadLibraryModuleBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlag, pErrorTracker, wszLibName); +} + +// static +void NDirect::FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle) +{ + STANDARD_VM_CONTRACT; + + // FreeLibrary doesn't throw if the input is null. + // This avoids further null propagation/check while freeing resources (ex: in finally blocks) + if (handle == NULL) + return; + +#ifndef FEATURE_PAL + BOOL retVal = FreeLibrary(handle); +#else // !FEATURE_PAL + BOOL retVal = PAL_FreeLibraryDirect(handle); +#endif // !FEATURE_PAL + + if (retVal == 0) + COMPlusThrow(kInvalidOperationException, W("Arg_InvalidOperationException")); +} + +//static +INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError) +{ + STANDARD_VM_CONTRACT; + + if (handle == NULL) + COMPlusThrowArgumentNull(W("handle"), W("Arg_InvalidHandle")); + + if (symbolName == NULL) + COMPlusThrowArgumentNull(W("symbolName"), W("ArgumentNull_String")); + + MAKE_UTF8PTR_FROMWIDE(lpstr, symbolName); + +#ifndef FEATURE_PAL + INT_PTR address = reinterpret_cast(GetProcAddress((HMODULE)handle, lpstr)); + if ((address == NULL) && throwOnError) + COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL, symbolName); +#else // !FEATURE_PAL + INT_PTR address = reinterpret_cast(PAL_GetProcAddressDirect((NATIVE_LIBRARY_HANDLE)handle, lpstr)); + if ((address == NULL) && throwOnError) + COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO, symbolName); +#endif // !FEATURE_PAL + + return address; +} + +// static +NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, PCWSTR wszLibName) { STANDARD_VM_CONTRACT; //Dynamic Pinvoke Support: @@ -6376,12 +6507,14 @@ static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* num #endif // FEATURE_PAL // Search for the library and variants of its name in probing directories. -NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName) +//static +NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssembly, + BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlag, + LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName) { STANDARD_VM_CONTRACT; - + NATIVE_LIBRARY_HANDLE hmod = NULL; - AppDomain* pDomain = GetAppDomain(); #if defined(FEATURE_CORESYSTEM) && !defined(PLATFORM_UNIX) // Try to go straight to System32 for Windows API sets. This is replicating quick check from @@ -6396,9 +6529,10 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD } #endif // FEATURE_CORESYSTEM && !FEATURE_PAL + AppDomain* pDomain = GetAppDomain(); DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag(); bool libNameIsRelativePath = Path::IsRelative(wszLibName); - DWORD dllImportSearchPathFlag = 0; + // P/Invokes are often declared with variations on the actual library name. // For example, it's common to leave off the extension/suffix of the library // even if it has one, or to leave off a prefix like "lib" even if it has one @@ -6419,31 +6553,6 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD return hmod; } - // First checks if the method has DefaultDllImportSearchPathsAttribute. If method has the attribute - // then dllImportSearchPathFlag is set to its value. - // Otherwise checks if the assembly has the attribute. - // If assembly has the attribute then flag is set to its value. - BOOL searchAssemblyDirectory = TRUE; - BOOL attributeIsFound = FALSE; - - if (pMD->HasDefaultDllImportSearchPathsAttribute()) - { - dllImportSearchPathFlag = pMD->DefaultDllImportSearchPathsAttributeCachedValue(); - searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory(); - attributeIsFound = TRUE; - } - else - { - Module * pModule = pMD->GetModule(); - - if (pModule->HasDefaultDllImportSearchPathsAttribute()) - { - dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue(); - searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory(); - attributeIsFound = TRUE; - } - } - if (!libNameIsRelativePath) { DWORD flags = loadWithAlteredPathFlags; @@ -6460,10 +6569,9 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD return hmod; } } - else if (searchAssemblyDirectory) + else if ((callingAssembly != nullptr) && searchAssemblyDirectory) { - Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); - hmod = LoadFromPInvokeAssemblyDirectory(pAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlag, pErrorTracker); + hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlag, pErrorTracker); if (hmod != NULL) { return hmod; @@ -6547,8 +6655,6 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke hmod = pDomain->FindUnmanagedImageInCache(wszLibName); if (hmod != NULL) { - // AppDomain caches the PAL_registered handles - // So, no need to PAL_Register the handle obtained from the cache return hmod.Extract(); } @@ -6574,7 +6680,7 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke hmod = PAL_RegisterLibraryDirect(hmod, wszLibName); #endif // FEATURE_PAL } - } + } if (hmod != NULL) { diff --git a/src/vm/dllimport.h b/src/vm/dllimport.h index 05336b4..2f25e59 100644 --- a/src/vm/dllimport.h +++ b/src/vm/dllimport.h @@ -5,9 +5,6 @@ // File: DllImport.h // -// - - #ifndef __dllimport_h__ #define __dllimport_h__ @@ -77,9 +74,13 @@ public: static HRESULT HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs); static LPVOID NDirectGetEntryPoint(NDirectMethodDesc *pMD, HINSTANCE hMod); - static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath); + static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError); + static NATIVE_LIBRARY_HANDLE LoadLibraryByName(LPCWSTR name, Assembly *callingAssembly, + BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag, + BOOL throwOnError); static HINSTANCE LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracker *pErrorTracker); - + static void FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle); + static INT_PTR GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError); static VOID NDirectLink(NDirectMethodDesc *pMD); @@ -125,6 +126,7 @@ private: 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 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); #if !defined(FEATURE_PAL) // Indicates if the OS supports the new secure LoadLibraryEx flags introduced in KB2533623 diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 9c8ef0b..f7c5a79 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -863,6 +863,12 @@ FCFuncStart(gInteropMarshalFuncs) FCFuncElement("GetExceptionForHRInternal", MarshalNative::GetExceptionForHR) FCFuncElement("GetDelegateForFunctionPointerInternal", MarshalNative::GetDelegateForFunctionPointerInternal) FCFuncElement("GetFunctionPointerForDelegateInternal", MarshalNative::GetFunctionPointerForDelegateInternal) + + QCFuncElement("LoadLibraryFromPath", MarshalNative::LoadLibraryFromPath) + QCFuncElement("LoadLibraryByName", MarshalNative::LoadLibraryByName) + QCFuncElement("FreeNativeLibrary", MarshalNative::FreeNativeLibrary) + QCFuncElement("GetNativeLibraryExport", MarshalNative::GetNativeLibraryExport) + #ifdef FEATURE_COMINTEROP FCFuncElement("GetHRForException", MarshalNative::GetHRForException) FCFuncElement("GetHRForException_WinRT", MarshalNative::GetHRForException_WinRT) diff --git a/src/vm/marshalnative.cpp b/src/vm/marshalnative.cpp index a356900..117b66d 100644 --- a/src/vm/marshalnative.cpp +++ b/src/vm/marshalnative.cpp @@ -923,6 +923,68 @@ FCIMPL1(int, MarshalNative::GetHRForException_WinRT, Object* eUNSAFE) } FCIMPLEND +// static +INT_PTR QCALLTYPE MarshalNative::LoadLibraryFromPath(LPCWSTR path, BOOL throwOnError) +{ + QCALL_CONTRACT; + + NATIVE_LIBRARY_HANDLE handle = nullptr; + + BEGIN_QCALL; + + handle = NDirect::LoadLibraryFromPath(path, throwOnError); + + END_QCALL; + + return reinterpret_cast(handle); +} + +// static +INT_PTR QCALLTYPE MarshalNative::LoadLibraryByName(LPCWSTR name, QCall::AssemblyHandle callingAssembly, + BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag, + BOOL throwOnError) +{ + QCALL_CONTRACT; + + NATIVE_LIBRARY_HANDLE handle = nullptr; + Assembly *pAssembly = (callingAssembly != NULL) ? callingAssembly->GetAssembly() : NULL; + + BEGIN_QCALL; + + handle = NDirect::LoadLibraryByName(name, pAssembly, hasDllImportSearchPathFlag, dllImportSearchPathFlag, throwOnError); + + END_QCALL; + + return reinterpret_cast(handle); +} + +// static +void QCALLTYPE MarshalNative::FreeNativeLibrary(INT_PTR handle) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + NDirect::FreeNativeLibrary((NATIVE_LIBRARY_HANDLE) handle); + + END_QCALL; +} + +//static +INT_PTR QCALLTYPE MarshalNative::GetNativeLibraryExport(INT_PTR handle, LPCWSTR symbolName, BOOL throwOnError) +{ + QCALL_CONTRACT; + + INT_PTR address = NULL; + + BEGIN_QCALL; + + address = NDirect::GetNativeLibraryExport((NATIVE_LIBRARY_HANDLE)handle, symbolName, throwOnError); + + END_QCALL; + + return address; +} #ifdef FEATURE_COMINTEROP diff --git a/src/vm/marshalnative.h b/src/vm/marshalnative.h index 25c1dc3..9b6aa2c 100644 --- a/src/vm/marshalnative.h +++ b/src/vm/marshalnative.h @@ -85,6 +85,17 @@ public: static FCDECL2(Object*, GetDelegateForFunctionPointerInternal, LPVOID FPtr, ReflectClassBaseObject* refTypeUNSAFE); static FCDECL1(LPVOID, GetFunctionPointerForDelegateInternal, Object* refDelegateUNSAFE); + + //==================================================================== + // These methods provide the native callbacks for library loading APIs + //==================================================================== + static INT_PTR QCALLTYPE LoadLibraryFromPath(LPCWSTR path, BOOL throwOnError); + static INT_PTR QCALLTYPE LoadLibraryByName(LPCWSTR name, QCall::AssemblyHandle callingAssembly, + BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag, + BOOL throwOnError); + static void QCALLTYPE FreeNativeLibrary(INT_PTR handle); + static INT_PTR QCALLTYPE GetNativeLibraryExport(INT_PTR handle, LPCWSTR symbolName, BOOL throwOnError); + #ifdef FEATURE_COMINTEROP //==================================================================== // map GUID to Type diff --git a/tests/src/Common/CoreCLRTestLibrary/Utilities.cs b/tests/src/Common/CoreCLRTestLibrary/Utilities.cs index 5b36bd2..d1ef171 100644 --- a/tests/src/Common/CoreCLRTestLibrary/Utilities.cs +++ b/tests/src/Common/CoreCLRTestLibrary/Utilities.cs @@ -57,10 +57,15 @@ namespace TestLibrary } } - public static bool IsWindows => (Path.DirectorySeparatorChar == '\\'); - + public static bool IsX86 => (RuntimeInformation.ProcessArchitecture == Architecture.X86); + public static bool IsX64 => (RuntimeInformation.ProcessArchitecture == Architecture.X64); + public static bool IsArm => (RuntimeInformation.ProcessArchitecture == Architecture.Arm); + public static bool IsArm64 => (RuntimeInformation.ProcessArchitecture == Architecture.Arm64); + + public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + public static bool IsMacOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); public static bool IsWindowsNanoServer => (!IsWindowsIoTCore && GetInstallationType().Equals("Nano Server", StringComparison.OrdinalIgnoreCase)); - public static bool IsWindowsIoTCore { get diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt index bfebf73..a1a55e7 100644 --- a/tests/src/Interop/CMakeLists.txt +++ b/tests/src/Interop/CMakeLists.txt @@ -54,6 +54,7 @@ add_subdirectory(StringMarshalling/AnsiBSTR) add_subdirectory(StringMarshalling/VBByRefStr) add_subdirectory(MarshalAPI/FunctionPointer) add_subdirectory(MarshalAPI/IUnknown) +add_subdirectory(MarshalAPI/NativeLibrary) add_subdirectory(SizeConst) add_subdirectory(DllImportAttribute/ExeFile) add_subdirectory(DllImportAttribute/FileNameContainDot) diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt b/tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt new file mode 100644 index 0000000..f7d98ba --- /dev/null +++ b/tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required (VERSION 2.6) +project (NativeLibrary) +include_directories(${INC_PLATFORM_DIR}) +set(SOURCES NativeLibrary.cpp) + +# add the executable +add_library (NativeLibrary SHARED ${SOURCES}) +target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS NativeLibrary DESTINATION bin) + + diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp new file mode 100644 index 0000000..7c1090f --- /dev/null +++ b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp @@ -0,0 +1,18 @@ +// 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; +} + +extern "C" DLL_EXPORT int RunExportedFunction(void *function, int arg1, int arg2) +{ + int(*f)(int, int) = reinterpret_cast(function); + return f(arg1, arg2); +} + + diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs new file mode 100644 index 0000000..7ef2afb --- /dev/null +++ b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs @@ -0,0 +1,379 @@ +// 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; + +enum TestResult { + Success, + ReturnFailure, + ReturnNull, + IncorrectEvaluation, + ArgumentNull, + ArgumentBad, + + DllNotFound, + BadImage, + InvalidOperation, + EntryPointNotFound, + GenericException + }; + +public class NativeLibraryTest +{ + static string CurrentTest; + static bool Verbose = false; + + public static int Main() + { + bool success = true; + + Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly(); + string testBinDir = Path.GetDirectoryName(assembly.Location); + string libName; + IntPtr handle; + + // ----------------------------------------------- + // 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)); + + // Calls on non-existant file + libName = Path.Combine(testBinDir, "notfound"); + success &= EXPECT(LoadLibrarySimple(libName), TestResult.DllNotFound); + success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ReturnFailure); + + // Calls on an invalid file + libName = Path.Combine(testBinDir, "NativeLibrary.cpp"); + success &= EXPECT(LoadLibrarySimple(libName), + (TestLibrary.Utilities.IsWindows) ? TestResult.BadImage : TestResult.DllNotFound); + success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ReturnFailure); + + // Calls on null input + libName = null; + success &= EXPECT(LoadLibrarySimple(libName), TestResult.ArgumentNull); + success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ArgumentNull); + + // ----------------------------------------------- + // Advanced LoadLibrary() API Tests + // ----------------------------------------------- + + // 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)); + + // 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); + + // 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), + (TestLibrary.Utilities.IsWindows) ? TestResult.BadImage : TestResult.DllNotFound); + success &= 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)); + + // Calls on Native Library name with correct prefix-suffix + libName = GetNativeLibraryName(); + success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null)); + success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); + + // Calls on full path without prefix-siffix + libName = Path.Combine(testBinDir, GetNativeLibraryPlainName()); + // 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), + (TestLibrary.Utilities.IsWindows) ? TestResult.Success : TestResult.DllNotFound); + success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), + (TestLibrary.Utilities.IsWindows) ? TestResult.Success : TestResult.ReturnFailure); + + if (TestLibrary.Utilities.IsWindows) + { + // Calls on a valid library from System32 directory + libName = GetNativeLibraryPlainName(); + success &= EXPECT(LoadLibraryAdvanced("mapi32", assembly, DllImportSearchPath.System32)); + success &= EXPECT(TryLoadLibraryAdvanced("mapi32", assembly, DllImportSearchPath.System32)); + + // Calls on a valid library from application directory + success &= EXPECT(LoadLibraryAdvanced("mapi32", assembly, DllImportSearchPath.ApplicationDirectory), TestResult.DllNotFound); + success &= EXPECT(TryLoadLibraryAdvanced("mapi32", 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); + + // Calls with null assembly + libName = GetNativeLibraryPlainName(); + success &= EXPECT(LoadLibraryAdvanced(libName, null, null), TestResult.ArgumentNull); + success &= 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); + + // ----------------------------------------------- + // FreeLibrary Tests + // ----------------------------------------------- + + libName = Path.Combine(testBinDir, GetNativeLibraryName()); + handle = Marshal.LoadLibrary(libName); + + // Valid Free + success &= EXPECT(FreeLibrary(handle)); + + // Double Free + success &= EXPECT(FreeLibrary(handle), TestResult.InvalidOperation); + + // Null Free + success &= EXPECT(FreeLibrary(IntPtr.Zero)); + + // ----------------------------------------------- + // GetLibraryExport Tests + // ----------------------------------------------- + libName = Path.Combine(testBinDir, GetNativeLibraryName()); + handle = Marshal.LoadLibrary(libName); + + // 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")); + + // Call with null handle + success &= EXPECT(GetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); + success &= 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); + + // Call with wrong string + success &= EXPECT(GetLibraryExport(handle, "NonNativeSum"), TestResult.EntryPointNotFound); + success &= EXPECT(TryGetLibraryExport(handle, "NonNativeSum"), TestResult.ReturnFailure); + + Marshal.FreeLibrary(handle); + + return (success) ? 100 : -100; + } + + static string GetNativeLibraryPlainName() + { + return "NativeLibrary"; + } + + 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); + + IntPtr handle = IntPtr.Zero; + + TestResult result = Run(() => { + handle = Marshal.LoadLibrary(libPath); + if (handle == IntPtr.Zero) + return TestResult.ReturnNull; + return TestResult.Success; + }); + + Marshal.FreeLibrary(handle); + + return result; + } + + static TestResult TryLoadLibrarySimple(string libPath) + { + CurrentTest = String.Format("TryLoadLibrary({0})", libPath); + + IntPtr handle = IntPtr.Zero; + + TestResult result = Run(() => { + bool success = Marshal.TryLoadLibrary(libPath, out handle); + if(!success) + return TestResult.ReturnFailure; + if (handle == null) + return TestResult.ReturnNull; + return TestResult.Success; + }); + + Marshal.FreeLibrary(handle); + + return result; + } + + + static TestResult LoadLibraryAdvanced(string libName, Assembly assembly, DllImportSearchPath? searchPath) + { + CurrentTest = String.Format("LoadLibrary({0}, {1}, {2})", libName, assembly, searchPath); + + IntPtr handle = IntPtr.Zero; + + TestResult result = Run(() => { + handle = Marshal.LoadLibrary(libName, assembly, searchPath); + if (handle == IntPtr.Zero) + return TestResult.ReturnNull; + return TestResult.Success; + }); + + Marshal.FreeLibrary(handle); + + return result; + } + + static TestResult TryLoadLibraryAdvanced(string libName, Assembly assembly, DllImportSearchPath? searchPath) + { + CurrentTest = String.Format("TryLoadLibrary({0}, {1}, {2})", libName, assembly, searchPath); + + IntPtr handle = IntPtr.Zero; + + TestResult result = Run(() => { + bool success = Marshal.TryLoadLibrary(libName, assembly, searchPath, out handle); + if (!success) + return TestResult.ReturnFailure; + if (handle == IntPtr.Zero) + return TestResult.ReturnNull; + return TestResult.Success; + }); + + Marshal.FreeLibrary(handle); + + return result; + } + + static TestResult FreeLibrary(IntPtr handle) + { + CurrentTest = String.Format("FreeLibrary({0})", handle); + + return Run(() => { + Marshal.FreeLibrary(handle); + return TestResult.Success; + }); + } + + static TestResult GetLibraryExport(IntPtr handle, string name) + { + CurrentTest = String.Format("GetLibraryExport({0}, {1})", handle, name); + + return Run(() => { + IntPtr address = Marshal.GetLibraryExport(handle, name); + if (address == null) + return TestResult.ReturnNull; + if (RunExportedFunction(address, 1, 1) != 2) + return TestResult.IncorrectEvaluation; + return TestResult.Success; + }); + } + + static TestResult TryGetLibraryExport(IntPtr handle, string name) + { + CurrentTest = String.Format("TryGetLibraryExport({0}, {1})", handle, name); + + return Run(() => { + IntPtr address = IntPtr.Zero; + bool success = Marshal.TryGetLibraryExport(handle, name, out address); + if (!success) + return TestResult.ReturnFailure; + if (address == null) + return TestResult.ReturnNull; + if (RunExportedFunction(address, 1, 1) != 2) + return TestResult.IncorrectEvaluation; + return TestResult.Success; + }); + } + + [DllImport("NativeLibrary")] + static extern int RunExportedFunction(IntPtr address, int arg1, int arg2); +} diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj new file mode 100644 index 0000000..d3d7eb2 --- /dev/null +++ b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj @@ -0,0 +1,45 @@ + + + + + Debug + AnyCPU + NativeLibraryTests + 2.0 + {F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + true + + + + + + + + + False + + + + + + + + + + + PreserveNewest + + + + + {c8c0dc74-fac4-45b1-81fe-70c4808366e0} + CoreCLRTestLibrary + + + + + -- 2.7.4