Carregamento de biblioteca nativa
Este artigo explica quais caminhos o tempo de execução pesquisa ao carregar bibliotecas nativas via P/Invoke. Ele também mostra como usar SetDllImportResolvero .
Variações de nomes de bibliotecas
Para facilitar o código P/Invoke multiplataforma mais simples, o tempo de execução adiciona a extensão canônica de biblioteca compartilhada (.dll
, .so
ou .dylib
) aos nomes de bibliotecas nativas. Em plataformas baseadas em Unix, o tempo de execução também tentará antecipar lib
. Essas variações de nomes de bibliotecas são pesquisadas automaticamente quando você usa APIs que carregam bibliotecas nativas, como DllImportAttribute.
Nota
Caminhos absolutos em nomes de bibliotecas (por exemplo, /usr/lib/libc.so
) são tratados como estão e nenhuma variação será pesquisada.
Considere o seguinte exemplo de uso de P/Invoke:
[DllImport("nativedep")]
static extern int ExportedFunction();
Ao executar no Windows, a DLL é pesquisada na seguinte ordem:
nativedep
nativedep.dll
(se o nome da biblioteca ainda não terminar com.dll
ou .exe
)
Quando executado em Linux ou macOS, o tempo de execução tentará antecipar lib
e anexar a extensão canônica de biblioteca compartilhada. Nesses sistemas operacionais, as variações de nome da biblioteca são tentadas na seguinte ordem:
nativedep.so
/nativedep.dylib
libnativedep.so
/libnativedep.dylib
1ºnativedep
libnativedep
1º
No Linux, a ordem de pesquisa é diferente se o nome da biblioteca terminar com .so
ou contiver .so.
(observe o trailing .
). Considere o seguinte exemplo:
[DllImport("nativedep.so.6")]
static extern int ExportedFunction();
Nesse caso, as variações de nome da biblioteca são tentadas na seguinte ordem:
nativedep.so.6
libnativedep.so.6
1ºnativedep.so.6.so
libnativedep.so.6.so
1º
1 O caminho só será verificado se o nome da biblioteca não contiver um caractere separador de diretório (/
).
Resolvedor de importação personalizado
Em cenários mais complexos, você pode usar SetDllImportResolver para resolver importações de DLL em tempo de execução. No exemplo a seguir, nativedep
é resolvido se a CPU oferece suporte a nativedep_avx2
ele.
Gorjeta
Essa funcionalidade só está disponível no .NET Core 3.1 e no .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;
}
}
}