本机库加载

本文介绍在通过 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:

  1. nativedep
  2. nativedep.dll(如果库名称尚未以 .dll 或 .exe 结尾)

在 Linux 或 macOS 上运行时,运行时将尝试在 lib 前添加,并追加规范共享库扩展。 在这些 OS 上,按以下顺序尝试库名称变体:

  1. nativedep.so / nativedep.dylib
  2. libnativedep.so / libnativedep.dylib 1
  3. nativedep
  4. libnativedep 1

在 Linux 上,如果库名称以 .so 结尾或包含 .so.(注意尾随 .),则搜索顺序会有所不同。 请考虑以下示例:

[DllImport("nativedep.so.6")]
static extern int ExportedFunction();

在这种情况下,将按以下顺序尝试库名称变体:

  1. nativedep.so.6
  2. libnativedep.so.6 1
  3. nativedep.so.6.so
  4. 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;
        }
    }
}

另请参阅