本机代码互操作是一种技术,可用于从托管代码访问非托管库,或向非托管代码公开托管库(相反方向)。
虽然本机代码互操作在本机 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 示例。