VB中文处理问题(Unicode和Ascii的编码问题)
前段时间电脑显示中文不正常,就是因为把“控制面板”里的“语言地区设置”  高级选项 中设置为把非Unicode字符当English Unite State 地区来处理,不是China (PRC). 因为要做测试的程序不支持中文,改了上面说到的语言选项之后就出问题了。结果别人发来的QQ信息都看不到,全部显示乱码。
跟踪调试了好几次,终于把这个问题解决了。首先修改的是StringtoHex 和HexToString 两个函数,可以看出这两个函数明显不支持中文。它在里面使用Len()Asc() Hex()等函数来作String变量到二进制字符串的转换。由于中文汉字有两个字节大小,而Len()都是但作一个长度来处理,在加上Asc()处理中文字符,都是返回负数。所以重写了这两个函数改成对字符串字节作处理,直接处理Byte到二进制表示法字符串的变换,很方便。
使用
dim strtemp as string
dim a() as byte
a=System.Text.Encoding.Unicode.har)
便可以获取字符串保存的数据byte信息了。
但是关键的地方 还不是上面的那个函数,而是  一个加解密的函数。经过跟踪发现,这个函数加密后返回的字符串字节长度增加了。可以看到是先用len()来计算字符串长度,再调用微软的加密解密函数库来加密,然后用一个 Left()函数来截取加密后的字符串返回。就是控制面板里的语言选项设置导致把加密函数库返回的字符串字节当作Unicode来处理,所以最后采用Left()截取的字符串和传进去的字符串字节长度不一样,这就是问题的根源。 由于VB.NET中全部字符串都是当作Unicode来处理。当把Ascii字母传给微软加密库之后,得到的却是VB自动转换而来的Unicode字符。我曾想过字节编写函数把这个unicode 的两个字节分离成两个AscIi字符。但实现起来并不十分成功,并不如想象中的简单。网上到如下资料:
////////////////////////////////////////////////////////////
Q  如何实现ANSI和UNICODE的相互转换 
将ANSI转换到Unicode 
(1)通过L这个宏来实现,例如:  CLSIDFromProgID(  L"MAPI.Folder",&clsid); 
(2)通过MultiByteToWideChar函数实现转换,例如: 
char  *szProgID  =  "MAPI.Folder"; 
WCHAR  szWideProgID[128]; 
CLSID  clsid; 
long  lLen  =  MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID)); 
szWideProgID[lLen]  =  '\0';   
(3)通过A2W宏来实现,例如:   
USES_CONVERSION;   
CLSIDFromProgID(  A2W(szProgID),&clsid);   
将Unicode转换到ANSI 
(1)使用WideCharToMultiByte,例如: 
//  假设已经有了一个Unicode  串     
char  szANSIString  [MAX_PATH];   
WideCharToMultiByte  (  CP_ACP,  WC_COMPOSITECHECK,  wszSomeString,  -1,  szANSIString,  sizeof(szANSIString),  NULL,  NULL  ); 
(2)使用W2A宏来实现,例如: 
USES_CONVERSION; 
pTemp=W2A(wszSomeString);   
注意在转换时可能存在的问题: 
因为ANSI转UNICODE,如果使用A2W或MultiByteToWideChar(第一个参数是CP_ACP)的话,是根据系统默认的转码表,把转入的ANSI字符串看作Multi-Bytes字符串处理的,如果是中文(中文windows默认就是中文),一个大于0x87的byte可能和下一 byte一起被看作一个汉字,然后根据汉字的Unicode编码转换为相同的Unicode汉字,如果不到相应的编码,一般就用一个默认的字符来取代它(一般是问号"?"),由此看,如果随便把一段数据给他转,转化很复杂而且极可能不可逆,而且你加密过的ANSI码是相当混乱的有很多〉0x87的 byte,转换就变得不可逆了。 
建议自己直接就这样写: 
CHAR  lpANSI[COUNT]; 
WCHAR  lpUnicode[COUNT]; 
int  i  =  0;   
while(lpANSI[i]  !=  '\0'  )  { 
lpUnicode[i]  =  (WCHAR)lpANSI[i]; 
lpUnicode[i]  =  L'\0'; 
然后按相同的方法转回来,因为对于0~0x87的ANSI字符串,对应的Unicode码就是相同的16位值,至于其他的,你的字符串反正加了密,没必要转换成显示出来是一样的字符,就按同样的方法处理了,其实如果中间的字符串不用显示或别的,直接reutrn  (LPWSTR)lpANSI;过去也可以,  反正接受的时候自己清楚就可以了。 
/////////////////////////////////////////////////////////////////////////
我记得再VB中也是有StrConV这个函数可以用的,结果发现现在VB中的这个函数和以前不同了,可能是因为VB.NET全部都是Unicode字符串。尝试用Systme.Text.Encoding.Ascii ,Systme.Text.string (), 结果摸索了半天没有进展。得出结论VB.NET中Unicode和Ascii字符的处理是很吃力。我指的是Unicode字节的分离。  看来只能取巧了。因为微软的加解密函数库中的函数库是采用Byte来加解密的
(这点可以由C语言声明看出),所以VB中调用时把string型变量传过去并不是十分合适,特别是这种Unicode和Ascii相关转换很容易出问题。而直接把Byte传给它更符合它的处理方法,也可以更好的控制byte的长度。前面说过字符串的字节信息是可以通过调用System.Text.Encoding.Unicode.har) 来获取的,所以加解密字符串也是很简单的。解密之后的Byte数组,经过 System.Text.Encoding.Unicode.GetString(strByteArray) 就可以取得了。这样可以严格控制加解密的长度,不会出现Left()取得返回字符串长度不正确的问题。
修改微软加解密API的声明为如下形式:
Private Declare Function CryptEncrypt Lib "advapi32.dll" (ByVal hSessionKey As Integer, ByVal hHash As Integer, ByVal Final As Integer,
ByVal dwFlags As Integer, ByRef pbData As Byte, ByRef pdwDataLen As Integer, ByVal dwBufLen As Integer) As Integer
Private Declare Function CryptDecrypt Lib "advapi32.dll" (ByVal hSessionKey As Integer, ByVal hHash As Integer, ByVal Final As Integer,
ByVal dwFlags As Integer, ByRef pbData As Byte, ByRef pdwDataLen As Integer) As Integer
而原先  pbData 参数 是这样声明的 ByVal  pbData as String ,因为CryptEncrypt 函数本来就是对byte类型来作处理的,所以这
样的改变没有什么问题。相应的调用改为:
Dim strByteArray() As Byte = System.Text.Encoding.Unicode.GetBytes(Text.ToCharArray())
…………………… 
lngReturnValue = CryptEncrypt(hSessionKey, 0, 1, 0, strByteArray(0), lngLength, lngLength)
重新编译运行,一切通过,问题解决
unicode汉字
总结:注意加解密之后字符串的改变和编码解码问题。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。