Поделиться через


Загрузка собственной библиотеки

В этой статье объясняется, какие пути выполняет поиск во время выполнения при загрузке собственных библиотек с помощью P/Invoke. В нем также показано, как использовать SetDllImportResolver.

Варианты имени библиотеки

Чтобы упростить кроссплатформенный код P/Invoke, среда выполнения добавляет каноническое расширение общей библиотеки (.dll.soили.dylib) в имена собственных библиотек. На платформах под управлением Unix среда выполнения также попытается при подготовке lib. Эти варианты имен библиотек автоматически выполняются при использовании API, которые загружают собственные библиотеки, например DllImportAttribute.

Примечание.

Абсолютные пути в именах библиотек (например, /usr/lib/libc.so) обрабатываются как есть, и никакие варианты не будут искать.

Рассмотрим следующий пример использования P/Invoke:

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

При запуске в Windows библиотека DLL выполняется в следующем порядке:

  1. nativedep
  2. nativedep.dll (если имя библиотеки еще не заканчивается .dll или .exe)

При запуске в Linux или macOS среда выполнения попытается принудить и lib добавить каноническое расширение общей библиотеки. В этих OSes варианты имени библиотеки выполняются в следующем порядке:

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

В Linux порядок поиска отличается, если имя библиотеки заканчивается .so или содержится .so. (обратите внимание на конечный .). Рассмотрим следующий пример:

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

В этом случае варианты имени библиотеки выполняются в следующем порядке:

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

1 Путь проверяется только в том случае, если имя библиотеки не содержит символ разделителя каталогов (/).

Настраиваемый сопоставитель импорта

В более сложных сценариях можно использовать SetDllImportResolver для разрешения импорта БИБЛИОТЕК DLL во время выполнения. В следующем примере разрешается разрешение nativedep_avx2 на то, nativedep поддерживает ли ЦП его.

Совет

Эта функция доступна только в .NET Core 3.1 и .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;
        }
    }
}

См. также