C#调用带__stdcall的非托管C++ DLL函数遇入口点错误求助
解决C++ DLL跨C#调用的入口点与类型兼容问题
先解决入口点找不到的问题
你遇到的入口点错误,核心原因是x64平台下MSVC不区分__stdcall/__cdecl等调用约定——所有函数都用统一的x64调用约定,你写的__stdcall会被编译器直接忽略。这导致C#里指定CallingConvention.StdCall完全无效,反而会干扰入口点匹配。
修正步骤:
- 用VS开发者命令提示符重新确认DLL的导出名称,执行:
复制输出里对应dumpbin /exports mydll.dllFunction的完整修饰名(就是带?的那串),确保C#的EntryPoint完全匹配,符号、大小写一个都不能错。 - 修改C#的
DllImport,把调用约定改成CallingConvention.Winapi(x64下Winapi等价于默认的x64调用约定),或者直接省略调用约定参数(x64下默认就是正确的):[DllImport("mydll.dll", EntryPoint = "?Function@NS@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z", CallingConvention = CallingConvention.Winapi)] static extern IntPtr Function(int Num); // 这里先把返回值改成IntPtr,因为std::string不能直接传
关键问题:std::string不能直接跨语言传递
就算入口点找到了,直接返回std::string给C#一定会崩溃——std::string是C++编译器实现的私有类,内存布局和C#的String完全不兼容,跨语言传递属于未定义行为。必须改成C兼容的类型,推荐两种靠谱方案:
方案1:返回C风格字符串(推荐简单场景)
修改C++函数,返回用CoTaskMemAlloc分配的堆内存字符串(C#可以直接释放):
namespace NS { __declspec(dllexport) char* __stdcall Function(int Num) { std::string result = "计算结果:" + std::to_string(Num); // 分配足够存字符串加结束符的内存 char* buffer = (char*)CoTaskMemAlloc(result.length() + 1); if (buffer != nullptr) { strcpy_s(buffer, result.length() + 1, result.c_str()); } return buffer; } }
C#里调用并释放内存:
[DllImport("mydll.dll", EntryPoint = "?Function@NS@@YAPEADH@Z", CallingConvention = CallingConvention.Winapi)] static extern IntPtr Function(int Num); // 使用示例 IntPtr strPtr = Function(456); string result = Marshal.PtrToStringAnsi(strPtr); Marshal.FreeCoTaskMem(strPtr); // 必须释放,避免内存泄漏
方案2:用回调函数传递数据(适合复杂类型如std::vector)
如果需要传递std::vector这类复杂容器,用回调让C主动把数据推给C#是更安全的方式:
C代码:
// 先定义回调函数类型 typedef void(__stdcall* StringCallback)(const char* str); typedef void(__stdcall* IntCallback)(int value); namespace NS { __declspec(dllexport) void __stdcall ProcessData(int inputNum, StringCallback strCb, IntCallback intCb) { // 返回字符串结果 std::string strResult = "输入值:" + std::to_string(inputNum); strCb(strResult.c_str()); // 传递std::vector里的元素 std::vector<int> vec = {10, 20, 30}; for (int num : vec) { intCb(num); } } }
C#代码:
// 定义匹配的委托 [UnmanagedFunctionPointer(CallingConvention.Winapi)] delegate void StringCallback(IntPtr strPtr); [UnmanagedFunctionPointer(CallingConvention.Winapi)] delegate void IntCallback(int value); [DllImport("mydll.dll", EntryPoint = "?ProcessData@NS@@YAXHP6GXPEBD@ZP6GXH@Z@Z", CallingConvention = CallingConvention.Winapi)] static extern void ProcessData(int inputNum, StringCallback strCb, IntCallback intCb); // 使用示例 StringCallback strCallback = (ptr) => { string str = Marshal.PtrToStringAnsi(ptr); Console.WriteLine(str); }; IntCallback intCallback = (num) => { Console.WriteLine($"Vector元素:{num}"); }; ProcessData(789, strCallback, intCallback);
最后检查事项
- 确保C++ DLL和C#程序都是x64架构,Debug/Release模式要一致;
- 把DLL复制到C#程序的输出目录(比如bin/x64/Debug),避免找不到DLL;
- 绝对不要直接传递
std::string、std::vector这类C++专属类型,必须转成C兼容的类型或用回调传递。
内容的提问来源于stack exchange,提问作者Iman Abdollahzadeh
相关产品推荐
相关产品推荐

