Compartir a través de


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:

  1. nativedep
  2. 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:

  1. nativedep.so / nativedep.dylib
  2. libnativedep.so / libnativedep.dylib 1
  3. nativedep
  4. 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:

  1. nativedep.so.6
  2. libnativedep.so.6 1
  3. nativedep.so.6.so
  4. 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;
        }
    }
}

Consulte también