验证中...
LibraryLoader.cs
Raw Copy
#define NET45
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
namespace Test
{
public delegate IntPtr GetOrLoadLibrary(string libraryName);
public static partial class ffmpeg
{
private static readonly object SyncRoot = new object();
public static readonly Dictionary<string, List<string>> LibraryDependenciesMap = new Dictionary<string, List<string>>
{
{"avcodec", new List<string> {"avutil", "swresample"}},
{"avdevice", new List<string> {"avcodec", "avfilter", "avformat", "avutil"}},
{"avfilter", new List<string> {"avcodec", "avformat", "avutil", "postproc", "swresample", "swscale"}},
{"avformat", new List<string> {"avcodec", "avutil"}},
{"avutil", new List<string>()},
{"postproc", new List<string> {"avutil"}},
{"swresample", new List<string> {"avutil"}},
{"swscale", new List<string> {"avutil"}}
};
public static Dictionary<string, int> LibraryVersionMap = new Dictionary<string, int>
{
{"avcodec", 58},
{"avdevice", 58},
{"avfilter", 7},
{"avformat", 58},
{"avutil", 56},
{"postproc", 55},
{"swresample", 3},
{"swscale", 5},
};
static ffmpeg()
{
var loadedLibraries = new Dictionary<string, IntPtr>();
GetOrLoadLibrary = name =>
{
if (loadedLibraries.TryGetValue(name, out var ptr)) return ptr;
lock (SyncRoot)
{
if (loadedLibraries.TryGetValue(name, out ptr)) return ptr;
ptr = LoadLibrary(name);
loadedLibraries.Add(name, ptr);
}
return ptr;
};
}
/// <summary>
/// Gets or sets the root path for loading libraries.
/// </summary>
/// <value>The root path.</value>
public static string RootPath { get; set; } = string.Empty;
public static GetOrLoadLibrary GetOrLoadLibrary { get; set; }
private static IntPtr LoadLibrary(string libraryName)
{
var dependencies = LibraryDependenciesMap[libraryName];
dependencies.ForEach(x => GetOrLoadLibrary(x));
var version = LibraryVersionMap[libraryName];
var ptr = LibraryLoader.LoadNativeLibrary(RootPath, libraryName, version);
if (ptr == IntPtr.Zero) throw new DllNotFoundException($"Unable to load DLL '{libraryName}.{version}': The specified module could not be found.");
return ptr;
}
public static T GetFunctionDelegate<T>(IntPtr libraryHandle, string functionName)
=> FunctionLoader.GetFunctionDelegate<T>(libraryHandle, functionName);
}
#region LibraryLoader
public delegate PlatformID GetPlatformId();
public delegate string GetNativeLibraryName(string libraryName, int version);
public static class LibraryLoader
{
static LibraryLoader()
{
GetPlatformId = () =>
{
#if NET45
return Environment.OSVersion.Platform;
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return PlatformID.Win32NT;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return PlatformID.Unix;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return PlatformID.MacOSX;
throw new PlatformNotSupportedException();
#endif
};
GetNativeLibraryName = (libraryName, version) =>
{
switch (GetPlatformId())
{
case PlatformID.MacOSX:
return $"lib{libraryName}.{version}.dylib";
case PlatformID.Unix:
return $"lib{libraryName}.so.{version}";
case PlatformID.Win32NT:
case PlatformID.Win32S:
case PlatformID.Win32Windows:
return $"{libraryName}-{version}.dll";
default:
throw new PlatformNotSupportedException();
}
};
}
public static GetPlatformId GetPlatformId;
public static GetNativeLibraryName GetNativeLibraryName;
/// <summary>
/// Attempts to load a native library using platform nammig convention.
/// </summary>
/// <param name="path">Path of the library.</param>
/// <param name="libraryName">Name of the library.</param>
/// <param name="version">Version of the library.</param>
/// <returns>
/// A handle to the library when found; otherwise, <see cref="IntPtr.Zero" />.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
public static IntPtr LoadNativeLibrary(string path, string libraryName, int version)
{
var nativeLibraryName = GetNativeLibraryName(libraryName, version);
var fullName = Path.Combine(path, nativeLibraryName);
return LoadNativeLibrary(fullName);
}
/// <summary>
/// Attempts to load a native library.
/// </summary>
/// <param name="libraryName">Name of the library.</param>
/// <returns>
/// A handle to the library when found; otherwise, <see cref="IntPtr.Zero" />.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
public static IntPtr LoadNativeLibrary(string libraryName)
{
switch (GetPlatformId())
{
case PlatformID.MacOSX:
return MacNativeMethods.dlopen(libraryName, MacNativeMethods.RTLD_NOW);
case PlatformID.Unix:
return LinuxNativeMethods.dlopen(libraryName, LinuxNativeMethods.RTLD_NOW);
case PlatformID.Win32NT:
case PlatformID.Win32S:
case PlatformID.Win32Windows:
return WindowsNativeMethods.LoadLibrary(libraryName);
default:
throw new PlatformNotSupportedException();
}
}
}
#endregion
#region FunctionLoader
/// <summary>
/// Supports loading functions from native libraries. Provides a more flexible alternative to P/Invoke.
/// </summary>
public static class FunctionLoader
{
/// <summary>
/// Creates a delegate which invokes a native function.
/// </summary>
/// <typeparam name="T">
/// The function delegate.
/// </typeparam>
/// <param name="nativeLibraryHandle">
/// The native library which contains the function.
/// </param>
/// <param name="functionName">
/// The name of the function for which to create the delegate.
/// </param>
/// <returns>
/// A new delegate which points to the native function.
/// </returns>
internal static T GetFunctionDelegate<T>(IntPtr nativeLibraryHandle, string functionName, bool throwOnError = true)
{
var ptr = GetFunctionPointer(nativeLibraryHandle, functionName);
if (ptr == IntPtr.Zero)
{
if (throwOnError) throw new EntryPointNotFoundException($"Could not find the entrypoint for {functionName}.");
return default(T);
}
#if NET45
return (T)(object)Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
#else
try
{
return Marshal.GetDelegateForFunctionPointer<T>(ptr);
}
catch (MarshalDirectiveException)
{
if (throwOnError)
throw;
return default(T);
}
#endif
}
private static IntPtr GetFunctionPointer(IntPtr nativeLibraryHandle, string functionName)
{
#if NET45
switch (LibraryLoader.GetPlatformId())
{
case PlatformID.MacOSX:
return MacNativeMethods.dlsym(nativeLibraryHandle, functionName);
case PlatformID.Unix:
return LinuxNativeMethods.dlsym(nativeLibraryHandle, functionName);
case PlatformID.Win32NT:
case PlatformID.Win32S:
case PlatformID.Win32Windows:
return WindowsNativeMethods.GetProcAddress(nativeLibraryHandle, functionName);
default:
throw new PlatformNotSupportedException();
}
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return LinuxNativeMethods.dlsym(nativeLibraryHandle, functionName);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return MacNativeMethods.dlsym(nativeLibraryHandle, functionName);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return WindowsNativeMethods.GetProcAddress(nativeLibraryHandle, functionName);
throw new PlatformNotSupportedException();
#endif
}
}
#endregion
#region NativeMethods
public static class MacNativeMethods
{
public const int RTLD_NOW = 0x002;
private const string Libdl = "libdl";
[DllImport(Libdl)]
public static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport(Libdl)]
public static extern IntPtr dlopen(string fileName, int flag);
}
public static class LinuxNativeMethods
{
public const int RTLD_NOW = 0x002;
private const string Libdl = "libdl.so.2";
[DllImport(Libdl)]
public static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport(Libdl)]
public static extern IntPtr dlopen(string fileName, int flag);
}
public static class WindowsNativeMethods
{
private const string Kernel32 = "kernel32";
[DllImport(Kernel32, CharSet = CharSet.Ansi, BestFitMapping = false)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
/// <summary>
/// Loads the specified module into the address space of the calling process. The specified module may cause other modules to be loaded.
/// </summary>
/// <param name="dllToLoad">
/// <para>
/// The name of the module. This can be either a library module (a <c>.dll</c> file) or an executable module (an <c>.exe</c> file).
/// The name specified is the file name of the module and is not related to the name stored in the library module itself,
/// as specified by the LIBRARY keyword in the module-definition (<c>.def</c>) file.
/// </para>
/// <para>
/// If the string specifies a full path, the function searches only that path for the module.
/// </para>
/// <para>
/// If the string specifies a relative path or a module name without a path, the function uses a standard search strategy
/// to find the module; for more information, see the Remarks.
/// </para>
/// <para>
/// If the function cannot find the module, the function fails. When specifying a path, be sure to use backslashes (<c>\</c>),
/// not forward slashes (<c>/</c>). For more information about paths, see Naming a File or Directory.
/// </para>
/// <para>
/// If the string specifies a module name without a path and the file name extension is omitted, the function appends the
/// default library extension <c>.dll</c> to the module name. To prevent the function from appending <c>.dll</c> to the module name,
/// include a trailing point character (<c>.</c>) in the module name string.
/// </para>
/// </param>
/// <returns>
/// If the function succeeds, the return value is a handle to the module.
/// If the function fails, the return value is <see cref="IntPtr.Zero"/>. To get extended error information, call
/// <see cref="Marshal.GetLastWin32Error"/>.
/// </returns>
/// <seealso href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx"/>
[DllImport(Kernel32, SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
}
#endregion
#region ICustomMarshaler char* to string
internal class ConstCharPtrMarshaler : ICustomMarshaler
{
public object MarshalNativeToManaged(IntPtr pNativeData) => Marshal.PtrToStringAnsi(pNativeData);
public IntPtr MarshalManagedToNative(object managedObj) => IntPtr.Zero;
public void CleanUpNativeData(IntPtr pNativeData)
{
}
public void CleanUpManagedData(object managedObj)
{
}
public int GetNativeDataSize() => IntPtr.Size;
private static readonly ConstCharPtrMarshaler Instance = new ConstCharPtrMarshaler();
public static ICustomMarshaler GetInstance(string cookie) => Instance;
}
#endregion
}

Comment list( 0 )

You need to Sign in for post a comment

Help Search