Carga de biblioteca nativa
En este artículo se explica qué rutas de acceso busca el runtime al cargar bibliotecas nativas mediante P/Invoke. También se muestra cómo usar el SetDllImportResolver.
Variaciones de nombre de biblioteca
Para que el código de P/Invoke multiplataforma sea más sencillo, el runtime agrega la extensión de biblioteca compartida canónica (.dll
, .so
o .dylib
) a los nombres de bibliotecas nativas. En las plataformas basadas en Unix, el entorno de ejecución también intentará anteponerlib
. Estas variaciones de nombre de biblioteca se buscan automáticamente cuando se usan API que cargan bibliotecas no administradas, como DllImportAttribute.
Nota:
Las rutas de acceso absolutas en los nombres de biblioteca (por ejemplo, /usr/lib/libc.so
) se tratan tal como está y no se buscará ninguna variación.
Considere el siguiente ejemplo de uso de P/Invoke:
[DllImport("nativedep")]
static extern int ExportedFunction();
Cuando se ejecuta en Windows, se busca el archivo DLL en el orden siguiente:
nativedep
nativedep.dll
(si el nombre de la biblioteca no termina con.dll
o .exe
)
Al ejecutarse en Linux o macOS, el runtime intenta anteponer lib
y anexar la extensión de biblioteca compartida canónica. En estos sistemas operativos, las variaciones de nombre de biblioteca se intentan en el orden siguiente:
nativedep.so
/nativedep.dylib
libnativedep.so
/libnativedep.dylib
1nativedep
libnativedep
1
En Linux, el orden de búsqueda es diferente si el nombre de la biblioteca termina con .so
o contiene .so.
(tenga en cuenta el .
final). Considere el ejemplo siguiente:
[DllImport("nativedep.so.6")]
static extern int ExportedFunction();
En este caso, las variaciones de nombre de biblioteca se intentan en el orden siguiente:
nativedep.so.6
libnativedep.so.6
1nativedep.so.6.so
libnativedep.so.6.so
1
1 La ruta de acceso solo se comprueba si el nombre de la biblioteca no contiene un carácter separador de directorios (/
).
Solucionador de importación personalizado
En escenarios más complejos, puede usar SetDllImportResolver para resolver importaciones de DLL en tiempo de ejecución. En el ejemplo siguiente, nativedep
se resuelve en nativedep_avx2
si la CPU lo admite.
Sugerencia
Esta funcionalidad sólo está disponible en .NET Core 3.1 y .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;
}
}
}