当前位置: 代码迷 >> C# >> C#调用C++动态库时数据类型匹配有关问题
  详细解决方案

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

热度:323   发布时间:2016-04-28 08:40:43.0
C#调用C++动态库时数据类型匹配问题
我用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类型后提示“尝试读取或写入受保护的内存”,麻烦有经验的指导下

------解决思路----------------------
如果你要导出成函数最好加个_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是因为对其参数内容进行修改输出才用到的。



------解决思路----------------------
如果你的 C++ 版本是 CString 的话,那么你需要在C#端模拟一个。
而且,标准的 C++ 导出时不可以返回 CSString 的。

使用 BSTR 作为返回值,或者使用缓冲区来返回字符串。

------解决思路----------------------
在 C++ 端,如果 LPCWSTR 作为返回值,那么你需要考虑这个字符串如何释放的问题。
很多时候,在 C++ 端,我们不需要考虑这个问题。
但是,当从非托管内存到托管内存,就需要考虑这个问题。这就是是使用 BSTR 的问题。
因为 BSTR 使用的是 SysAllocString。

如果你确实需要使用 LPCWSTR 的话(比如C++的代码不是你可以控制的),那么你需要知道这个返回的字符串是如何分配的,
是否需要你自己释放,如果需要的话,那么如何释放。
缺省是使用 CoTaskMemAlloc 的话,可以使用 Marshal 来指示。
否则,只能使用 IntPtr 来读取和释放了。

而是用缓冲区的话,就没有分配和释放的问题了。

至于使用 CString 的话,那么是不正确的,CString 在任何时候,都不应该作为导出的参数类型和返回值类型。
  相关解决方案