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