diff --git a/FoundationDB.Client/Native/FdbNative.cs b/FoundationDB.Client/Native/FdbNative.cs index 460032f0f..bf4adbc06 100644 --- a/FoundationDB.Client/Native/FdbNative.cs +++ b/FoundationDB.Client/Native/FdbNative.cs @@ -49,7 +49,7 @@ internal static unsafe class FdbNative private const string FDB_C_DLL = "libfdb_c.so"; #else /// Name of the C API dll used for P/Invoking - private const string FDB_C_DLL = "fdb_c.dll"; + private const string FDB_C_DLL = "fdb_c"; #endif /// Handle on the native FDB C API library @@ -235,42 +235,72 @@ public static extern void fdb_transaction_clear_range( static FdbNative() { - // Impact of NativeLibPath: - // - If null, don't preload the library, and let the CLR find the file using the default P/Invoke behavior - // - If String.Empty, call win32 LoadLibrary("fdb_c.dll") and let the os find the file (using the standard OS behavior) - // - Else, combine the path with "fdb_c.dll" and call LoadLibrary with the resulting (relative or absolute) path + var libraryPath = GetPreloadPath(); - var libraryPath = Fdb.Options.NativeLibPath; - if (libraryPath != null) + if (libraryPath == null) + { // PInvoke will load + return; + } + + try { - try + FdbCLib = UnmanagedLibrary.Load(libraryPath); + } + catch (Exception e) + { + if (FdbCLib != null) FdbCLib.Dispose(); + FdbCLib = null; + if (e is BadImageFormatException && IntPtr.Size == 4) { - if (libraryPath.Length == 0) - { // CLR will handle the search - libraryPath = FDB_C_DLL; - } - else if (!libraryPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) - { // add the file name - libraryPath = Path.Combine(Fdb.Options.NativeLibPath, FDB_C_DLL); - } - - FdbCLib = UnmanagedLibrary.Load(libraryPath); + e = new InvalidOperationException("The native FDB client is 64-bit only, and cannot be loaded in a 32-bit process.", e); } - catch (Exception e) + else { - if (FdbCLib != null) FdbCLib.Dispose(); - FdbCLib = null; - if (e is BadImageFormatException && IntPtr.Size == 4) - { - e = new InvalidOperationException("The native FDB client is 64-bit only, and cannot be loaded in a 32-bit process.", e); - } - else - { - e = new InvalidOperationException("An error occurred while loading the native FoundationDB library", e); - } - LibraryLoadError = ExceptionDispatchInfo.Capture(e); + e = new InvalidOperationException($"An error occurred while loading the native FoundationDB library: '{libraryPath}'.", e); + } + LibraryLoadError = ExceptionDispatchInfo.Capture(e); + } + + } + + private static string GetPreloadPath() + { + // we need to provide sensible defaults for loading the native library + // if this method returns null we'll let PInvoke deal + // otherwise - use explicit platform-specific dll loading + var libraryPath = Fdb.Options.NativeLibPath; + + // on non-windows, library loading by convention just works. + // unless override is provided, just let PInvoke do the work + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (string.IsNullOrEmpty(libraryPath)) + { + return null; } + // otherwise just use the provided path + return libraryPath; + } + + // Impact of NativeLibPath on windows: + // - If null, don't preload the library, and let the CLR find the file using the default P/Invoke behavior + // - If String.Empty, call win32 LoadLibrary(FDB_C_DLL + ".dll") and let the os find the file (using the standard OS behavior) + // - If path is folder, append the FDB_C_DLL + var winDllWithExtension = FDB_C_DLL + ".dll"; + if (libraryPath == null) + { + return null; + } + if (libraryPath.Length == 0) + { + return winDllWithExtension; + } + var fileName = Path.GetFileName(libraryPath); + if (String.IsNullOrEmpty(fileName)) + { + libraryPath = Path.Combine(libraryPath, winDllWithExtension); } + return libraryPath; } private static void EnsureLibraryIsLoaded() diff --git a/FoundationDB.Client/Native/UnmanagedLibrary.cs b/FoundationDB.Client/Native/UnmanagedLibrary.cs index 62659bbdd..968045b0a 100644 --- a/FoundationDB.Client/Native/UnmanagedLibrary.cs +++ b/FoundationDB.Client/Native/UnmanagedLibrary.cs @@ -72,23 +72,24 @@ protected override bool ReleaseHandle() [SuppressUnmanagedCodeSecurity] private static class NativeMethods { -#if __MonoCS__ - const string KERNEL = "dl"; + const string LIBDL = "dl"; + - [DllImport(KERNEL)] + [DllImport(LIBDL)] public static extern SafeLibraryHandle dlopen(string fileName, int flags); - [DllImport(KERNEL, SetLastError = true)] + + [DllImport(LIBDL, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern int dlclose(IntPtr hModule); - public static SafeLibraryHandle LoadLibrary(string fileName) - { +#if __MonoCS__ + public static SafeLibraryHandle LoadPlatformLibrary(string fileName) + { return dlopen(fileName, 1); - } - public static bool FreeLibrary(IntPtr hModule) { return dlclose(hModule) == 0; } + public static bool FreePlatformLibrary(IntPtr hModule) { return dlclose(hModule) == 0; } #else const string KERNEL = "kernel32"; @@ -100,6 +101,24 @@ public static SafeLibraryHandle LoadLibrary(string fileName) [DllImport(KERNEL, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FreeLibrary(IntPtr hModule); + + public static SafeLibraryHandle LoadPlatformLibrary(string fileName) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return LoadLibrary(fileName); + } + return dlopen(fileName, 1); + } + + public static bool FreePlatformLibrary(IntPtr hModule) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return FreeLibrary(hModule); + } + return dlclose(hModule) == 0; + } #endif } @@ -112,7 +131,7 @@ public static UnmanagedLibrary Load(string path) { if (path == null) throw new ArgumentNullException("path"); - var handle = NativeMethods.LoadLibrary(path); + var handle = NativeMethods.LoadPlatformLibrary(path); if (handle == null || handle.IsInvalid) { var ex = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());