http://winsystem.ctocio.com.cn/168/12077668.shtml
最近项目,需要在C# 中调用C++ 写的DLL,因为C# 默认的编码方式是Unicode,而调用的DLL规定只处理UTF8编码格式的字符串,DLL中的输入参数类型char*被我Marshal成byte[],输出参数类型char**被我Marshal成了string(C++和C#之间的类型转换请参阅相关资料),于是我就经历了无数次用于接收时的string-->string(UTF8-->Unicode)和用于发送时的string-->byte[](Unicode-->UTF8)这样频繁的编码转换,期间多次出现中文乱码,看来编码转换并非想象中那么简单,今天花时间google一番,原来以前的理解确实不够深,存在很多误区,现整理如下:
(一)、Encoding和CharSet
为什么先提这两个,实属问题之源。在C#中包装DLL的时候,DllImportAttribute当中的选项CharSet着实让我糊涂了很久,MSDN曰:规定封送字符串应使用何种字符集,其中枚举值有Ansi和Unicode,我真不知道到底改选哪一个。于是乎, google一番,Encoding这棵救命草被我找到,同时也释疑了不少疑惑。
首先,字符集不同于编码,以前总将它们混为一谈,CharSet是字符集,Encoding是编码。字符集是字符的集合,规定这个集合里有哪些字符,每个字符都有一个整数编号(只是编号不是编码);而编码是用来规定字符编号如何与二进制交互,每个“字符”分别用一个字节还是多个字节存储。啊呜,原来这样,那我这里接触到的Ansi、Unicode、UTF8等等等等究竟是怎么回事呢,借此机会,一探究竟!^_^
(二)、Ansi、Unicode、UTF8、bala bala
提到字符集,有ASCII、GB2312、GBK、GB18030、BIG5、JIS等等多种,与此相对应的编码方式为ASCII、GB2312、GBK、GB18030、BIG5、JIS(囧,难怪糊涂如我般的人如此多),但是Unicode字符集却有多种编码方式:UTF-8、 UTF-7、UTF-16、 UnicodeLittle、UnicodeBig。原来如此,字符集与编码原来是这个样子。ˇˍˇ|||
那Ansi又是什么呢?
Ansi:系统编码的发展经历了三个阶段,ASCIIàAnsi(不同国家语言本地化)àUnicode(标准化),原来Ansi编码也好,Ansi字符集也好,都是指本地化的东西,在简体中文系统下,ANSI 编码代表 GB2312 编码,Windows下自带的记事本程序,默认的就是ANSI编码。呵呵,那偶就去试试吧,输入“anhui合肥”(不含引号),保存编码方式选“ANSI”,查看,哦,9个字节,明白了,原来Ansi编码保留了对ASCII编码的兼容,当遇到ASCII字符时,采用单字节存储,当遇到非ASCII编码时,采用双字节表示(GB2312编码)。
(三)、DllImportAttribute中的CharSet枚举值选择
对字符集和编码的概念清楚了以后,终于可以研究C#中调用非托管的DLL的方法咯。从托管应用程序去调用非托管代码,如果CharSet=Unicode,则DLL中的接口函数将出现的所有字符串(包括参数和返回值)视为Unicode字符集,Ansi一样的道理。真不错,了解到这里总算有点拨云见日的感觉了!O(∩_∩)O
好像目前需要了解的知识点都差不多了,终于可以开始来解决我的问题了, (*^__^*)。回顾整理一下,第一:我需要调用非托管代码,第二:非托管代码只处理UTF8编码格式的字符,第三,万恶的中文乱码,接口函数签名中,无论参数还是返回值,接收到或是发送出的字符串都含有中文。呵呵,看来上面的知识应该可以解决这个问题了:首先接收字符串,因为接收到的是UTF8编码格式,CharSet属性肯定要设置成Unicode了,接收后要正确显示中文,在当前系统中,需要将编码方式转换成GB2312,即Encoding.Default;另外关于发送字符数组,因为无论是C#中默认的Unicode编码方式,还是DLL处理时规定的UTF8编码方式,都是Unicode字符集的一种编码方式,所以CharSet也要设置成Unicode,只是要在发送前需要将字符数组转换一下编码方式。以下附带两个简单的函数实现。
以下是代码片段: // 转换接收到的字符串 public string UTF8ToUnicode(string recvStr) { byte[] tempStr = Encoding.UTF8.GetBytes(recvNotify); byte[] tempDef = Encoding.Convert(Encoding.UTF8, Encoding.Default, tempStr); string msgBody = Encoding.Default.GetString(tempDef); return msgBody; } // 转换要发送的字符数组 public byte[] UnicodeToUTF8(string sendStr) { string tempStr = Encoding.UTF8.GetString(sendStr); byte[] msgBody = Encoding.UTF8.GetBytes(tempUTF8); return msgBody; } |
总结一下,本文因项目完成的需要,难免存在局限性,只讨论了两种情况:Unicode编码的字符串转UTF8格式的字符数组,以及UTF8格式的字符串转Unicode格式的字符串,上面两个方法均通过测试,至此总算解决了中文乱码的问题。平台调用的知识点很多,只有真正掌握必需的基础知识和平台调用的原理,才能做到活学活用。