Caricamento della libreria nativa
Questo articolo illustra i percorsi in cui il runtime esegue la ricerca durante il caricamento di librerie native tramite P/Invoke. Mostra inoltre come usare SetDllImportResolver.
Varianti dei nomi delle librerie
Per semplificare il codice P/Invoke multipiattaforma, il runtime aggiunge l'estensione della libreria condivisa canonica (.dll
, .so
o .dylib
) ai nomi delle librerie native. Nelle piattaforme basate su Unix, il runtime proverà anche ad anteporre lib
. Queste varianti dei nomi di libreria vengono cercate automaticamente quando si usano API che caricano librerie native, come DllImportAttribute.
Nota
I percorsi assoluti nei nomi delle librerie, ad esempio /usr/lib/libc.so
, vengono considerati così come sono e non verrà eseguita alcuna ricerca di varianti.
Si consideri l'esempio seguente di utilizzo di P/Invoke:
[DllImport("nativedep")]
static extern int ExportedFunction();
Durante l'esecuzione in Windows, la DLL viene cercata nell'ordine seguente:
nativedep
nativedep.dll
(se il nome della libreria non termina già con.dll
o .exe
)
Quando si esegue in Linux o macOS, il runtime tenterà di anteporre lib
e di aggiungere l'estensione della libreria condivisa canonica. In questi sistemi operativi, le varianti dei nomi di libreria vengono cercati nell'ordine seguente:
nativedep.so
/nativedep.dylib
libnativedep.so
/libnativedep.dylib
1nativedep
libnativedep
1
In Linux l'ordine di ricerca è diverso se il nome della libreria termina con .so
o contiene .so.
(si noti l'elemento finale .
). Si consideri l'esempio seguente:
[DllImport("nativedep.so.6")]
static extern int ExportedFunction();
In questo caso, le varianti dei nomi di libreria vengono cercate nell'ordine seguente:
nativedep.so.6
libnativedep.so.6
1nativedep.so.6.so
libnativedep.so.6.so
1
1 Il percorso viene selezionato solo se il nome della libreria non contiene un carattere separatore di directory (/
).
Resolver di importazione personalizzato
In scenari più complessi, è possibile usare SetDllImportResolver per risolvere le importazioni di DLL in fase di esecuzione. Nell'esempio seguente nativedep
viene risolto in nativedep_avx2
se la CPU lo supporta.
Suggerimento
Questa funzionalità è disponibile solo in .NET Core 3.1 e .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;
}
}
}