VC 程序员的阵痛--将多字节环境移植到Unicode环境
环境的移植是什么样的感受?我的一位师哥说的很真切:这是一项体力活!说的一点没错。在我将项目的环境设置成Unicode时,编译时竟出现500多了错误!而在多字节环境下却可以正常运行。在MSDN、网上查找了相关移植的资料.发现所谓的移植主要是将一些多字节函数使用Unicode环境下的函数替换一下.
例如strcmp()函数原型为:
未设置任何环境下的函数
int strcmp(
const char *string1,
const char *string2
);
Unicode环境下的函数
int wcscmp(
const wchar_t *string1,
const wchar_t *string2
);
多字节环境下的函数
int _mbscmp(
const unsigned char *string1,
const unsigned char *string2
);
因此,我按照MSDN上的函数说明,使用查找替换的方法,将所有strcmp 替换为 wcscmp.重新编译一下,结果错误不仅没有减少,反而又多了起来.经过查看错误列表,发现新增的一些错误来自些带多字节参数的函数,如 void Compress(const char *lpszInfile,const char *lpszOutfile).由于这些函数参数形式为多字节的,而在函数体内部却使用Unicode环境下的比较函数,这当然是行不通的.再次查看MSDN,发现如下表格:
Generic-Text Routine Mappings
TCHAR.H routine | _UNICODE & _MBCS not defined | _MBCS defined | _UNICODE defined |
_tcscmp | strcmp | _mbscmp | wcscmp |
它的意思是说你可以使用宏定义函数来代替MBSC和Unicode函数. Routine:说明该宏定义函数只有在运行时才会动态的根据环境来决定使用MBSC函数还是Unicode函数.
现在我们分别在Unicode、MBSC、UnKnow环境下查看_tcscmp函数的提示信息:
Unicode环境
MBSC环境
UnKnow环境
一切都很明白。宏函数在各种环境下使用了不同的函数定义。现在我们可以放心的使用宏函数来替换原来的函数了。
于是我将需要的替换的函数都用宏函数做了替换。但是错误仍然很多,到底什么地方出错了?我又仔细的查看了错误信息。原来许多类的函数带有char类型参数,由于在Unicode环境下使用宏函数,所以函数的参数被解释成了Unicode下的变量类型wchar_t,所以产生了类型不匹配的错误.但是我总不能都将所有的char类型都替换成wchar_t类型吧。这样一来,就只能在Unicode环境下编译使用了,如果需要转换为MBCS环境又要修改变量类型,所以不具备通用性。经过思考,我决定采用两种方法来解决此问题。
一是保留原有参数类型,在函数体内不使用宏函数,如果参数是MBCS类型的就使用MBSC类型的函数。如果使用Unicode参数类型就使用Unicode类型函数。
如:参数是const char* 就使用 strcpy,strcmp之类的函数。
参数是 const wchar_t* 就使用 wcscpy,wcscmp之类的函数。
二是使用宏变量类型TCHAR,该变量也会根据环境的不同,使用不同的变量类型。
如:在MBSC环境下,TCHAR 代表char。
在Unicode环境下,TCHAR代表 wchar_t。
经过如上的反复修改和调试,错误终于改完了。谢天谢地!
可惜,我高兴的太早了。许多隐藏的错误弄得我焦头烂额。在填充一个树状控件时,每一个树项都成了乱码。但编译、运行为什么没有出错?带着这个疑问,我使用调试器查看了运行时的变量值。结果发现,从文件中读取的树项被放入了char类型的缓冲区,而填充树项却在Unicode环境下进行的。我们知道:在MBSC环境下A=0x41,而在Unicode环境下A=0x41 00,所以多字节的字符串AB=0x4142,而在Unicdoe环境下则变成了AB=0x14 00 42 00,所以在Unicode环境下,0x4142被认为是一个字符,但编码中并不存在这一字符,结果就变成了乱字符串。原因找到了,解决的办法就是使用MultiByteToWideChar和WideCharToMultiByte两个函数在MBCS和Unicode环境下来回的转换。现在好了,终于完成了环境的移植。心里踏实多了。
希望我的这篇文章能对大家有所帮助。