使用 P/invoke 而不使用 Assembly.Load 或类似的调用托管 C# 库

Call a managed C# library with P/invoke without Assembly.Load or similar

我已经找了好几天了,但我只找到了如何使用 P/Invoke 从 C# 调用 unmanaged 库。我需要做不同的事情:我正在寻找使用 P/Invoke 从另一个调用 managed 程序集(或使用其他东西,avoiding 调用 Assembly.LoadAssembly.LoadFrom 等)主要是由于 CoreRT/NativeAOT 限制(参见 here)。

基本上,使用 CoreRT/NativeAOT 的想法是由于生成本机可执行文件,这会提高我的应用程序的安全性,因为普通反编译器无法使用它(IDA 和使用 ASM 的聪明开发人员除外)知识,但它们更难获得)。考虑到 CoreRT/NativeAOT 不能(it can,但 .NET 团队现在不想...)使用任何 .NET Interop 方法加载外部程序集(Assembly.LoadAssembly.LoadFrom, 等)但它可以使用 DllImport,我想调用一个外部程序集,如果不使用任何这些程序集加载方法,我不太关心它是否被反编译。

是的,我知道我可以用 CoreRT 本身编写一个包装器或其他东西来生成本机库并从应用程序中使用 P/Invoke 调用它,但在 Entity Framework 的情况下它不会由于 Reflection.Emit,无法编译。

这里的理想解决方案是了解如何在不使用程序集的情况下从另一个 C# app/assembly 调用任何 .NET 程序集 (DLL)。Load/LoadFrom/LoadFromStream/etc。使用其他方法,可以是 P/Invoke(可以吗?)或其他方法。

解决方案

感谢@ChristianHeld 建议使用 .NET 5.0 的本机导出(还要感谢 Aaron Robinson 的代码。我是这样工作的:

.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    ADD THIS -> <EnableDynamicLoading>true</EnableDynamicLoading>
    ADD THIS -> <DnneAddGeneratedBinaryToProject>true</DnneAddGeneratedBinaryToProject>
  </PropertyGroup>

  <ItemGroup>
    ADD THIS -> <PackageReference Include="DNNE" Version="1.*" />
  </ItemGroup>

</Project>

库 .NET 代码(剥离):

[UnmanagedCallersOnlyAttribute]
public static void Init()
{
    // ... Some code here...
}

调用方 .NET 代码(示例):

class Program
{
    const string LIBNAME = @"LibraryNE.dll";
    [DllImport(LIBNAME)] public static extern void Init();

    static void Main(string[] args)
    {
        Init();
    }
}

重要提示: 当 运行 VSCode (F5) 时,它可能会这样说:

Failed to initialize context for config: C:\Project\Library.runtimeconfig.json. Error code: 0x80008092

忽略这个,因为它似乎发生了,因为 VSCode 使用 dotnet.exe 调试应用程序,忽略每个程序集的 runtimeconfig.json(它以某种方式抛出 LibHostInvalidArgs。不知道为什么).

如果您直接执行应用程序的 .EXE,它将起作用(只是将其与 CoreRT/NativeAOT-generated 二进制文件一起使用)。解决方法是编辑 launch.json 文件并在 configurations -> program.

中将 .dll 更改为 .exe

此外,请记住也复制 .NET dll。发布时好像只复制了原生库(后缀为NE),但它本身不会工作,因为它只是一个包装器。它也需要真正的 .NET dll 位于同一文件夹中。

调试体验不是很好,因为当您调试方法时,它似乎使用了不同的、不存在的源文件,因此您单步执行时的行号不匹配。它是可调试的,但您将针对 ghost/offset 行进行调试。