C#调用C++动态库时数据类型匹配问题

GK_2014 2015-10-20 03:32:50
我用C++将一个加密函数封装成动态库,然后在C#里面调用,发现函数返回的结果有问题,请大家帮我看看:
在C++里对加密函数做了声明
extern "C" _declspec(dllexport) CString _des(LPCTSTR key, LPCTSTR data, LPCTSTR mode);

函数实现代码如下,由于该函数有调用了多个函数,我就不一一展开了:
CString _des(LPCTSTR key, LPCTSTR data, LPCTSTR mode) {
unsigned char tmpbuf[300];
unsigned char tmpstr[601];
unsigned char keyBuf[32];

int dataLen = strlen(data);
dataLen = hexStrToHexBuf(data, dataLen, tmpstr);

if(dataLen % 8 != 0) {
return _T("");
}

int keyLen = strlen(key);
if(keyLen < 16) {
return _T("");
}

keyLen = hexStrToHexBuf(key, keyLen, keyBuf);

char deOrEn = 0;

if(strcmp(mode, "0") == 0 || strcmp(mode, "00") == 0) { //加密
deOrEn = 1;
} else { //
deOrEn = 0;
}

if(keyLen == 8) { // des
desEcb(keyBuf, tmpstr, dataLen, tmpbuf, deOrEn);
} else {
tripDesEcb(keyBuf, keyLen, tmpstr, dataLen, tmpbuf, deOrEn);
}

int rLen = hexBufToHexStr(tmpbuf, dataLen, (char *)tmpstr);
tmpstr[rLen] = '\0';

CString result = (CString)tmpstr;
return result;
}


