相关文章推荐
纯真的石榴  ·  React 项目中使用 ...·  2 年前    · 
博学的墨镜  ·  DBT中文社区 - 知乎·  2 年前    · 

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

虽然本机代码互操作在本机 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 示例