c++ 直接调用 c# dll 失败,因原生 c++ 无法识别托管 il 字节码且未加载 clr;需用 /clr 编译的 c++/cli 作为桥梁,或通过 extern "c" 导出函数间接调用。

为什么直接用 C++ 调用 C# DLL 会失败
因为原生 C++(即 Win32 或 MSVC 编译的 cl.exe 默认模式)根本看不懂 .NET 的 IL 字节码,也没加载 CLR 运行时。你扔一个 MyLib.dll(C# 编译出来的托管 DLL)给纯 C++ 程序,LoadLibrary 会返回 NULL,GetLastError() 很可能报 ERROR_BAD_EXE_FORMAT —— 它压根不是 Windows 认的原生 PE 格式,而是托管程序集。
常见错误现象:
-
LoadLibrary("MyCSharpLib.dll")返回NULL,且GetLastError()是 193(ERROR_BAD_EXE_FORMAT) - 用
#using "MyCSharpLib.dll"编译报错:C3673: cannot take address of managed type - 在 C++/CLI 工程里写
System::String^ s = "hello";却提示未定义标识符 —— 忘了开 /clr 开关
/clr 是开关,不是语言扩展
/clr 是 MSVC 编译器的一个开关,它让 C++ 编译器生成混合模式(mixed-mode)目标文件:既有原生 x86/x64 机器码,也有 IL 元数据。没有它,C++/CLI 语法(比如 gcnew、^、%)连编译都过不去;开了它,才能在同一个 .cpp 文件里混写原生指针和托管引用。
实操建议:
- VS 项目属性 → 配置属性 → 常规 → “公共语言运行时支持” 必须设为
/clr(不能选/clr:pure或/clr:safe,它们已弃用且限制极多) - 所有用到托管类型(如
System::String^)或调用 C# 代码的.cpp文件,必须被/clr编译;但纯原生模块(如算法库)可保持默认/MD,不参与混合编译 -
#using "MyCSharpLib.dll"必须出现在/clr编译的源文件中,且 DLL 路径要加到“附加 #using 目录”里(项目属性 → 配置属性 → 常规 → “附加 #using 目录”)
从 C++/CLI 中调用 C# 类型的三步落地法
不是所有 C# 类都能直接拿来用。C# 端暴露的类必须满足基本互操作契约:public、有无参构造函数(或显式声明)、避免泛型类型参数跨边界(如 List<string></string> 可传,List<t></t> 不行),且最好用 [ClassInterface(ClassInterfaceType.AutoDual)] 或显式接口。
实操建议:
- C# 项目 → 属性 → 应用程序 → “输出类型”选“类库”,勾选“为 COM 交互启用”(非必需,但能绕过部分反射限制)
- C# 类加
[System::Runtime::InteropServices::ComVisible(true)]特性(如果走 COM 路线),或确保是 public + public 构造函数(走#using+ IL 引用路线) - C++/CLI 端写法示例(假设 C# 有
namespace MyLib { public class Calculator { public int Add(int a, int b) => a + b; } }):#using "MyCSharpLib.dll"<br>using namespace MyLib;<br><br>int result = (gcnew Calculator())->Add(3, 4); // 注意 gcnew 和 ->
原生 C++ 怎么间接吃上 C# 的能力
如果你手头只有纯 C++ 主程序(比如 Qt 或 SDL 写的游戏引擎),又不想把它整个转成 C++/CLI(成本太高、破坏 ABI、影响第三方库链接),那就得靠中间层:用 C++/CLI 写一个“胶水 DLL”,导出纯 C 风格函数,再被原生 C++ 用 LoadLibrary + GetProcAddress 调用。
关键点:
- C++/CLI 胶水 DLL 的导出函数必须是
extern "C" __declspec(dllexport),返回/参数只能是int、const char*、void*这类 C 兼容类型 - 内部用
gcnew创建 C# 对象,把托管对象指针转成void*存起来(例如用GCHandle::Alloc固定对象,再取PtrToStructure) - 性能上,每次跨层调用都有 CLR 切换开销,别在 tight loop 里高频调用;字符串来回转换(
marshal_as<:string></:string>)也耗 CPU,尽量批量传或用 UTF-16 缓冲区共享
容易被忽略的是:C++/CLI 胶水 DLL 运行时依赖 .NET Framework(如 v4.0.30319)或 .NET Core/.NET 5+ 的运行时宿主,部署时得确认目标机装了对应版本,否则 LoadLibrary 失败不是因为路径错,而是 CLR 初始化失败。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











