Načítání nativní knihovny

Tento článek vysvětluje, které cesty modul runtime prohledává při načítání nativních knihoven prostřednictvím P/Invoke. Také ukazuje, jak používat SetDllImportResolver.

Varianty názvů knihoven

Pro usnadnění jednoduššího kódu P/Invoke pro různé platformy modul runtime přidá do názvů nativních knihoven kanonické rozšíření sdílené knihovny (.dll.sonebo.dylib). Na unixových platformách se běhové prostředí také pokusí o předřazení lib. Tyto varianty názvů knihoven se automaticky prohledávají při použití rozhraní API, která načítají nativní knihovny, například DllImportAttribute.

Poznámka:

Absolutní cesty v názvech knihoven (například /usr/lib/libc.so) se považují za dané a nebudou hledány žádné varianty.

Zvažte následující příklad použití P/Invoke:

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

Při spuštění ve Windows se knihovna DLL vyhledá v následujícím pořadí:

  1. nativedep
  2. nativedep.dll (pokud název knihovny nekončí na .dll nebo na .exe)

Při spuštění v Linuxu nebo macOS se runtime pokusí o předřazení lib a přidání kanonického rozšíření sdílené knihovny. V těchto operačních systémech se varianty názvů knihoven zkouší v následujícím pořadí:

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

V Linuxu se pořadí hledání liší, pokud název knihovny končí .so nebo obsahuje .so. (všimněte si koncového .). Představte si následující příklad:

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

V tomto případě se varianty názvů knihoven zkouší v následujícím pořadí:

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

1 Cesta je zaškrtnutá pouze v případě, že název knihovny neobsahuje znak oddělovače adresáře (/).

Vlastní importní resolver

Ve složitějších scénářích můžete použít SetDllImportResolver k řešení importů DLL v běhu programu. V následujícím příkladu se nativedep změní na nativedep_avx2, pokud ho procesor podporuje.

Návod

Tato funkce je dostupná pouze v .NET Core 3.1 a .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;
        }
    }
}

Viz také