using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Packaging.Targets.Native
{
///
/// Supports loading functions from native libraries. Provides a more flexible alternative to P/Invoke.
///
internal static class FunctionLoader
{
///
/// Attempts to load a native library.
///
///
/// Possible names of the library on Windows. This can include full paths.
///
///
/// Possible names of the library on Linux.
///
///
/// Possible names of the library on macOS.
///
///
/// A handle to the library when found; otherwise, .
///
public static IntPtr LoadNativeLibrary(IEnumerable windowsNames, IEnumerable linuxNames, IEnumerable osxNames)
{
IntPtr lib = IntPtr.Zero;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
foreach (var name in linuxNames)
{
lib = LinuxNativeMethods.dlopen(name, LinuxNativeMethods.RTLD_NOW);
if (lib != IntPtr.Zero)
{
break;
}
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
foreach (var name in osxNames)
{
lib = MacNativeMethods.dlopen(name, MacNativeMethods.RTLD_NOW);
if (lib != IntPtr.Zero)
{
break;
}
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
foreach (var name in windowsNames)
{
lib = WindowsNativeMethods.LoadLibrary(name);
if (lib != IntPtr.Zero)
{
break;
}
}
}
else
{
throw new PlatformNotSupportedException();
}
// This function may return a null handle. If it does, individual functions loaded from it will throw a DllNotFoundException,
// but not until an attempt is made to actually use the function (rather than load it). This matches how PInvokes behave.
return lib;
}
///
/// Creates a delegate which invokes a native function.
///
///
/// The function delegate.
///
///
/// The native library which contains the function.
///
///
/// The name of the function for which to create the delegate.
///
///
/// A new delegate which points to the native function.
///
public static T LoadFunctionDelegate(IntPtr nativeLibraryHandle, string functionName, bool throwOnError = true)
where T : class
{
IntPtr ptr = LoadFunctionPointer(nativeLibraryHandle, functionName);
if (ptr == IntPtr.Zero)
{
if (throwOnError)
{
#if NETSTANDARD2_0
throw new EntryPointNotFoundException($"Could not find the entrypoint for {functionName}");
#else
throw new Exception($"Could not find the entrypoint for {functionName}");
#endif
}
else
{
return null;
}
}
return Marshal.GetDelegateForFunctionPointer(ptr);
}
private static IntPtr LoadFunctionPointer(IntPtr nativeLibraryHandle, string functionName)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return LinuxNativeMethods.dlsym(nativeLibraryHandle, functionName);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return MacNativeMethods.dlsym(nativeLibraryHandle, functionName);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return WindowsNativeMethods.GetProcAddress(nativeLibraryHandle, functionName);
}
else
{
throw new PlatformNotSupportedException();
}
}
}
}