Compartilhar via


Carregamento de biblioteca nativa

Este artigo explica quais caminhos o runtime pesquisa ao carregar bibliotecas nativas por meio de P/Invoke. Este exemplo também mostra como usar SetDllImportResolver.

Variações do nome de biblioteca

Para simplificar o código P/Invoke entre plataformas, o runtime adiciona a extensão de biblioteca compartilhada canônica (.dll, .so ou .dylib) a nomes de biblioteca nativa. Em plataformas baseadas em Unix, o runtime também tentará anexar lib. Essas variações de nomes de biblioteca são pesquisadas automaticamente quando você usa APIs que carregam bibliotecas nativas, como DllImportAttribute.

Observação

Caminhos absolutos em nomes de biblioteca (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 já não terminar com .dll ou .exe)

Ao ser executado no Linux ou no macOS, o runtime tentará preceder lib e acrescentar a extensão de biblioteca compartilhada canônica. Nesses sistemas operacionais, as variações do nome de biblioteca são tentadas na seguinte ordem:

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

No Linux, a ordem de pesquisa será diferente se o nome da biblioteca terminar com .so ou contiver .so. (observe o . à direita). Considere o seguinte exemplo:

[DllImport("nativedep.so.6")]
static extern int ExportedFunction();

Nesse caso, as variações do nome de biblioteca são tentadas na seguinte ordem:

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

1 O caminho será verificado somente 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 as importações de DLL em tempo de execução. No exemplo a seguir, nativedep será resolvido como nativedep_avx2 se a CPU der suporte.

Dica

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;
        }
    }
}

Confira também