c – 什么是TCHAR字符串和Win32 API函数的“A”或“W”版本?

参见英文答案 > Strings. TCHAR LPWCS LPCTSTR CString. Whats what here, simple quick                                    2个
什么是TCHAR字符串,例如LPTSTR和LPCTSTR以及如何使用这些字符串?当我在Visual Studio中创建一个新项目时,它为我创建了这个代码:

#include <tchar.h>

int _tmain(int argc, _TCHAR* argv[])
{
   return 0;
}

例如,我如何连接所有命令行参数?

如果我想打开第一个命令行参数给出的名称的文件,我该怎么做? Windows API定义了许多函数的“A”和“W”版本,例如CreateFile,CreateFileA和CreateFileW;那么这些如何彼此不同以及我应该使用哪一个?

解决方法:

首先我要说的是,最好不要将TCHAR用于新的Windows项目,而应直接使用Unicode.关于实际答案:

字符集

我们需要了解的第一件事是字符集在Visual Studio中的工作原理.项目属性页面有一个选项可以选择使用的字符集:

>未设置
>使用Unicode字符集
>使用多字节字符集

根据您选择的三个选项中的哪一个,更改了许多定义以适应所选字符集.有三个主要类:字符串,来自tchar.h的字符串例程和API函数:

>’Not Set’对应于使用ANSI编码的TCHAR = char,其中您使用系统的标准8位代码页来表示字符串.所有tchar.h字符串例程都使用基本的char版本.所有使用字符串的API函数都将使用API​​函数的“A”版本.
>’Unicode’对应于使用UTF-16编码的TCHAR = wchar_t.所有tchar.h字符串例程都使用wchar_t版本.所有使用字符串的API函数都将使用API​​函数的“W”版本.
>’Multi-Byte’对应于TCHAR = char,使用一些多字节编码方案.所有tchar.h字符串例程都使用多字节字符集版本.所有使用字符串的API函数都将使用API​​函数的“A”版本.

相关阅读:About the “Character set” option in visual studio 2010

TCHAR.h标题

tchar.h头是一个帮助器,用于对字符串的C字符串操作使用通用名称,切换到给定字符集的正确函数.例如,_tcscat将切换到strcat(未设置),wcscat(unicode)或_mbscat(mbcs). _tcslen将切换到strlen(未设置),wcslen(unicode)或strlen(mbcs).

通过将所有_txxx符号定义为评估为正确函数的宏来进行切换,具体取决于编译器开关.

它背后的想法是你可以使用编码不可知的类型TCHAR(或_TCHAR)和与它们相关的编码不可知函数,来自tchar.h,而不是string.h中的常规字符串函数.

同样,_tmain被定义为main或wmain.另见:What is the difference between _tmain() and main() in C++?

帮助宏_T(..)被定义为获取正确类型的字符串文字,“常规文字”或L“wchar_t文字”.

请参阅此处提到的警告:Is TCHAR still relevant? — dan04’s answer

_tmain示例

对于问题中main的示例,以下代码将作为命令行参数传递的所有字符串连接成一个.

int _tmain(int argc, _TCHAR *argv[])
{
   TCHAR szCommandLine[1024];

   if (argc < 2) return 0;

   _tcscpy(szCommandLine, argv[1]);
   for (int i = 2; i < argc; ++i)
   {
       _tcscat(szCommandLine, _T(" "));
       _tcscat(szCommandLine, argv[i]);
   }

   /* szCommandLine now contains the command line arguments */

   return 0;
}

(省略了错误检查)此代码适用于字符集的所有三种情况,因为我们使用TCHAR,tchar.h字符串函数和字符串文字_T.在编写此类TCHAR程序时,忘记用_T(..)包围字符串文字是编译器错误的常见原因.
如果我们没有完成所有这些事情,那么切换字符集会导致代码无法编译,或者更糟糕的是,编译但在运行时期间行为异常.

