Caricamento della libreria nativa

Questo articolo illustra i percorsi in cui il runtime esegue la ricerca durante il caricamento di librerie native tramite P/Invoke. Mostra inoltre come usare SetDllImportResolver.

Varianti dei nomi delle librerie

Per semplificare il codice P/Invoke multipiattaforma, il runtime aggiunge l'estensione della libreria condivisa canonica (.dll, .soo .dylib) ai nomi delle librerie native. Nelle piattaforme basate su Unix, il runtime proverà anche ad anteporre lib. Queste varianti dei nomi di libreria vengono cercate automaticamente quando si usano API che caricano librerie native, come DllImportAttribute.

Nota

I percorsi assoluti nei nomi delle librerie, ad esempio /usr/lib/libc.so, vengono considerati così come sono e non verrà eseguita alcuna ricerca di varianti.

Si consideri l'esempio seguente di utilizzo di P/Invoke:

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

Durante l'esecuzione in Windows, la DLL viene cercata nell'ordine seguente:

  1. nativedep
  2. nativedep.dll (se il nome della libreria non termina già con .dll o .exe)

Quando si esegue in Linux o macOS, il runtime tenterà di anteporre lib e di aggiungere l'estensione della libreria condivisa canonica. In questi sistemi operativi, le varianti dei nomi di libreria vengono cercati nell'ordine seguente:

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

In Linux l'ordine di ricerca è diverso se il nome della libreria termina con .so o contiene .so. (si noti l'elemento finale .). Si consideri l'esempio seguente:

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

In questo caso, le varianti dei nomi di libreria vengono cercate nell'ordine seguente:

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

1 Il percorso viene selezionato solo se il nome della libreria non contiene un carattere separatore di directory (/).

Resolver di importazione personalizzato

In scenari più complessi, è possibile usare SetDllImportResolver per risolvere le importazioni di DLL in fase di esecuzione. Nell'esempio seguente nativedep viene risolto in nativedep_avx2 se la CPU lo supporta.

Suggerimento

Questa funzionalità è disponibile solo in .NET Core 3.1 e .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;
        }
    }
}

Vedi anche