本机代码与本机 AOT 的互操作

本机代码互操作是一种技术,可用于从托管代码访问非托管库,或向非托管代码公开托管库(相反方向)。

虽然本机代码互操作在本机 AOT 和非 AOT 部署中的工作方式类似,但在作为本机 AOT 发布时,存在一些不同之处。

直接 P/Invoke 调用

默认情况下,AOT 编译的二进制文件中的 P/Invoke 调用在运行时延迟绑定,以提高兼容性。 可以将 AOT 编译器配置为对所选 P/Invoke 方法生成直接调用,这些方法在启动期间由操作系统附带的动态加载程序绑定。 通过直接调用引用的非托管库和入口点必须在运行时始终可用,否则本机二进制文件将无法启动。

直接 P/Invoke 调用的优点包括:

  • 它们具有 更好的稳定状态性能
  • 它们使 非托管依赖项可以静态链接

可以使用项目文件中的 <DirectPInvoke> 项配置直接 P/Invoke 生成。 项名称可以是 <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 上,本机 AOT 使用预填充的直接 P/Invoke 方法列表,这些方法在所有受支持的 Windows 版本上都可用。

警告

由于直接 P/Invoke 方法由操作系统动态加载程序解析,而不是由本机 AOT 运行时库解析,因此直接 P/Invoke 方法不遵循 DefaultDllImportSearchPathsAttribute。 库搜索顺序将遵循作系统定义的动态加载程序规则。 某些作系统和加载程序提供了通过链接器标志(例如 /DEPENDENTLOADFLAG 在 Windows 或 -rpath Linux 上)控制动态加载的方法。 有关如何指定链接器标志的详细信息,请参阅 “链接 ”部分。

链接

若要静态链接到非托管库,需要指定 <NativeLibrary Include="filename" /> 指向 Windows 系统上的 .lib 文件和 Unix 类似系统上的 .a 文件。

例子:

<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 示例