Взаимодействие нативного кода с Native AOT

Нативная интероперация — это технология, которая позволяет получать доступ к неуправляемым библиотекам из управляемого кода или делать управляемые библиотеки доступными для неуправляемого кода (в противоположном направлении).

Хотя взаимодействие с нативным кодом работает аналогично в развертываниях Native AOT и без AOT, существуют некоторые особенности, которые отличаются при публикации в качестве Native AOT.

Прямые вызовы P/Invoke

Вызовы P/Invoke в двоичных файлах, скомпилированных AOT, по умолчанию привязаны неявно во время выполнения для повышения совместимости. Компилятор AOT можно настроить для создания прямых вызовов выбранных методов P/Invoke, привязанных во время запуска динамическим загрузчиком, который поставляется с операционной системой. Неуправляемые библиотеки и точки входа, на которые ссылаются прямые вызовы, всегда должны быть доступны во время выполнения, в противном случае собственный двоичный файл не запускается.

Преимущества прямых вызовов P/Invoke:

  • Они имеют лучшую стационарную производительность.
  • Они позволяют связать неуправляемые зависимости статически.

Вы можете настроить генерацию прямого вызова P/Invoke с помощью <DirectPInvoke> объектов в файле проекта. Имя элемента может быть либо <modulename>, что позволяет прямые вызовы для всех точек входа в модуле, либо <modulename!entrypointname>, что обеспечивает прямой вызов только для конкретного модуля и точки входа.

Чтобы указать список точек входа во внешнем файле, используйте <DirectPInvokeList> элементы в файле проекта. Список полезен, если количество прямых вызовов P/Invoke большое, и это непрактично указывать их с помощью отдельных <DirectPInvoke> элементов. Файл может содержать пустые строки и комментарии, начиная с #.

Примеры.

<ItemGroup>
  <!-- Generate direct PInvoke calls for everything in __Internal -->
  <!-- This option replicates Mono AOT behavior that generates direct PInvoke calls for __Internal -->
  <DirectPInvoke Include="__Internal" />
  <!-- Generate direct PInvoke calls for everything in libc (also matches libc.so on Linux or libc.dylib on macOS) -->
  <DirectPInvoke Include="libc" />
  <!-- Generate direct PInvoke calls for Sleep in kernel32 (also matches kernel32.dll on Windows) -->
  <DirectPInvoke Include="kernel32!Sleep" />
  <!-- Generate direct PInvoke for all APIs listed in DirectXAPIs.txt -->
  <DirectPInvokeList Include="DirectXAPIs.txt" />
</ItemGroup>

В Windows Native AOT использует предварительно заполненный список прямых методов P/Invoke, доступных во всех поддерживаемых версиях Windows.

Предупреждение

Поскольку прямые методы P/Invoke разрешаются динамическим загрузчиком операционной системы, а не библиотекой среды выполнения Native AOT, прямые методы P/Invoke не будут учитывать DefaultDllImportSearchPathsAttribute. Порядок поиска библиотек будет соответствовать правилам динамического загрузчика, определенным операционной системой. Некоторые операционные системы и загрузчики предлагают способы управления динамической загрузкой с помощью флагов компоновщика (например: /DEPENDENTLOADFLAG на Windows или -rpath в Linux). Дополнительные сведения об указании флагов компоновщика см. в разделе Связывание.

Связывание

Чтобы статически связаться с неуправляемой библиотекой, необходимо указать <NativeLibrary Include="filename" /> , указывая .lib на файл в Windows и .a файл в системах, таких как Unix.

Примеры.

<ItemGroup>
  <!-- Generate direct PInvokes for Dependency -->
  <DirectPInvoke Include="Dependency" />
  <!-- Specify library to link against -->
  <NativeLibrary Include="Dependency.lib" Condition="$(RuntimeIdentifier.StartsWith('win'))" />
  <NativeLibrary Include="Dependency.a" Condition="!$(RuntimeIdentifier.StartsWith('win'))" />
</ItemGroup>

Чтобы указать дополнительные флаги для родного компоновщика, используйте <LinkerArg> элемент.

Примеры.

<ItemGroup>
  <!-- link.exe is used as the linker on Windows -->
  <LinkerArg Include="/DEPENDENTLOADFLAG:0x800" Condition="$(RuntimeIdentifier.StartsWith('win'))" />

  <!-- Native AOT invokes clang/gcc as the linker, so arguments need to be prefixed with "-Wl," -->
  <LinkerArg Include="-Wl,-rpath,'/bin/'" Condition="$(RuntimeIdentifier.StartsWith('linux'))" />
</ItemGroup>

Собственные экспорты

Собственный компилятор AOT экспортирует методы, аннотированные с UnmanagedCallersOnlyAttribute и с непустым свойством EntryPoint в качестве общедоступных точек входа C. Это позволяет динамически или статически связывать скомпилированные модули AOT с внешними программами. Рассматриваются только методы, помеченные UnmanagedCallersOnly в опубликованной сборке. Методы в ссылках на проекты или пакетах NuGet не будут экспортированы. Дополнительные сведения см. в примере NativeLibrary.