Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
changed NativeLibraryLoader from proj ref to internal code due to dot…
…net pack limitations
- Loading branch information
Showing
8 changed files
with
575 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace NativeLibraryLoader | ||
{ | ||
internal static class Kernel32 | ||
{ | ||
[DllImport("kernel32")] | ||
public static extern IntPtr LoadLibrary(string fileName); | ||
|
||
[DllImport("kernel32")] | ||
public static extern IntPtr GetProcAddress(IntPtr module, string procName); | ||
|
||
[DllImport("kernel32")] | ||
public static extern int FreeLibrary(IntPtr module); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
using System; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace NativeLibraryLoader | ||
{ | ||
/// <summary> | ||
/// Exposes functionality for loading native libraries and function pointers. | ||
/// </summary> | ||
public abstract class LibraryLoader | ||
{ | ||
/// <summary> | ||
/// Loads a native library by name and returns an operating system handle to it. | ||
/// </summary> | ||
/// <param name="name">The name of the library to open.</param> | ||
/// <returns>The operating system handle for the shared library.</returns> | ||
public IntPtr LoadNativeLibrary(string name) | ||
{ | ||
return LoadNativeLibrary(name, PathResolver.Default); | ||
} | ||
|
||
/// <summary> | ||
/// Loads a native library by name and returns an operating system handle to it. | ||
/// </summary> | ||
/// <param name="names">An ordered list of names. Each name is tried in turn, until the library is successfully loaded. | ||
/// </param> | ||
/// <returns>The operating system handle for the shared library.</returns> | ||
public IntPtr LoadNativeLibrary(string[] names) | ||
{ | ||
return LoadNativeLibrary(names, PathResolver.Default); | ||
} | ||
|
||
/// <summary> | ||
/// Loads a native library by name and returns an operating system handle to it. | ||
/// </summary> | ||
/// <param name="name">The name of the library to open.</param> | ||
/// <param name="pathResolver">The path resolver to use.</param> | ||
/// <returns>The operating system handle for the shared library.</returns> | ||
public IntPtr LoadNativeLibrary(string name, PathResolver pathResolver) | ||
{ | ||
if (string.IsNullOrEmpty(name)) | ||
{ | ||
throw new ArgumentException("Parameter must not be null or empty.", nameof(name)); | ||
} | ||
|
||
IntPtr ret = LoadWithResolver(name, pathResolver); | ||
|
||
if (ret == IntPtr.Zero) | ||
{ | ||
throw new FileNotFoundException("Could not find or load the native library: " + name); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
/// <summary> | ||
/// Loads a native library by name and returns an operating system handle to it. | ||
/// </summary> | ||
/// <param name="names">An ordered list of names. Each name is tried in turn, until the library is successfully loaded. | ||
/// </param> | ||
/// <param name="pathResolver">The path resolver to use.</param> | ||
/// <returns>The operating system handle for the shared library.</returns> | ||
public IntPtr LoadNativeLibrary(string[] names, PathResolver pathResolver) | ||
{ | ||
if (names == null || names.Length == 0) | ||
{ | ||
throw new ArgumentException("Parameter must not be null or empty.", nameof(names)); | ||
} | ||
|
||
IntPtr ret = IntPtr.Zero; | ||
foreach (string name in names) | ||
{ | ||
ret = LoadWithResolver(name, pathResolver); | ||
if (ret != IntPtr.Zero) | ||
{ | ||
break; | ||
} | ||
} | ||
|
||
if (ret == IntPtr.Zero) | ||
{ | ||
throw new FileNotFoundException($"Could not find or load the native library from any name: [ {string.Join(", ", names)} ]"); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
private IntPtr LoadWithResolver(string name, PathResolver pathResolver) | ||
{ | ||
if (Path.IsPathRooted(name)) | ||
{ | ||
return CoreLoadNativeLibrary(name); | ||
} | ||
else | ||
{ | ||
foreach (string loadTarget in pathResolver.EnumeratePossibleLibraryLoadTargets(name)) | ||
{ | ||
if (!Path.IsPathRooted(loadTarget) || File.Exists(loadTarget)) | ||
{ | ||
IntPtr ret = CoreLoadNativeLibrary(loadTarget); | ||
if (ret != IntPtr.Zero) | ||
{ | ||
return ret; | ||
} | ||
} | ||
} | ||
|
||
return IntPtr.Zero; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Loads a function pointer out of the given library by name. | ||
/// </summary> | ||
/// <param name="handle">The operating system handle of the opened shared library.</param> | ||
/// <param name="functionName">The name of the exported function to load.</param> | ||
/// <returns>A pointer to the loaded function.</returns> | ||
public IntPtr LoadFunctionPointer(IntPtr handle, string functionName) | ||
{ | ||
if (string.IsNullOrEmpty(functionName)) | ||
{ | ||
throw new ArgumentException("Parameter must not be null or empty.", nameof(functionName)); | ||
} | ||
|
||
return CoreLoadFunctionPointer(handle, functionName); | ||
} | ||
|
||
/// <summary> | ||
/// Frees the library represented by the given operating system handle. | ||
/// </summary> | ||
/// <param name="handle">The handle of the open shared library.</param> | ||
public void FreeNativeLibrary(IntPtr handle) | ||
{ | ||
if (handle == IntPtr.Zero) | ||
{ | ||
throw new ArgumentException("Parameter must not be zero.", nameof(handle)); | ||
} | ||
|
||
CoreFreeNativeLibrary(handle); | ||
} | ||
|
||
/// <summary> | ||
/// Loads a native library by name and returns an operating system handle to it. | ||
/// </summary> | ||
/// <param name="name">The name of the library to open. This parameter must not be null or empty.</param> | ||
/// <returns>The operating system handle for the shared library. | ||
/// If the library cannot be loaded, IntPtr.Zero should be returned.</returns> | ||
protected abstract IntPtr CoreLoadNativeLibrary(string name); | ||
|
||
/// <summary> | ||
/// Frees the library represented by the given operating system handle. | ||
/// </summary> | ||
/// <param name="handle">The handle of the open shared library. This must not be zero.</param> | ||
protected abstract void CoreFreeNativeLibrary(IntPtr handle); | ||
|
||
/// <summary> | ||
/// Loads a function pointer out of the given library by name. | ||
/// </summary> | ||
/// <param name="handle">The operating system handle of the opened shared library. This must not be zero.</param> | ||
/// <param name="functionName">The name of the exported function to load. This must not be null or empty.</param> | ||
/// <returns>A pointer to the loaded function.</returns> | ||
protected abstract IntPtr CoreLoadFunctionPointer(IntPtr handle, string functionName); | ||
|
||
/// <summary> | ||
/// Returns a default library loader for the running operating system. | ||
/// </summary> | ||
/// <returns>A LibraryLoader suitable for loading libraries.</returns> | ||
public static LibraryLoader GetPlatformDefaultLoader() | ||
{ | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
{ | ||
return new Win32LibraryLoader(); | ||
} | ||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||
{ | ||
return new LinuxLibraryLoader(); | ||
} | ||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | ||
{ | ||
return new OsxLibraryLoader(); | ||
} | ||
|
||
throw new PlatformNotSupportedException("This platform cannot load native libraries."); | ||
} | ||
|
||
private class Win32LibraryLoader : LibraryLoader | ||
{ | ||
protected override void CoreFreeNativeLibrary(IntPtr handle) | ||
{ | ||
Kernel32.FreeLibrary(handle); | ||
} | ||
|
||
protected override IntPtr CoreLoadFunctionPointer(IntPtr handle, string functionName) | ||
{ | ||
return Kernel32.GetProcAddress(handle, functionName); | ||
} | ||
|
||
protected override IntPtr CoreLoadNativeLibrary(string name) | ||
{ | ||
return Kernel32.LoadLibrary(name); | ||
} | ||
} | ||
|
||
private class LinuxLibraryLoader : LibraryLoader | ||
{ | ||
protected override void CoreFreeNativeLibrary(IntPtr handle) | ||
{ | ||
libdl_linux.dlclose(handle); | ||
} | ||
|
||
protected override IntPtr CoreLoadFunctionPointer(IntPtr handle, string functionName) | ||
{ | ||
return libdl_linux.dlsym(handle, functionName); | ||
} | ||
|
||
protected override IntPtr CoreLoadNativeLibrary(string name) | ||
{ | ||
return libdl_linux.dlopen(name, libdl_linux.RTLD_NOW); | ||
} | ||
} | ||
|
||
private class OsxLibraryLoader : LibraryLoader | ||
{ | ||
protected override void CoreFreeNativeLibrary(IntPtr handle) | ||
{ | ||
libdl_osx.dlclose(handle); | ||
} | ||
|
||
protected override IntPtr CoreLoadFunctionPointer(IntPtr handle, string functionName) | ||
{ | ||
return libdl_osx.dlsym(handle, functionName); | ||
} | ||
|
||
protected override IntPtr CoreLoadNativeLibrary(string name) | ||
{ | ||
return libdl_osx.dlopen(name, libdl_linux.RTLD_NOW); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace NativeLibraryLoader | ||
{ | ||
/// <summary> | ||
/// Represents a native shared library opened by the operating system. | ||
/// This type can be used to load native function pointers by name. | ||
/// </summary> | ||
public class NativeLibrary : IDisposable | ||
{ | ||
private static readonly LibraryLoader s_platformDefaultLoader = LibraryLoader.GetPlatformDefaultLoader(); | ||
private readonly LibraryLoader _loader; | ||
|
||
/// <summary> | ||
/// The operating system handle of the loaded library. | ||
/// </summary> | ||
public IntPtr Handle { get; } | ||
|
||
/// <summary> | ||
/// Constructs a new NativeLibrary using the platform's default library loader. | ||
/// </summary> | ||
/// <param name="name">The name of the library to load.</param> | ||
public NativeLibrary(string name) : this(name, s_platformDefaultLoader, PathResolver.Default) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Constructs a new NativeLibrary using the platform's default library loader. | ||
/// </summary> | ||
/// <param name="names">An ordered list of names to attempt to load.</param> | ||
public NativeLibrary(string[] names) : this(names, s_platformDefaultLoader, PathResolver.Default) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Constructs a new NativeLibrary using the specified library loader. | ||
/// </summary> | ||
/// <param name="name">The name of the library to load.</param> | ||
/// <param name="loader">The loader used to open and close the library, and to load function pointers.</param> | ||
public NativeLibrary(string name, LibraryLoader loader) : this(name, loader, PathResolver.Default) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Constructs a new NativeLibrary using the specified library loader. | ||
/// </summary> | ||
/// <param name="names">An ordered list of names to attempt to load.</param> | ||
/// <param name="loader">The loader used to open and close the library, and to load function pointers.</param> | ||
public NativeLibrary(string[] names, LibraryLoader loader) : this(names, loader, PathResolver.Default) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Constructs a new NativeLibrary using the specified library loader. | ||
/// </summary> | ||
/// <param name="name">The name of the library to load.</param> | ||
/// <param name="loader">The loader used to open and close the library, and to load function pointers.</param> | ||
/// <param name="pathResolver">The path resolver, used to identify possible load targets for the library.</param> | ||
public NativeLibrary(string name, LibraryLoader loader, PathResolver pathResolver) | ||
{ | ||
_loader = loader; | ||
Handle = _loader.LoadNativeLibrary(name, pathResolver); | ||
} | ||
|
||
/// <summary> | ||
/// Constructs a new NativeLibrary using the specified library loader. | ||
/// </summary> | ||
/// <param name="names">An ordered list of names to attempt to load.</param> | ||
/// <param name="loader">The loader used to open and close the library, and to load function pointers.</param> | ||
/// <param name="pathResolver">The path resolver, used to identify possible load targets for the library.</param> | ||
public NativeLibrary(string[] names, LibraryLoader loader, PathResolver pathResolver) | ||
{ | ||
_loader = loader; | ||
Handle = _loader.LoadNativeLibrary(names, pathResolver); | ||
} | ||
|
||
/// <summary> | ||
/// Loads a function whose signature matches the given delegate type's signature. | ||
/// </summary> | ||
/// <typeparam name="T">The type of delegate to return.</typeparam> | ||
/// <param name="name">The name of the native export.</param> | ||
/// <returns>A delegate wrapping the native function.</returns> | ||
/// <exception cref="InvalidOperationException">Thrown when no function with the given name | ||
/// is exported from the native library.</exception> | ||
public T LoadFunction<T>(string name) | ||
{ | ||
IntPtr functionPtr = _loader.LoadFunctionPointer(Handle, name); | ||
if (functionPtr == IntPtr.Zero) | ||
{ | ||
throw new InvalidOperationException($"No function was found with the name {name}."); | ||
} | ||
|
||
return Marshal.GetDelegateForFunctionPointer<T>(functionPtr); | ||
} | ||
|
||
/// <summary> | ||
/// Loads a function pointer with the given name. | ||
/// </summary> | ||
/// <param name="name">The name of the native export.</param> | ||
/// <returns>A function pointer for the given name, or 0 if no function with that name exists.</returns> | ||
public IntPtr LoadFunction(string name) | ||
{ | ||
return _loader.LoadFunctionPointer(Handle, name); | ||
} | ||
|
||
/// <summary> | ||
/// Frees the native library. Function pointers retrieved from this library will be void. | ||
/// </summary> | ||
public void Dispose() | ||
{ | ||
_loader.FreeNativeLibrary(Handle); | ||
} | ||
} | ||
} |
Oops, something went wrong.