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:

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

  1. nativedep.so / nativedep.dylib
  2. libnativedep.so / libnativedep.dylib1
  3. nativedep
  4. libnativedep1

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:

  1. nativedep.so.6
  2. libnativedep.so.61
  3. nativedep.so.6.so
  4. libnativedep.so.6.so1

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 class Program
    {
        [DllImport("nativedep")]
        private static extern 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 também