Windows API函数

对字符串起作用的Windows API函数(如CreateFile和GetCurrentDirectory)在Windows标头中实现为宏,与tchar.h宏一样,切换到“A”版本或“W”版本.例如,CreateFile是一个宏,定义为ANSI和MBCS的CreateFileA,以及Unicode的CreateFileW.

无论何时在代码中使用平面形式(没有“A”或“W”),调用的实际函数将根据所选字符集进行切换.您可以使用显式的“A”或“W”名称强制使用特定版本.

结论是您应始终使用非限定名称,除非您希望始终引用特定版本,而与字符集选项无关.

对于问题中的示例,我们要打开第一个参数给出的文件:

int _tmain(int argc, _TCHAR *argv[])
{  
   if (argc < 2) return 1;

   HANDLE hFile = CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

   /* Read from file and do other stuff */
   ...

   CloseHandle(hFile);

   return 0;
}

(省略错误检查)请注意,对于此示例,我们无需使用任何特定于TCHAR的东西,因为宏定义已经为我们处理了这个问题.

利用C字符串

我们已经看到了如何使用tchar.h例程来使用C样式的字符串操作来处理TCHAR,但如果我们可以利用C字符串来处理它,那将会很好.

我的建议最重要的是不使用TCHAR而是直接使用Unicode,请参阅结论部分,但如果您想使用TCHAR,则可以执行以下操作.

要使用TCHAR,我们想要的是使用TCHAR的std :: basic_string实例.您可以通过键入自己的tstring来完成此操作:

typedef std::basic_string<TCHAR> tstring;

对于字符串文字,不要忘记使用_T.

您还需要使用正确版本的cin和cout.您可以使用引用来实现tcin和tcout:

#if defined(_UNICODE)
std::wistream &tcin = wcin;
std::wostream &tcout = wcout;
#else
std::istream &tcin = cin;
std::ostream &tcout = cout;
#end

这应该可以让你几乎做任何事情.可能偶尔会出现异常,例如std :: to_string和std :: to_wstring,您可以找到类似的解决方法.

结论

这个答案(希望如此)详细说明了TCHAR是什么以及它如何与Visual Studio和Windows标头一起使用和交织在一起.但是,我们也应该想知道是否要使用它.

我的建议是直接对所有新的Windows程序使用Unicode,而根本不使用TCHAR!

其他人给出了同样的建议:Is TCHAR still relevant?

要在创建新项目后使用Unicode,请首先确保将字符集设置为Unicode.然后,删除#include< tchar.h>来自您的源文件(或来自stdafx.h).修复任何TCHAR或_TCHAR到wchar_t和_tmain到wmain:

int wmain(int argc, wchar_t *argv[])

对于非控制台项目,Windows应用程序的入口点是WinMain,并且将以TCHAR术语显示为

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR    lpCmdLine, int nCmdShow)

并且应该成为

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR    lpCmdLine, int nCmdShow)

在此之后,只使用wchar_t字符串和/或std :: wstrings.

进一步的警告

>在使用TCHAR数组(字符串)时写入sizeof(szMyString)时要小心,因为对于ANSI,这是字符和字节的大小,对于Unicode,这只是字节大小,字符数最多为一半,对于MBCS,这是以字节为单位的大小,字符数可能相等也可能不相等. Unicode和MBCS都可以使用多个TCHAR来编码单个字符.
>混合TCHAR的东西和固定的char或wchar_t是非常烦人的;你必须使用正确的代码页将字符串从一个转换为另一个!简单副本在一般情况下不起作用.
>如果要有条件地定义自己的函数,_UNICODE和UNICODE之间会略有不同.见Why both UNICODE and _UNICODE?

一个非常好的,互补的答案是:Difference between MBCS and UTF-8 on Windows

上一篇:c-无法从’const char *’转换为’LPCTSTR’


下一篇:Go语言常用类型转换