Ładowanie biblioteki natywnej
W tym artykule wyjaśniono, które ścieżki środowiska uruchomieniowego wyszukują podczas ładowania bibliotek natywnych za pośrednictwem protokołu P/Invoke. Pokazano również, jak używać polecenia SetDllImportResolver.
Odmiany nazw biblioteki
Aby ułatwić prostszy międzyplatformowy kod P/Invoke, środowisko uruchomieniowe dodaje rozszerzenie biblioteki udostępnionej kanonicznej (.dll
.so
lub .dylib
) do nazw bibliotek natywnych. Na platformach opartych na systemie Unix środowisko uruchomieniowe spróbuje również wstępnie utworzyć lib
element . Te odmiany nazw bibliotek są automatycznie przeszukiwane podczas korzystania z interfejsów API ładujących biblioteki natywne, takie jak DllImportAttribute.
Uwaga
Ścieżki bezwzględne w nazwach bibliotek (na przykład /usr/lib/libc.so
) są traktowane jako i nie będą przeszukiwane żadne odmiany.
Rozważmy następujący przykład użycia funkcji P/Invoke:
[DllImport("nativedep")]
static extern int ExportedFunction();
Podczas uruchamiania w systemie Windows biblioteka DLL jest wyszukiwana w następującej kolejności:
nativedep
nativedep.dll
(jeśli nazwa biblioteki nie kończy się jeszcze ciągiem.dll
lub ).exe
Podczas uruchamiania w systemie Linux lub macOS środowisko uruchomieniowe spróbuje wstępnie dołączyć lib
rozszerzenie biblioteki udostępnionej kanonicznej. W tych systemach operacyjnych odmiany nazw bibliotek są wypróbowane w następującej kolejności:
nativedep.so
/nativedep.dylib
libnativedep.so
/libnativedep.dylib
1nativedep
libnativedep
1
W systemie Linux kolejność wyszukiwania różni się, jeśli nazwa biblioteki kończy się ciągiem .so
lub zawiera .so.
(zwróć uwagę na końcowy .
ciąg ). Rozważmy następujący przykład:
[DllImport("nativedep.so.6")]
static extern int ExportedFunction();
W takim przypadku odmiany nazw biblioteki są sprawdzane w następującej kolejności:
nativedep.so.6
libnativedep.so.6
1nativedep.so.6.so
libnativedep.so.6.so
1
1 Ścieżka jest sprawdzana tylko wtedy, gdy nazwa biblioteki nie zawiera znaku separatora katalogu (/
).
Niestandardowy moduł rozpoznawania importu
W bardziej złożonych scenariuszach można użyć polecenia , aby rozwiązać problemy z SetDllImportResolver importowaniem bibliotek DLL w czasie wykonywania. W poniższym przykładzie jest rozpoznawana wartość nativedep_avx2
, nativedep
jeśli procesor cpu go obsługuje.
Napiwek
Ta funkcja jest dostępna tylko w programach .NET Core 3.1 i .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;
}
}
}