首页 > 解决方案 > 使用没有 Assembly.Load 或类似的 P/invoke 调用托管 C# 库

问题描述

我已经找了好几天了,但我发现的只是如何使用 P/Invoke 从 C# 调用非托管库。我需要以不同的方式做:我希望使用 P/Invoke 从另一个调用托管程序集(或使用其他东西,避免调用Assembly.Load,Assembly.LoadFrom等),主要是由于 CoreRT/NativeAOT 限制(参见此处) .

基本上,使用 CoreRT/NativeAOT 的想法是由于原生可执行文件的生成,这将提高我的应用程序的安全性,因为常见的反编译器无法使用它(除了 IDA 和具有 ASM 知识的聪明开发人员,但他们更难过来)。考虑到 CoreRT/NativeAOT 不能(它可以,但 .NET 团队现在不想......)使用任何 .NET 互操作方法(Assembly.LoadAssembly.LoadFrom等)加载外部程序集,但它可以使用DllImport,我'想调用一个外部程序集,如果不使用任何这些程序集加载方法,它是否被反编译,我并不在意。

是的,我知道我可以用 CoreRT 本身编写一个包装器或其他东西来生成一个本机库,并从应用程序中使用 P/Invoke 调用它,但是对于由于 而无法编译的实体框架Reflection.Emit,这是不可能的。

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

标签: c#.net-corepinvoke.net-assemblycorert

解决方案


解决方案

感谢@ChristianHeld 建议使用 .NET 5.0 的 Native Exports(还要感谢 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 生成的二进制文件一起使用)。对此的解决方案是编辑您的launch.json文件并.dll使用.exein 进行更改configurations -> program

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

调试体验不是很好,因为当您调试该方法时,它似乎使用不同的、不存在的源文件,因此单步执行时的行号不匹配。它是可调试的,但您将针对幽灵/偏移线这样做。


推荐阅读