调用C++导出库方法时触发System.AccessViolationException求助
解决C++库导入C#时的System.AccessViolationException错误
这个内存访问冲突问题大概率是跨语言调用时的类型不匹配、调用约定不一致或者C++名字修饰导致的,我帮你拆解问题并给出具体的修复方案:
问题根源分析
std::string 无法被C#直接识别
C++的std::string是标准库封装的类型,内存布局和C#的string完全不同,跨语言调用时不能直接用它作为函数参数——C#无法正确封送这个类型,必然会导致内存读写错误。C++函数导出的名字修饰问题
你原来的代码里,只在声明时加了extern "C",但定义时没加,这会导致C++编译器对函数名进行名字修饰(mangling),C#通过DllImport查找的函数名和实际导出的名字不匹配,间接引发内存错误。回调函数的调用约定一致性
虽然你用了CALLBACK(本质是__stdcall),但C#端如果没有正确声明委托的调用约定,会导致栈不平衡,同样触发内存访问异常。
具体修复步骤
第一步:修改C++端代码
把函数参数换成C兼容的类型,确保导出的函数没有名字修饰,代码如下:
#include <string> // 回调函数保持CALLBACK(__stdcall)调用约定不变 int CALLBACK xCallback(long xmitlen, int buflen, char *buf, long flen) { return 0; } // 用extern "C"包裹声明和定义,避免C++名字修饰 // 参数改用const char*,这是跨语言调用字符串的标准做法 extern "C" __declspec(dllexport) int __stdcall TXFIlestransfer(int port, const char* fileName) { int ret; // 如果sio_FtKermitTx要求非const的char*,可以用const_cast转换(确认库不会修改传入的字符串) ret = sio_FtKermitTx(port, const_cast<char*>(fileName), xCallback, 27); return ret; }
第二步:修改C#端调用代码
需要正确声明回调委托和导入函数,保证调用约定、字符集和参数类型完全匹配:
using System; using System.Runtime.InteropServices; class FileTransferClient { // 声明回调委托,必须指定StdCall调用约定,参数类型对应C++的类型 // Windows下long是32位,对应C#的int;char*用IntPtr接收(如果需要操作缓冲区可以转成byte[]) [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int XCallbackDelegate(int xmitlen, int buflen, IntPtr buf, int flen); // 导入C++函数,注意替换成你的DLL文件名 // 指定StdCall调用约定和ANSI字符集(匹配C++的char类型) [DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] private static extern int TXFIlestransfer(int port, string fileName); static void Main(string[] args) { try { // 测试调用,替换成你的端口和文件名 int transferResult = TXFIlestransfer(5000, "testfile.txt"); Console.WriteLine($"文件传输结果:{transferResult}"); } catch (AccessViolationException ex) { Console.WriteLine($"错误:{ex.Message}"); } } }
额外注意事项
- 字符集匹配:如果你的C库用的是Unicode(
wchar_t*),那C#端要把CharSet改成CharSet.Unicode,同时C参数换成const wchar_t*。 - 回调生命周期:如果C++库会在
TXFIlestransfer返回后继续调用回调,一定要把C#的委托保存为静态成员(比如private static XCallbackDelegate _callback;),防止被GC回收导致内存错误。 - 参数类型验证:确认
sio_FtKermitTx的参数类型和你传递的完全一致,比如long在Windows是32位,对应C#的int,如果是64位系统的long则对应C#的long。
内容的提问来源于stack exchange,提问作者bigGstatus
相关产品推荐
相关产品推荐

