.NET 6.0中UnmanagedExports、DllExport及NativeAOT失效问题求助
问题描述
原基于.NET Framework的应用使用Robert Giesecke的UnmanagedExport工具,通过[DllExport]特性向原生语言导出函数,运行正常。升级至.NET 6.0后出现以下问题:
- 函数可通过
dumpbin查看到导出信息,原生程序能加载DLL并找到函数地址,但调用时抛出异常:Unhandled exception at 0x00007FFB928CCD29 (KernelBase.dll) in NativeExportTest.exe: 0xE0434352 - 切换回.NET Framework或.NET Standard则恢复正常
- 已尝试
UnmanagedExports.Repack、UnmanagedExports.Repack.Upgrade、DllExport等包,均无效 - NativeAOT虽能运行,但不支持.NET 6.0的全部特性
- .NET 6.0的
[UnmanagedCallersOnlyAttribute]因目标原生语言为运行时编译的C++,疑似不支持回调 - 启用混合调试后,加载前出现新异常:
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified
相关代码
C#项目文件
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0-windows</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <Platforms>AnyCPU;x64</Platforms> </PropertyGroup> <ItemGroup> <PackageReference Include="UnmanagedExports.Repack.Upgrade" Version="1.2.1" /> </ItemGroup> </Project>
C#导出类
public class Class1 { [DllExport] public static int _add(int a, int b) { return a + b; } }
C++测试代码
const TCHAR* pemodule = _T("path to release dll"); HMODULE lib = LoadLibrary(pemodule); typedef int( *_add)(int a, int b); auto pAdd = (_add)GetProcAddress(lib, "_add"); int c = pAdd(1,2); // 此处抛出异常
解决方案
方案1:使用NetDllExport适配.NET 6+
这是专门为.NET Core/.NET 5+设计的DLL导出工具,替代旧版UnmanagedExport:
- 安装NuGet包
NetDllExport(选择适配.NET 6的最新版本) - 修改项目文件指定平台和运行时标识符,确保依赖正确复制
- 调整导出特性的调用约定,匹配原生程序需求
修改后的C#项目文件
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0-windows</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <Platforms>x64</Platforms> <!-- 避免AnyCPU位数不匹配问题 --> <RuntimeIdentifier>win-x64</RuntimeIdentifier> <!-- 确保复制运行时依赖 --> <SelfContained>false</SelfContained> <!-- 按需设置,true则打包所有依赖 --> <DllExportCallingConvention>Cdecl</DllExportCallingConvention> <!-- 统一调用约定 --> </PropertyGroup> <ItemGroup> <PackageReference Include="NetDllExport" Version="1.0.11" /> </ItemGroup> </Project>
修改后的C#导出代码
using NetDllExport; public class Class1 { [DllExport("_add", CallingConvention = CallingConvention.Cdecl)] public static int Add(int a, int b) { return a + b; } }
方案2:修复System.Runtime加载问题(适配旧方案)
若坚持使用旧版UnmanagedExport衍生包,需解决依赖加载问题:
- 放弃AnyCPU编译,指定与原生程序一致的平台(x64/x86)
- 在项目文件中添加
<RuntimeIdentifier>win-x64</RuntimeIdentifier>,确保编译时复制所有.NET运行时依赖到输出目录 - 非自包含部署时,确保目标机器已安装.NET 6 Desktop Runtime(Windows平台)
- 检查输出目录,若缺失
System.Runtime.dll等依赖,手动从.NET安装目录复制
方案3:COM互操作替代(无需回调场景)
如果原生程序支持COM调用,可将C#类注册为COM组件:
- 在C#类上标记
[ComVisible(true)]并生成唯一GUID - 调试时启用项目属性的"Register for COM Interop",或用
regasm工具注册组件 - 原生C++通过COM接口调用方法
C# COM代码示例
using System.Runtime.InteropServices; [ComVisible(true)] [Guid("F89D2E1A-1234-5678-90AB-CDEF01234567")] public interface ICalculator { int Add(int a, int b); } [ComVisible(true)] [Guid("F89D2E1B-1234-5678-90AB-CDEF01234567")] public class Calculator : ICalculator { public int Add(int a, int b) { return a + b; } }
C++ COM调用示例
#include <windows.h> #include <objbase.h> // 需导入tlb文件或手动声明接口GUID const IID IID_ICalculator = {0xF89D2E1A, 0x1234, 0x5678, {0x90, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}}; const CLSID CLSID_Calculator = {0xF89D2E1B, 0x1234, 0x5678, {0x90, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}}; int main() { CoInitialize(NULL); ICalculator* pCalc = nullptr; HRESULT hr = CoCreateInstance(CLSID_Calculator, NULL, CLSCTX_INPROC_SERVER, IID_ICalculator, (void**)&pCalc); if (SUCCEEDED(hr)) { int result = pCalc->Add(1, 2); pCalc->Release(); } CoUninitialize(); return 0; }
关键注意事项
- 必须保证.NET DLL与原生程序的位数一致(x64/x86),否则会出现加载或调用异常
- .NET 6+非自包含部署依赖目标机器的.NET Runtime环境,自包含部署则需打包所有依赖
- 导出函数的调用约定(Cdecl/StdCall)必须与原生程序匹配,否则会导致栈损坏或异常
内容的提问来源于stack exchange,提问作者clamchoda
相关产品推荐
相关产品推荐