然后在C#代码中调用该动态库,对导入函数声明如下:
        [DllImport("dllTest.dll", EntryPoint = "_des", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern string _des(StringBuilder key, StringBuilder data, StringBuilder mode);

具体测试如下,即用16字节0对8字节0加密,结果应该返回8字节加密结果:
            StringBuilder mk_mf = new StringBuilder("00000000000000000000000000000000");
StringBuilder random = new StringBuilder("0000000000000000");
StringBuilder type = new StringBuilder("0");
string strResult = _des(mk_mf, random, type);

但是运行时却返回"\vM_0000000000000000000000000000"
搜了网上的资料,貌似C++的LPCTSTR对应于C#的string或stringbuilder数据类型
我怀疑是C#声明导入函数的参数类型不对,换成string类型后提示“尝试读取或写入受保护的内存”,麻烦有经验的指导下
...全文
259 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
Saleayas 2015-10-21
  • 打赏
  • 举报
回复
在 C++ 端,如果 LPCWSTR 作为返回值,那么你需要考虑这个字符串如何释放的问题。 很多时候,在 C++ 端,我们不需要考虑这个问题。 但是,当从非托管内存到托管内存,就需要考虑这个问题。这就是是使用 BSTR 的问题。 因为 BSTR 使用的是 SysAllocString。 如果你确实需要使用 LPCWSTR 的话(比如C++的代码不是你可以控制的),那么你需要知道这个返回的字符串是如何分配的, 是否需要你自己释放,如果需要的话,那么如何释放。 缺省是使用 CoTaskMemAlloc 的话,可以使用 Marshal 来指示。 否则,只能使用 IntPtr 来读取和释放了。 而是用缓冲区的话,就没有分配和释放的问题了。 至于使用 CString 的话,那么是不正确的,CString 在任何时候,都不应该作为导出的参数类型和返回值类型。
GK_2014 2015-10-21
  • 打赏
  • 举报
回复
引用 5 楼 Saleayas 的回复:
如果你的 C++ 版本是 CString 的话,那么你需要在C#端模拟一个。 而且,标准的 C++ 导出时不可以返回 CSString 的。 使用 BSTR 作为返回值,或者使用缓冲区来返回字符串。
你的意思是函数返回类型改成void,加一个形参,从形参返回结果?
GK_2014 2015-10-21
  • 打赏
  • 举报
回复
引用 6 楼 crystal_lz 的回复:
cdecl 的调用方式没有用过 一般都用stdcall 你可以试试 传入缓存试试 就像Windows的一些函数 比如:

int GetWindowText(
  HWND hWnd,        // handle to window or control
  LPTSTR lpString,  // text buffer
  int nMaxCount     // maximum number of characters to copy);

int GetClassName(
  HWND hWnd,           // handle to window
  LPTSTR lpClassName,  // class name
  int nMaxCount        // size of class name buffer);
我在_des函数里增加了一个字符串形参作为返回结果值,函数返回类型改成void,这样可以得到想要的结果
GK_2014 2015-10-21
  • 打赏
  • 举报
回复
引用 4 楼 angel6709 的回复:
返回的string需要分配内存
C#的string怎么分配内存?
crystal_lz 2015-10-21
  • 打赏
  • 举报
回复
引用 8 楼 GK_2014 的回复:
[quote=引用 6 楼 crystal_lz 的回复:] cdecl 的调用方式没有用过 一般都用stdcall 你可以试试 传入缓存试试 就像Windows的一些函数 比如:

int GetWindowText(
  HWND hWnd,        // handle to window or control
  LPTSTR lpString,  // text buffer
  int nMaxCount     // maximum number of characters to copy);

int GetClassName(
  HWND hWnd,           // handle to window
  LPTSTR lpClassName,  // class name
  int nMaxCount        // size of class name buffer);
我在_des函数里增加了一个字符串形参作为返回结果值,函数返回类型改成void,这样可以得到想要的结果[/quote] 严格来说 你不应该返回 void 而是 int 被写入的长度 不然你不会知道 你传入的缓存被写入了多少长度 当然 如果是字符串 根本不需要担心这个问题 因为 \0 处就是结束
crystal_lz 2015-10-20
  • 打赏
  • 举报
回复
cdecl 的调用方式没有用过 一般都用stdcall 你可以试试 传入缓存试试 就像Windows的一些函数 比如:

int GetWindowText(
  HWND hWnd,        // handle to window or control
  LPTSTR lpString,  // text buffer
  int nMaxCount     // maximum number of characters to copy);

int GetClassName(
  HWND hWnd,           // handle to window
  LPTSTR lpClassName,  // class name
  int nMaxCount        // size of class name buffer);
Saleayas 2015-10-20
  • 打赏
  • 举报
回复
如果你的 C++ 版本是 CString 的话,那么你需要在C#端模拟一个。 而且,标准的 C++ 导出时不可以返回 CSString 的。 使用 BSTR 作为返回值,或者使用缓冲区来返回字符串。
angel6709 2015-10-20
  • 打赏
  • 举报
回复
返回的string需要分配内存
本拉灯 2015-10-20
  • 打赏
  • 举报
回复
如果你要导出成函数最好加个_In_ extern "C" __declspec (dllexport) bool ExecuteAssembly(_In_ LPCTSTR assemblyPath) LPCTSTR 这种参数要区别对待的 还有你要看 你编译DLL所用的 字符集 是 使用多字节字符集 还是用Unicode, DLL模认编译的是 Unicode此时你C#就不能用CharSet.Ansi 应用CharSet.Unicode [DllImport("dllTest.dll", EntryPoint = "_des", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] public static extern string _des(StringBuilder key, StringBuilder data, StringBuilder mode); 如果使用多字节字符集 那模认的是CharSet.Ansi LPCTSTR 在方法的内部没有对参数进行修改,可以可以使用string的。 用StringBuider是因为对其参数内容进行修改输出才用到的。
runerback 2015-10-20
  • 打赏
  • 举报
回复
string不是char* 吗
白衣如花 2015-10-20
  • 打赏
  • 举报
回复
感觉C++的字符串长度是C#的长度+1 比如C#的“123”长度是3,C++可能就是4了,应该他在字符串的末尾有一个结束符'\0' 你试试在定义数组长度是,用lenth+1

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