Skip to content

Commit

Permalink
changed NativeLibraryLoader from proj ref to internal code due to dot…
Browse files Browse the repository at this point in the history
…net pack limitations
  • Loading branch information
MV10 committed Sep 23, 2018
1 parent 79021cf commit b5e2418
Show file tree
Hide file tree
Showing 8 changed files with 575 additions and 12 deletions.
8 changes: 1 addition & 7 deletions dotnet-curses.sln
Expand Up @@ -7,9 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-curses", "dotnet-cur
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sample-fireworks", "sample-fireworks\sample-fireworks.csproj", "{0D898BD9-91CF-45C9-ACE7-B42F062094F4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeLibraryLoader", "NativeLibraryLoader\NativeLibraryLoader.csproj", "{F15EF57E-BA2D-4F71-9ED1-04284D36A75C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sample-mouse", "sample-mouse\sample-mouse.csproj", "{8B237AEF-6B72-4223-9A9B-0A9F54A17AFF}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sample-mouse", "sample-mouse\sample-mouse.csproj", "{8B237AEF-6B72-4223-9A9B-0A9F54A17AFF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -25,10 +23,6 @@ Global
{0D898BD9-91CF-45C9-ACE7-B42F062094F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D898BD9-91CF-45C9-ACE7-B42F062094F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D898BD9-91CF-45C9-ACE7-B42F062094F4}.Release|Any CPU.Build.0 = Release|Any CPU
{F15EF57E-BA2D-4F71-9ED1-04284D36A75C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F15EF57E-BA2D-4F71-9ED1-04284D36A75C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F15EF57E-BA2D-4F71-9ED1-04284D36A75C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F15EF57E-BA2D-4F71-9ED1-04284D36A75C}.Release|Any CPU.Build.0 = Release|Any CPU
{8B237AEF-6B72-4223-9A9B-0A9F54A17AFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B237AEF-6B72-4223-9A9B-0A9F54A17AFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B237AEF-6B72-4223-9A9B-0A9F54A17AFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down
17 changes: 17 additions & 0 deletions dotnet-curses/NativeLibraryLoader/Kernel32.cs
@@ -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);
}
}
240 changes: 240 additions & 0 deletions dotnet-curses/NativeLibraryLoader/LibraryLoader.cs
@@ -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);
}
}
}
}
115 changes: 115 additions & 0 deletions dotnet-curses/NativeLibraryLoader/NativeLibrary.cs
@@ -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);
}
}
}

0 comments on commit b5e2418

Please sign in to comment.