Teilen über


Laden der nativen Bibliothek

In diesem Artikel wird erläutert, welche Pfade die Runtime beim Laden nativer Bibliotheken über P/Invoke durchsucht. Es wird ferner gezeigt, wie Sie SetDllImportResolver verwenden.

Variationen von Bibliotheksnamen

Um einfacheren plattformübergreifenden P/Invoke-Code zu fördern, fügt die Runtime nativen Bibliotheksnamen die kanonische freigegebene Bibliothekserweiterung (.dll, .so oder .dylib) hinzu. Auf Unix-basierten Plattformen wird die Laufzeit auch versuchen, lib voranzustellen. Nach diesen Bibliotheksnamenvariationen wird automatisch gesucht, wenn Sie APIs verwenden, die native Bibliotheken laden, z. B. DllImportAttribute.

Hinweis

Absolute Pfade in Bibliotheksnamen (zum Beispiel /usr/lib/libc.so) werden unverändert übernommen, und es werden keine Variationen gesucht.

Betrachten Sie das folgende Beispiel für die Verwendung von P/Invoke:

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

Bei der Ausführung unter Windows wird die DLL in der folgenden Reihenfolge gesucht:

  1. nativedep
  2. nativedep.dll (wenn der Bibliotheksname noch nicht auf .dll oder exe endet)

Wenn die Runtime unter Linux oder macOS ausgeführt wird, versucht sie, lib voranzustellen und die kanonische freigegebene Bibliothekserweiterung anzufügen. Unter diesen Betriebssystemen werden Variationen von Bibliotheksnamen in der folgenden Reihenfolge ausprobiert:

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

Unter Linux unterscheidet sich die Suchreihenfolge, wenn der Bibliotheksname auf .so oder .so. endet (beachten Sie den nachfolgenden .). Betrachten Sie das folgende Beispiel:

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

In diesem Fall werden die Variationen von Bibliotheksnamen in der folgenden Reihenfolge ausprobiert:

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

1 Der Pfad wird nur überprüft, wenn der Bibliotheksname kein Verzeichnistrennzeichen (/) enthält.

Benutzerdefinierter Importauflöser

In komplexeren Szenarien können Sie SetDllImportResolver verwenden, um DLL-Importe zur Laufzeit aufzulösen. Im folgenden Beispiel wird nativedep in nativedep_avx2 aufgelöst, wenn die CPU dies unterstützt.

Tipp

Diese Funktionalität ist nur in .NET Core 3.1 und .NET 5+ verfügbar.

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

Siehe auch