字符串.TCHAR LPWCS LPCTSTR CString.什么在这里,简单快捷

2 c++ string winapi

TCHAR szExeFileName[MAX_PATH]; 
GetModuleFileName(NULL, szExeFileName, MAX_PATH);

CString tmp;
lstrcpy(szExeFileName, tmp);
CString out;
out.Format("\nInstall32 at %s\n", tmp);
TRACE(tmp);
Run Code Online (Sandbox Code Playgroud)

错误(格式化):

error C2664: 'void ATL::CStringT<BaseType,StringTraits>::Format(const wchar_t 
*,...)' : cannot convert parameter 1 from 'const char [15]' to 'const wchar_t
Run Code Online (Sandbox Code Playgroud)

我只想获得该程序启动的当前路径并将其复制到CString中,以便我可以在其他地方使用它.我目前正试图通过TRACE来看看它的路径.但字符串,字符,字符数组,我不可能得到所有的海峡.有人可以给我指针吗?

Adr*_*thy 10

接受的答案解决了这个问题.但问题还要求更好地理解Windows上所有字符类型之间的差异.

编码

一个char在Windows(和几乎所有其他系统)是一个字节.一个字节通常被解释为无符号值[0..255]或有符号值[-128..127].(较旧的C++标准保证只有[-127..127]的有符号范围,但大多数实现给出[-128..127].我相信C++ 11保证更大的范围.)

ASCII是[0..127]范围内的值到特定字符的字符映射,因此您可以将ASCII字符存储在有符号字节或无符号字节中,因此它总是适合a char.

但ASCII并不具备大多数语言所需的所有字符,因此通常使用字节中可用的其余值来扩展字符集,以表示某些语言(或语言系列)所需的其他字符.因此,虽然[0..127]几乎总是意味着相同的东西,但像150这样的值只能在特定编码的上下文中解释.对于单字节字母表,这些编码称为代码页.

代码页有所帮助,但它们并没有解决所有问题.您始终必须知道特定文档使用哪个代码页才能正确解释它.此外,您通常无法编写使用不同语言的单个文档.

此外,某些语言的字符数超过256个,因此无法char将一个字符映射到一个字符.这导致了多字节字符编码的发展,其中[0..127]仍然是ASCII,但是其他一些值是"转义",这意味着你必须查看一些后续的chars以找出什么字符你真的有.(最好将多字节视为变量字节,因为某些字符只需要一个字节,而其他字符只需要两个或更多字节.)多字节有效,但编码很麻烦.

与此同时,内存变得越来越丰富,因此许多组织聚集在一起创建了Unicode,其目标是将值通用映射到字符(对于"字符"的适当模糊定义).最初,人们相信所有角色(或者至少是所有人都会使用的角色)都适合16位值,这很好,因为你不必处理多字节编码 - 你只需要每个字符使用两个字节而不是一个字节.大约在这个时候,微软决定采用Unicode作为Windows中文本的内部表示.

WCHAR

所以Windows有一个叫做的类型WCHAR,一个代表"Unicode""字符"的双字节值.我在这里使用引号因为Unicode经过了原始的双字节编码,所以Windows称之为"Unicode"的并不是今天的Unicode - 它实际上是Unicode的特殊编码,称为UTF-16.并且"字符"在Unicode中并不像在ASCII中那样简单,因为在某些语言中,字符以有趣的方式组合或以其他方式影响相邻字符.

较新版本的Windows WCHAR在内部使用这些16位值作为文本,但是仍然有很多代码用于单字节代码页,甚至一些代码用于多字节编码.那些程序仍然使用chars而不是WCHARs.许多这些程序必须与使用旧版本Windows的人一起工作,这些Windows仍然使用char内部以及使用的新版本WCHAR.因此,使用C宏和类型定义的技术进行设计,让你可以多为编写代码的一种方式,并-在编译时-选择将它使用两种charWCHAR.

TCHAR

要实现这种灵活性,可以使用a TCHAR作为"文本字符".在一些头文件(通常是<tchar.h>),TCHAR将被Typedef的要么charWCHAR取决于编译时环境.Windows标头采用如下惯例:

  • LPTSTR是一个指向TCHARs 字符串的(长)指针.
  • LPWSTR是一个指向WCHARs 字符串的(长)指针.
  • LPSTR是一个指向chars 字符串的(长)指针.

(L"long"是16位日的剩余时间,当时我们有长,远,近指针.这些都已经过时了,但L前缀仍然存在.)

获取和返回字符串的大多数Windows API函数实际上都被两个版本替换:A版本(对于"ANSI"字符)和W版本(对于宽字符).(同样,历史遗留在这些中显示.代码页方案通常被称为ANSI代码页,但我从未清楚它们是否实际上由ANSI标准统治.)

因此,当您调用这样的Windows API时:

SetWindowText(hwnd, lptszTitle);
Run Code Online (Sandbox Code Playgroud)

你真正做的是调用预处理器宏扩展为SetWindowTextASetWindowTextW.它应该与TCHAR定义一致.也就是说,如果你想要chars的字符串,你将得到A版本,如果你想要WCHARs的字符串,你就得到了W版本.

但由于字符串文字,它有点复杂.如果你这样写:

SetWindowText(hwnd, "Hello World");  // works only in "ANSI" mode
Run Code Online (Sandbox Code Playgroud)

那么只有在你定位char版本时才会编译,因为它"Hello World"是一个chars 字符串,所以它只与SetWindowTextA版本兼容.如果你想要这个WCHAR版本,你必须写:

SetWindowText(hwnd, L"Hello World");  // only works in "Unicode" mode
Run Code Online (Sandbox Code Playgroud)

L意味着你想要宽字符.(L实际上代表很长,但它与上面的长指针不同.)当编译器L在字符串上看到前缀时,它知道字符串应该编码为一系列wchar_ts而不是chars.

(针对Windows的编译器使用两个字节的值wchar_t,这恰好与Windows定义的相同WCHAR.针对其他系统的编译器通常使用四字节值wchar_t,这是保存单个Unicode代码点所需的实际值. )

因此,如果您想要能够以任何方式编译的代码,则需要另一个宏来包装字符串文字.有两种可供选择: _T()TEXT().它们的工作方式完全相同.第一个来自编译器的库,第二个来自OS的库.所以你写这样的代码:

SetWindowText(hwnd, TEXT("Hello World"));  // compiles in either mode
Run Code Online (Sandbox Code Playgroud)

如果您的目标char是s,则宏是一个只返回常规字符串文字的无操作.如果你的目标WCHAR是s,宏会预先设定L.

那么如何告诉编译器你想要定位WCHAR?你定义UNICODE_UNICODE.前者用于Windows API,后者用于编译器库.确保你永远不要定义一个没有另一个.

  • 我认为你违反了问题的"简单快速"部分,但是为了彻底的+1. (2认同)