Introduce Marshall.LoadLibrary API (#20871)
authorSwaroop Sridhar <Swaroop.Sridhar@microsoft.com>
Wed, 21 Nov 2018 19:42:01 +0000 (11:42 -0800)
committerGitHub <noreply@github.com>
Wed, 21 Nov 2018 19:42:01 +0000 (11:42 -0800)
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

18 files changed:
src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
src/dlls/mscorrc/mscorrc.rc
src/dlls/mscorrc/resource.h
src/pal/inc/pal.h
src/pal/src/include/pal/module.h
src/pal/src/loader/module.cpp
src/vm/assemblynative.cpp
src/vm/dllimport.cpp
src/vm/dllimport.h
src/vm/ecalllist.h
src/vm/marshalnative.cpp
src/vm/marshalnative.h
tests/src/Common/CoreCLRTestLibrary/Utilities.cs
tests/src/Interop/CMakeLists.txt
tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp [new file with mode: 0644]
tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs [new file with mode: 0644]
tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj [new file with mode: 0644]

index b4f4dfa..e6a2937 100644 (file)
@@ -1760,5 +1760,133 @@ namespace System.Runtime.InteropServices
             RuntimeImports.RhZeroMemory(s, (UIntPtr)(Win32Native.lstrlenW(s) * 2));
             FreeHGlobal(s);
         }
+
+        /// APIs for managing Native Libraries 
+
+        /// <summary>
+        /// NativeLibrary Loader: Simple API
+        /// This method is a wrapper around OS loader, using "default" flags.
+        /// </summary>
+        /// <param name="libraryName">The name of the native library to be loaded</param>
+        /// <returns>The handle for the loaded native library</returns>  
+        /// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
+        /// <exception cref="System.DllNotFoundException ">Thrown if the library can't be found.</exception>
+        /// <exception cref="System.BadImageFormatException">Thrown if the library is not valid.</exception>
+        public static IntPtr LoadLibrary(string libraryPath)
+        {
+            bool throwOnError = true;
+            return LoadLibraryFromPath(libraryPath, throwOnError);
+        }
+
+        /// <summary>
+        /// NativeLibrary Loader: Simple API that doesn't throw
+        /// </summary>
+        /// <param name="libraryName">The name of the native library to be loaded</param>
+        /// <param name="handle">The out-parameter for the loaded native library handle</param>
+        /// <returns>True on successful load, false otherwise</returns>  
+        /// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
+        public static bool TryLoadLibrary(string libraryPath, out IntPtr handle)
+        {
+            bool throwOnError = false;
+            handle = LoadLibraryFromPath(libraryPath, throwOnError);
+            return handle != IntPtr.Zero;
+        }
+
+        /// <summary>
+        /// 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()
+        /// </summary>
+        /// <param name="libraryName">The name of the native library to be loaded</param>
+        /// <param name="dllImportSearchPath">The search path</param>
+        /// <param name="assembly">The assembly loading the native library</param>
+        /// <returns>The handle for the loaded library</returns>  
+        /// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
+        /// <exception cref="System.ArgumentNullException">If assembly is null</exception>
+        /// <exception cref="System.DllNotFoundException ">Thrown if the library can't be found.</exception>
+        /// <exception cref="System.BadImageFormatException">Thrown if the library is not valid.</exception>        
+        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);
+        }
+
+        /// <summary>
+        /// NativeLibrary Loader: High-level API that doesn't throw.
+        /// </summary>
+        /// <param name="libraryName">The name of the native library to be loaded</param>
+        /// <param name="dllImportSearchPath">The search path</param>
+        /// <param name="assembly">The assembly loading the native library</param>
+        /// <param name="handle">The out-parameter for the loaded native library handle</param>
+        /// <returns>True on successful load, false otherwise</returns>  
+        /// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
+        /// <exception cref="System.ArgumentNullException">If assembly is null and dllImportSearchPath includes AssemblyDirectory</exception>
+        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;
+        }
+
+        /// <summary>
+        /// Free a loaded library
+        /// Given a library handle, free it.
+        /// No action if the input handle is null.
+        /// </summary>
+        /// <param name="handle">The native library handle to be freed</param>
+        /// <exception cref="System.InvalidOperationException">If the operation fails</exception>
+        public static void FreeLibrary(IntPtr handle)
+        {
+            FreeNativeLibrary(handle);
+        }
+
+        /// <summary>
+        /// Get the address of an exported Symbol
+        /// This is a simple wrapper around OS calls, and does not perform any name mangling.
+        /// </summary>
+        /// <param name="handle">The native library handle</param>
+        /// <param name="name">The name of the exported symbol</param>
+        /// <returns>The address of the symbol</returns>  
+        /// <exception cref="System.ArgumentNullException">If handle or name is null</exception>
+        /// <exception cref="System.ArgumentException">If name cannot be converted to UTF8</exception>
+        /// <exception cref="System.EntryPointNotFoundException">If the symbol is not found</exception>
+        public static IntPtr GetLibraryExport(IntPtr handle, string name)
+        {
+            bool throwOnError = true;
+            return GetNativeLibraryExport(handle, name, throwOnError);
+        }
+
+        /// <summary>
+        /// Get the address of an exported Symbol, but do not throw
+        /// </summary>
+        /// <param name="handle">The  native library handle</param>
+        /// <param name="name">The name of the exported symbol</param>
+        /// <param name="address"> The out-parameter for the symbol address, if it exists</param>
+        /// <returns>True on success, false otherwise</returns>  
+        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);
     }
 }
index bb39c9a..59aa7c3 100644 (file)
@@ -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'."
index 8c315e1..735f315 100644 (file)
 #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
+
+
index 4598999..e304d85 100644 (file)
@@ -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
index 72df268..aacc326 100644 (file)
@@ -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;
index 04e41c3..3f24689 100644 (file)
@@ -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
index f95a181..afa4645 100644 (file)
@@ -314,7 +314,7 @@ INT_PTR QCALLTYPE AssemblyNative::InternalLoadUnmanagedDllFromPath(LPCWSTR unman
 
     BEGIN_QCALL;
 
-    moduleHandle = NDirect::LoadLibraryFromPath(unmanagedLibraryPath);
+    moduleHandle = NDirect::LoadLibraryFromPath(unmanagedLibraryPath, true);
 
     END_QCALL;
 
index 9ad41c5..18f4049 100644 (file)
 #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<INT_PTR>(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<INT_PTR>(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)
     {
index 05336b4..2f25e59 100644 (file)
@@ -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
index 9c8ef0b..f7c5a79 100644 (file)
@@ -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)
index a356900..117b66d 100644 (file)
@@ -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<INT_PTR>(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<INT_PTR>(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
 
index 25c1dc3..9b6aa2c 100644 (file)
@@ -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
index 5b36bd2..d1ef171 100644 (file)
@@ -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
index bfebf73..a1a55e7 100644 (file)
@@ -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 (file)
index 0000000..f7d98ba
--- /dev/null
@@ -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 (file)
index 0000000..7c1090f
--- /dev/null
@@ -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 <stdio.h>
+#include <xplatform.h>
+
+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<int(*)(int,int)>(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 (file)
index 0000000..7ef2afb
--- /dev/null
@@ -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<TestResult> 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 (file)
index 0000000..d3d7eb2
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>NativeLibraryTests</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="*.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="NativeLibrary.cpp">
+        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\Common\CoreCLRTestLibrary\CoreCLRTestLibrary.csproj">
+      <Project>{c8c0dc74-fac4-45b1-81fe-70c4808366e0}</Project>
+      <Name>CoreCLRTestLibrary</Name>
+    </ProjectReference>
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>