Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье объясняется, какие пути просматривает среда выполнения при загрузке собственных библиотек с помощью P/Invoke. В нем также показано, как использовать SetDllImportResolver.
Варианты имени библиотеки
Чтобы упростить кроссплатформенный код P/Invoke, среда выполнения добавляет каноническое расширение общей библиотеки (.dll.soили.dylib) в имена собственных библиотек. На платформах под управлением Unix среда выполнения также попытается добавить в начало lib. Эти вариации имен библиотек автоматически ищутся при использовании API, которые загружают собственные библиотеки, например DllImportAttribute.
Примечание.
Абсолютные пути в именах библиотек (например, /usr/lib/libc.so) обрабатываются как есть, и никакие варианты не будут искать.
Рассмотрим следующий пример использования P/Invoke:
[DllImport("nativedep")]
static extern int ExportedFunction();
При запуске в Windows библиотека DLL ищется в следующем порядке поиска:
nativedep-
nativedep.dll(если имя библиотеки еще не заканчивается.dllили .exe)
При запуске в Linux или macOS среда выполнения попытается добавить lib перед строкой и присоединить каноническое расширение общей библиотеки. В этих операционных системах проверяются варианты имени библиотеки в следующем порядке:
nativedep.so/nativedep.dylib-
libnativedep.so/libnativedep.dylib1 nativedep-
libnativedep1
В Linux порядок поиска отличается, если имя библиотеки заканчивается .so или содержится .so. (обратите внимание на конечный .). Рассмотрим следующий пример:
[DllImport("nativedep.so.6")]
static extern int ExportedFunction();
В этом случае варианты имени библиотеки проверяются в следующем порядке:
nativedep.so.6-
libnativedep.so.61 nativedep.so.6.so-
libnativedep.so.6.so1
1 Путь проверяется только в том случае, если имя библиотеки не содержит символ разделителя каталогов (/).
Настраиваемый резолвер импорта
В более сложных сценариях можно использовать SetDllImportResolver для разрешения импорта DLL во время выполнения. В следующем примере nativedep заменяется на nativedep_avx2, если процессор это поддерживает.
Совет
Эта функция доступна только в .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;
}
}
}