diff --git a/src/Numerics/Control.cs b/src/Numerics/Control.cs
index 1632b53c..ee2c9d4e 100644
--- a/src/Numerics/Control.cs
+++ b/src/Numerics/Control.cs
@@ -30,6 +30,7 @@
using MathNet.Numerics.Providers.LinearAlgebra;
using System;
+using System.IO;
using System.Threading.Tasks;
namespace MathNet.Numerics
@@ -144,6 +145,11 @@ namespace MathNet.Numerics
///
public static bool ThreadSafeRandomNumberGenerators { get; set; }
+ ///
+ /// Optional path to try to load native provider binaries from.
+ ///
+ public static DirectoryInfo NativeProviderPath { get; set; }
+
///
/// Gets or sets the linear algebra provider. Consider to use UseNativeMKL or UseManaged instead.
///
diff --git a/src/Numerics/Providers/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs b/src/Numerics/Providers/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs
index 5fe07ce2..33ee212e 100644
--- a/src/Numerics/Providers/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs
+++ b/src/Numerics/Providers/LinearAlgebra/Mkl/MklLinearAlgebraProvider.cs
@@ -138,7 +138,7 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Mkl
try
{
// Load the native library
- NativeProviderLoader.LoadNativeLibrary(SafeNativeMethods.DllName);
+ NativeProviderLoader.TryLoad(SafeNativeMethods.DllName);
a = SafeNativeMethods.query_capability(0);
b = SafeNativeMethods.query_capability(1);
diff --git a/src/Numerics/Providers/NativeProviderLoader.cs b/src/Numerics/Providers/NativeProviderLoader.cs
index 3a8c2b5b..f9e19c40 100644
--- a/src/Numerics/Providers/NativeProviderLoader.cs
+++ b/src/Numerics/Providers/NativeProviderLoader.cs
@@ -42,34 +42,43 @@ namespace MathNet.Numerics.Providers
///
/// Helper class to load native libraries depending on the architecture of the OS and process.
///
- static internal class NativeProviderLoader
+ internal static class NativeProviderLoader
{
- private static Lazy> _ArchitectureDirectories
- = new Lazy>(
- () => new Dictionary(StringComparer.OrdinalIgnoreCase)
- {
- {"x86", "x86"},
- {"AMD64", "x64"},
- {"IA64", "ia64"},
- {"ARM", "arm"}
- },
- true);
+ static Lazy> _ArchitectureDirectories
+ = new Lazy>(
+ () => new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ { "x86", "x86" },
+ { "AMD64", "x64" },
+ { "IA64", "ia64" },
+ { "ARM", "arm" }
+ },
+ true);
+
///
/// Default directories for each architecture.
///
- private static Dictionary ArchitectureDirectories { get { return _ArchitectureDirectories.Value; } }
+ static Dictionary ArchitectureDirectories
+ {
+ get { return _ArchitectureDirectories.Value; }
+ }
+
+ static Lazy> _nativeHandles = new Lazy>(true);
- private static Lazy> _nativeHandles = new Lazy>(true);
///
/// Dictionary of handles to previously loaded libraries,
///
- private static Dictionary NativeHandles { get { return _nativeHandles.Value; } }
+ static Dictionary NativeHandles
+ {
+ get { return _nativeHandles.Value; }
+ }
+
+ static string _ArchDirectory = null;
- private static string _ArchDirectory = null;
///
/// Gets a string indicating the architecture and bitness of the current process.
///
- private static string ArchDirectory
+ static string ArchDirectory
{
get
{
@@ -99,7 +108,7 @@ namespace MathNet.Numerics.Providers
}
}
- private static bool IsUnix
+ static bool IsUnix
{
get
{
@@ -114,33 +123,82 @@ namespace MathNet.Numerics.Providers
///
public static Exception LastException { get; private set; }
- private static object staticLock = new Object();
+ static readonly object StaticLock = new Object();
///
/// Load the native library with the given filename.
///
/// The file name of the library to load.
/// True if the library was successfully loaded or if it has already been loaded.
- public static bool LoadNativeLibrary(string fileName)
+ public static bool TryLoad(string fileName)
{
if (string.IsNullOrEmpty(fileName))
+ {
throw new ArgumentNullException("fileName");
+ }
- lock (staticLock)
+ // If we have an extra path provided by the user, look there first
+ var userDir = Control.NativeProviderPath;
+ if (userDir != null && userDir.Exists && TryLoad(fileName, userDir))
{
- IntPtr libraryHandle = IntPtr.Zero;
- if (NativeHandles.TryGetValue(fileName, out libraryHandle))
- return true;
+ return true;
+ }
- if (string.IsNullOrEmpty(ArchDirectory))
- return false;
+ // Look under the current AppDomain's base directory
+ if (TryLoad(fileName, new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory)))
+ {
+ return true;
+ }
+
+ // Look at this assembly's directory
+ var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ if (!string.IsNullOrEmpty(assemblyDir) && TryLoad(fileName, new DirectoryInfo(assemblyDir)))
+ {
+ return true;
+ }
+
+ return false;
+ }
- // Look for the library in a acrhtecture directory under the current AppDomain's base directory or this assembly's directory
- string path = Path.Combine(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory), ArchDirectory, fileName);
- if (!File.Exists(path))
- path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), ArchDirectory, fileName);
+ ///
+ /// Try to load a native library by providing its name and a directory.
+ /// Tries to load an implementation suitable for the current CPU architecture
+ /// and process mode if there is a matching subfolder.
+ ///
+ /// True if the library was successfully loaded or if it has already been loaded.
+ public static bool TryLoad(string fileName, DirectoryInfo directory)
+ {
+ if (!directory.Exists)
+ {
+ return false;
+ }
+
+ // If we have a know architecture, try the matching subdirectory first
+ var architecture = ArchDirectory;
+ if (!string.IsNullOrEmpty(architecture) && TryLoadFile(new FileInfo(Path.Combine(directory.FullName, architecture, fileName))))
+ {
+ return true;
+ }
+
+ // Otherwise try to load directly from the provided directory
+ return TryLoadFile(new FileInfo(Path.Combine(directory.FullName, fileName)));
+ }
+
+ ///
+ /// Try to load a native library by providing the full path including the file name of the library.
+ ///
+ /// True if the library was successfully loaded or if it has already been loaded.
+ public static bool TryLoadFile(FileInfo file)
+ {
+ lock (StaticLock)
+ {
+ IntPtr libraryHandle;
+ if (NativeHandles.TryGetValue(file.Name, out libraryHandle))
+ {
+ return true;
+ }
- if (!File.Exists(path))
+ if (!file.Exists)
{
// If the library isn't found within an architecture specific folder then return false
// to allow normal P/Invoke searching behaviour when the library is called
@@ -148,7 +206,7 @@ namespace MathNet.Numerics.Providers
}
// If successful this will return a handle to the library
- libraryHandle = IsUnix ? UnixLoader.LoadLibrary(path) : WindowsLoader.LoadLibrary(path);
+ libraryHandle = IsUnix ? UnixLoader.LoadLibrary(file.FullName) : WindowsLoader.LoadLibrary(file.FullName);
if (libraryHandle == IntPtr.Zero)
{
int lastError = Marshal.GetLastWin32Error();
@@ -158,7 +216,7 @@ namespace MathNet.Numerics.Providers
else
{
LastException = null;
- NativeHandles[fileName] = libraryHandle;
+ NativeHandles[file.Name] = libraryHandle;
}
return libraryHandle != IntPtr.Zero;
@@ -167,7 +225,7 @@ namespace MathNet.Numerics.Providers
[SuppressUnmanagedCodeSecurity]
[SecurityCritical]
- private static class WindowsLoader
+ static class WindowsLoader
{
public static IntPtr LoadLibrary(string fileName)
{
@@ -178,12 +236,12 @@ namespace MathNet.Numerics.Providers
const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
[DllImport("kernel32", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr LoadLibraryEx(string fileName, IntPtr reservedNull, uint flags);
+ static extern IntPtr LoadLibraryEx(string fileName, IntPtr reservedNull, uint flags);
}
[SuppressUnmanagedCodeSecurity]
[SecurityCritical]
- private static class UnixLoader
+ static class UnixLoader
{
public static IntPtr LoadLibrary(string fileName)
{
@@ -193,7 +251,7 @@ namespace MathNet.Numerics.Providers
const int RTLD_NOW = 2;
[DllImport("libdl.so", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr dlopen(String fileName, int flags);
+ static extern IntPtr dlopen(String fileName, int flags);
}
}
}