最近在研究中文分词中的歧义消除方案,项目是用C#编写的,我用C++写了个分词工具,想要整合进这个C#项目中,就打算用C#来调C++的dll,没成想这里面的坑竟然这么多。。。
ROUND 1 当C++接口的返回值是string类型时
C++对外接口的声明
extern "C" _declspec(dllexport) std::string __cdecl Segmentor(std::string); |
这是一个分词的接口,可以看到参数和返回值类型都是string类型
C#的调用方式
[ ] |
所以就想当然的用C#的string类型来接收C++的string结果,结果崩溃了。。。原来C#的string和C++的string是不一样的东西,因此得换种方法来调用
ROUND 2 用const char*代替string
好吧,既然string不能用那就换一种方式,由于传入的是窄字节字符串,因此C++可以用const char来代替,如果是宽字节字符串可以用*const wchat_t* **
C++对外接口的声明
extern "C" _declspec(dllexport) const char* __cdecl Segmentor(const char*); |
C#的调用方式
C#在调用const char的时候要注意,最好不要用C#的string来接收,因为const char是个指针,所以最好也用指针来接收,所以就用C#的IntPtr**类型来接收
[ ] |
此外,要想把获得的IntPtr结果转化为string类型,需要用Marshal.PtrToStringAnsi方法进行转化,
截至目前为止,一切都很美好,能够获得结果了,但是好景不长,又遇到了新的问题。。。
ROUND 3 用BSTR代替const char*
在返回值长度较小时(短文本),const char* 完全没有问题,但是文本过长偶尔会出现字符乱码的情况,那是因为C++中const char* 指针指向的时一个临时对象的地址,调用完之后这个临时对象就析构了,从而用C#的IntPtr类型来接收这个指针就会出现问题
字符串的长度可能互不相同,跨COM边界传输特定的字符串时,需要确定它的长度,而且字符串有时需要分配内存,因此需要找到一个所有COM兼容语言都可以使用的字符串,所以BSTR类型闪亮登场
C++对外接口的声明
extern "C" _declspec(dllexport) BSTR __cdecl Segmentor(const char*); |
C#的调用方式
[ ] |
注意,C#调用BSTR类型时需要注明**[return: MarshalAs(UnmanagedType.BStr)]**特性