什么是 TCHAR 字符串和 'A' 或 'W' 版本的 Win32 API 函数?
What are TCHAR strings and the 'A' or 'W' version of Win32 API functions?
什么是 TCHAR
字符串,例如 LPTSTR
和 LPCTSTR
,我该如何使用它们?当我在 Visual Studio 中创建一个新项目时,它会为我创建此代码:
#include <tchar.h>
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
例如,我如何连接所有命令行参数?
如果我想用第一个命令行参数给定的名称打开一个文件,我该怎么做? Windows API 定义了其许多函数的 'A' 和 'W' 版本,例如 CreateFile
、CreateFileA
和 CreateFileW
;那么它们之间有何不同,我应该使用哪一个?
首先我要说的是,您最好不要对新的 Windows 项目使用 TCHAR
,而是直接使用 Unicode。关于实际答案:
字符集
我们首先需要了解的是字符集在Visual Studio中是如何工作的。项目 属性 页面有一个选项 select 使用的字符集:
- 未设置
- 使用 Unicode 字符集
- 使用Multi-Byte字符集
根据您选择的三个选项中的哪一个,许多定义会发生变化以适应 selected 字符集。主要有三个 类:字符串、来自 tchar.h
的字符串例程和 API 函数:
- 'Not Set' 对应于
TCHAR = char
使用 ANSI 编码,其中您使用系统的标准 8 位代码页来表示字符串。所有 tchar.h
字符串例程都使用基本的 char
版本。所有使用字符串的 API 函数都将使用 API 函数的 'A' 版本。
- 'Unicode' 对应使用 UTF-16 编码的
TCHAR = wchar_t
。所有 tchar.h
字符串例程都使用 wchar_t
版本。所有使用字符串的 API 函数都将使用 API 函数的 'W' 版本。
- 'Multi-Byte'对应
TCHAR = char
,使用了一些multi-byte编码方案。所有 tchar.h
字符串例程都使用 multi-byte 字符集版本。所有使用字符串的 API 函数都将使用 API 函数的 'A' 版本。
相关阅读:About the "Character set" option in visual studio 2010
TCHAR.hheader
tchar.h
header 是一个帮助程序,用于对字符串的 C 字符串操作使用通用名称,切换到给定字符集的正确函数。例如,_tcscat
将切换为 strcat
(未设置)、wcscat
(unicode)或 _mbscat
(mbcs)。 _tcslen
将切换到 strlen
(未设置)、wcslen
(unicode)或 strlen
(mbcs)。
通过将所有 _txxx
符号定义为计算正确函数的宏来实现切换,具体取决于编译器切换。
其背后的想法是,您可以使用 encoding-agnostic 类型 TCHAR
(或 _TCHAR
)和适用于它们的 encoding-agnostic 函数,来自 tchar.h
,而不是 string.h
.
中的常规字符串函数
类似地,_tmain
被定义为 main
或 wmain
。另见:What is the difference between _tmain() and main() in C++?
定义了一个辅助宏 _T(..)
以获取正确类型的字符串文字,"regular literals"
或 L"wchar_t literals"
。
请参阅此处提到的注意事项: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 header 中实现作为宏,就像 tchar.h
宏一样,切换到 'A' 版本或 'W' 版本。例如,CreateFile
是一个宏,对于 ANSI 和 MBCS 定义为 CreateFileA
,对于 Unicode 定义为 CreateFileW
。
每当您在代码中使用平面形式(没有 'A' 或 'W')时,实际调用的函数将根据 selected 字符集进行切换。您可以通过使用明确的 '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
s,但是如果我们可以利用 C++ string
s 来处理这个。
我的建议最重要的是不要使用 TCHAR
而是直接使用 Unicode,请参阅结论部分,但如果您想使用 TCHAR
,您可以执行以下操作。
要使用TCHAR
,我们需要的是使用TCHAR
的std::basic_string
实例。您可以通过 typedef
ing 您自己的 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 header 交织在一起的。但是,我们也应该想知道我们是否要使用它。
我的建议是对所有新 Windows 程序直接使用 Unicode,根本不要使用 TCHAR
!
其他人给出同样的建议:Is TCHAR still relevant?
新建项目后要使用Unicode,首先确保字符集设置为Unicode。然后,从源文件(或 stdafx.h
)中删除 #include <tchar.h>
。将 TCHAR
或 _TCHAR
修正为 wchar_t
并将 _tmain
修正为 wmain
:
int wmain(int argc, wchar_t *argv[])
对于 non-console 项目,Windows 应用程序的入口点是 WinMain
并且在 TCHAR
-jargon 中将显示为
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
应该变成
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
之后,只使用wchar_t
个字符串and/orstd::wstring
s.
进一步注意事项
- 在使用
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
什么是 TCHAR
字符串,例如 LPTSTR
和 LPCTSTR
,我该如何使用它们?当我在 Visual Studio 中创建一个新项目时,它会为我创建此代码:
#include <tchar.h>
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
例如,我如何连接所有命令行参数?
如果我想用第一个命令行参数给定的名称打开一个文件,我该怎么做? Windows API 定义了其许多函数的 'A' 和 'W' 版本,例如 CreateFile
、CreateFileA
和 CreateFileW
;那么它们之间有何不同,我应该使用哪一个?
首先我要说的是,您最好不要对新的 Windows 项目使用 TCHAR
,而是直接使用 Unicode。关于实际答案:
字符集
我们首先需要了解的是字符集在Visual Studio中是如何工作的。项目 属性 页面有一个选项 select 使用的字符集:
- 未设置
- 使用 Unicode 字符集
- 使用Multi-Byte字符集
根据您选择的三个选项中的哪一个,许多定义会发生变化以适应 selected 字符集。主要有三个 类:字符串、来自 tchar.h
的字符串例程和 API 函数:
- 'Not Set' 对应于
TCHAR = char
使用 ANSI 编码,其中您使用系统的标准 8 位代码页来表示字符串。所有tchar.h
字符串例程都使用基本的char
版本。所有使用字符串的 API 函数都将使用 API 函数的 'A' 版本。 - 'Unicode' 对应使用 UTF-16 编码的
TCHAR = wchar_t
。所有tchar.h
字符串例程都使用wchar_t
版本。所有使用字符串的 API 函数都将使用 API 函数的 'W' 版本。 - 'Multi-Byte'对应
TCHAR = char
,使用了一些multi-byte编码方案。所有tchar.h
字符串例程都使用 multi-byte 字符集版本。所有使用字符串的 API 函数都将使用 API 函数的 'A' 版本。
相关阅读:About the "Character set" option in visual studio 2010
TCHAR.hheader
tchar.h
header 是一个帮助程序,用于对字符串的 C 字符串操作使用通用名称,切换到给定字符集的正确函数。例如,_tcscat
将切换为 strcat
(未设置)、wcscat
(unicode)或 _mbscat
(mbcs)。 _tcslen
将切换到 strlen
(未设置)、wcslen
(unicode)或 strlen
(mbcs)。
通过将所有 _txxx
符号定义为计算正确函数的宏来实现切换,具体取决于编译器切换。
其背后的想法是,您可以使用 encoding-agnostic 类型 TCHAR
(或 _TCHAR
)和适用于它们的 encoding-agnostic 函数,来自 tchar.h
,而不是 string.h
.
类似地,_tmain
被定义为 main
或 wmain
。另见:What is the difference between _tmain() and main() in C++?
定义了一个辅助宏 _T(..)
以获取正确类型的字符串文字,"regular literals"
或 L"wchar_t literals"
。
请参阅此处提到的注意事项: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 header 中实现作为宏,就像 tchar.h
宏一样,切换到 'A' 版本或 'W' 版本。例如,CreateFile
是一个宏,对于 ANSI 和 MBCS 定义为 CreateFileA
,对于 Unicode 定义为 CreateFileW
。
每当您在代码中使用平面形式(没有 'A' 或 'W')时,实际调用的函数将根据 selected 字符集进行切换。您可以通过使用明确的 '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
s,但是如果我们可以利用 C++ string
s 来处理这个。
我的建议最重要的是不要使用 TCHAR
而是直接使用 Unicode,请参阅结论部分,但如果您想使用 TCHAR
,您可以执行以下操作。
要使用TCHAR
,我们需要的是使用TCHAR
的std::basic_string
实例。您可以通过 typedef
ing 您自己的 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 header 交织在一起的。但是,我们也应该想知道我们是否要使用它。
我的建议是对所有新 Windows 程序直接使用 Unicode,根本不要使用 TCHAR
!
其他人给出同样的建议:Is TCHAR still relevant?
新建项目后要使用Unicode,首先确保字符集设置为Unicode。然后,从源文件(或 stdafx.h
)中删除 #include <tchar.h>
。将 TCHAR
或 _TCHAR
修正为 wchar_t
并将 _tmain
修正为 wmain
:
int wmain(int argc, wchar_t *argv[])
对于 non-console 项目,Windows 应用程序的入口点是 WinMain
并且在 TCHAR
-jargon 中将显示为
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
应该变成
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
之后,只使用wchar_t
个字符串and/orstd::wstring
s.
进一步注意事项
- 在使用
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