error C2664: “MessageBoxW”: 不能将参数 2 从“const char [10]”转换为“LPCWSTR”.
这个问题太普遍了,几乎所有的初学者都会遇到而且感到难以应付,因为按照提示使用(LPCWSTR)强制转型貌似并不能帮助我们解决问题,而且这个程序在VC6下面应该是没有任何问题的,那问题出现在哪里呢?问题在这里,请右键单击解决方案浏览器下面的项目.属性.
问题的根本就是字符集问题,在VC6中,我们默认使用的是多字节字符集,而现在我们默认需要的是UNICODE字符集,简单的,我们把这个字符集改成多字节字符集这个问题就解决了
再试试应该就可以了吧?但是我并不推荐大家这么做,因为让自己的程序适应各种字符集是我们写代码的人义不容辞的义务.
我们把程序改成下面这样:
#include <iostream>
#include <Windows.h>
#include <tchar.h>
using namespace std;
int main() {
MessageBox( NULL, TEXT("你好HelloWorld!"), TEXT("Information"), 0 );
MessageBox( NULL, _T("你好HelloWorld!"), _T("Information"), 0 ); return 0;
}
用两个宏TEXT或者_T都可以解决这个问题,它们两个并没有太大区别,也许区别在于前者是通过windows.h头文件引入的,而_T是通过tchar.h引入的,我推荐大家使用_T和tchar.h,因为tchar.h还帮助我们引入了其它一些很有用的宏,比如_tcscpy_s,这个宏在使用UNICODE字符集的时候被替换成wcscpy_s,在使用多字节字符集的使用被替换成strcpy_s。关于这部分的内容,请大家不要错过《Windows核心编程核心编程核心编程核心编程》第二章(第四版或第五版都可以)以及《深入学习C++ String2.1》 它们都有提到.
有人听说_T可以把多字节字符串转换成UNICODE,因此他写了如下的代码: const char* pStr = "haha哈哈"; MessageBox( NULL, _T(pStr), _T("Information"), 0 );
当然,除非你运气好的抓狂,否则你是编译不过去的,为什么呢?我们现在应该知道对于"Hello"这样的字符串,VC2010会默认的将它视为const char*,即多字节字符串,而L"Hello"前面有个L前缀的被视为UNICODE字符串,这和C#是有区别的,因为C#的字符串总是被视为UNICODE,C++/CLI下面编译器也会帮助我们做到这件事情,所以它们不需要L(C++/CLI兼容L这种写法)。
让我们看看_T的定义吧:
#define wxCONCAT_HELPER(text, line) text ## line
/* could already be defined by tchar.h (it's quasi standard) */
#ifndef _T
#if !wxUSE_UNICODE
#define _T(x) x
#else /* Unicode */
/* use wxCONCAT_HELPER so that x could be expanded if it's a macro */
#define _T(x) wxCONCAT_HELPER(L, x)
#endif /* ASCII/Unicode */
#endif /* !defined(_T) */
_T在UNICODE下面最终会被替换成L ## x。 ##是一个编译预处理指令,意味着让L和x贴在一起,比如L ## "Hello"最终就是L"Hello",因此它可以把"Hello"转换成UNICODE字符串。
那为什么上面的程序不行呢?让我们看看_T("pStr")会被替换成什么:L ## pStr -> LpStr,哦,LpStr是一个新的标识符,如果你没有定义过它,你当然不能通过编译啦。
因此我们可以了解到_T这样的宏只能处理直接的常量字符串,不能处理其它的情况。
而我们上面演示的那种情况需要我们动态的去转换编码,Windows有API可以帮助我们做到,C库也有函数可以帮助我们。
恰好我曾经写过这样的代码,欢迎大家参考:ASCII/UNICODE/UTF8字字字字符串互相转换的符串互相转换的符串互相转换的符串互相转换的C++代码代码代码代码 对于_T宏,再说一点东西,或许你会感到奇怪为什么_T不直接定义成#define _T(x) L ## x,而要绕个圈子去调用wxCONCAT_HELPER呢?这实际上涉及到宏展开顺序和截断的问题。
在这里,我们需要说一个宏参数的概念,这很函数的参数是类似的,这里_T(x)的x就是宏参数,好,记住下面一句话:
如果你定义的宏中使用了#或者是##的话,宏参数将不会被展开,也就是说_T(x)如果直接定义成L##x那么在下面这种情况就会出错( PS: #是给参数加引号的意思):
_T(__FUNCTION__),__FUNCTION__是一个预定义的宏,它代表了当前函数的名字,这个展开会是什么呢?L__FUNCTION__。
为什么间接调用wxCONCAT_HELPER就能得到正确的结果呢?因为当我们调用wxCONCAT_HELPER的时候,__FUNCTION__已经被_T展开成了函数名。