本机库加载
本文介绍在通过 P/Invoke 加载本机库时运行时搜索哪些路径。 它还演示如何使用 SetDllImportResolver。
库名称变体
为了帮助简化跨平台 P/Invoke 代码,运行时将规范共享库扩展(.dll
、.so
或 .dylib
)添加到本机库名称。 在基于 Unix 的平台上,运行时还将尝试在前面追加 lib
。 使用加载本机库(例如 DllImportAttribute)的 API 时,会自动搜索这些库名称变体。
注意
库名称中的绝对路径(例如 /usr/lib/libc.so
)按原样处理,不会搜索任何变体。
请考虑以下使用 P/Invoke 的示例:
[DllImport("nativedep")]
static extern int ExportedFunction();
在 Windows 上运行时,将按以下顺序搜索 DLL:
nativedep
nativedep.dll
(如果库名称尚未以.dll
或 .exe
结尾)
在 Linux 或 macOS 上运行时,运行时将尝试在 lib
前添加,并追加规范共享库扩展。 在这些 OS 上,按以下顺序尝试库名称变体:
nativedep.so
/nativedep.dylib
libnativedep.so
/libnativedep.dylib
1nativedep
libnativedep
1
在 Linux 上,如果库名称以 .so
结尾或包含 .so.
(注意尾随 .
),则搜索顺序会有所不同。 请考虑以下示例:
[DllImport("nativedep.so.6")]
static extern int ExportedFunction();
在这种情况下,将按以下顺序尝试库名称变体:
nativedep.so.6
libnativedep.so.6
1nativedep.so.6.so
libnativedep.so.6.so
1
1 仅当库名称不包含目录分隔符 (/
) 时才检查路径。
自定义导入解析程序
在较复杂的方案中,可以使用 SetDllImportResolver 在运行时解析 DLL 导入。 在下面的示例中,nativedep
解析为 nativedep_avx2
(如果 CPU 支持)。
提示
此功能仅在 .NET Core 3.1 和 .NET 5+ 中可用。
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace PInvokeSamples
{
public static partial class Program
{
[LibraryImport("nativedep")]
private static partial int ExportedFunction();
public static void Main(string[] args)
{
// Register the import resolver before calling the imported function.
// Only one import resolver can be set for a given assembly.
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), DllImportResolver);
int value = ExportedFunction();
Console.WriteLine(value);
}
private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
if (libraryName == "nativedep")
{
// On systems with AVX2 support, load a different library.
if (System.Runtime.Intrinsics.X86.Avx2.IsSupported)
{
return NativeLibrary.Load("nativedep_avx2", assembly, searchPath);
}
}
// Otherwise, fallback to default import resolver.
return IntPtr.Zero;
}
}
}