2
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);

错误(在格式):

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

我只想获取启动该程序的当前路径并将其复制到 CString 中,以便我可以在其他地方使用它。我目前只是试图通过追踪它来查看路径。但是字符串、字符、字符数组,我永远都搞不定。有人可以给我指点吗?

4

2 回答 2

11

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

编码

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

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

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

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

此外,某些语言的字符数超过 256 个,因此无法一对一地映射char字符。这导致了多字节字符编码的发展,其中 [0..127] 仍然是 ASCII,但其他一些值是“转义”,这意味着您必须查看一些后续char的 s 才能确定什么字符你真的有。(最好将多字节视为可变字节,因为有些字符只需要一个字节,而另一些则需要两个或更多。)多字节有效,但编写代码很痛苦。

与此同时,内存变得越来越充裕,因此许多组织聚集在一起创建了 Unicode,其目标是进行值到字符的通用映射(对于“字符”的适当模糊定义)。最初,人们认为所有字符(或至少任何人会使用的所有字符)都适合 16 位值,这很好,因为您不必处理多字节编码——您只需每个字符使用两个字节而不是一个。大约在这个时候,微软决定采用 Unicode 作为 Windows 中文本的内部表示。

WCHAR

所以 Windows 有一个类型叫做WCHAR,一个两字节的值,代表一个“Unicode”“字符”。我在这里使用引号是因为 Unicode 是从最初的两字节编码演变而来的,所以 Windows 所称的“Unicode”在今天并不是真正的 Unicode——它实际上是一种特殊的 Unicode 编码,称为 UTF-16。并且“字符”在 Unicode 中并不像在 ASCII 中那样简单,因为在某些语言中,字符以有趣的方式组合或以其他方式影响相邻字符。

较新版本的 Windows 在内部将这些 16 位WCHAR值用于文本,但仍有很多代码是为单字节代码页编写的,甚至还有一些是为多字节编码编写的。那些程序仍然使用chars 而不是WCHARs。并且其中许多程序必须与使用旧版本 Windows 的人一起工作,这些旧版本的 Windows 仍然char在内部使用 s 以及新版本的使用WCHAR. 因此,设计了一种使用 C 宏和 typedef 的技术,这样您就可以在大多数情况下以一种方式编写代码,并且 - 在编译时 - 选择让它使用charWCHAR.

TCHAR

为了实现这种灵活性,您可以使用 aTCHAR作为“文本字符”。在某些头文件(通常是<tchar.h>)中,TCHAR将根据编译时环境将类型定义为char或。WCHARWindows 标头采用如下约定:

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

Lfor“long”是 16 位时代的遗留物,当时我们有 long、far 和 near 指针。今天这些都已经过时了,但L前缀往往会保留下来。)

大多数接受和返回字符串的 Windows API 函数实际上都替换为两个版本:A版本(用于“ANSI”字符)和W版本(用于宽字符)。(同样,这些历史遗留问题。代码页方案通常被称为 ANSI 代码页,尽管我一直不清楚它们是否真的由 ANSI 标准统治。)

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

SetWindowText(hwnd, lptszTitle);

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

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

SetWindowText(hwnd, "Hello World");  // works only in "ANSI" mode

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

SetWindowText(hwnd, L"Hello World");  // only works in "Unicode" mode

这里的L意思是你想要宽字符。(theL实际上代表 long,但它与上面的 long 指针的 long 含义不同。)当编译器看到L字符串上的前缀时,它知道字符串应该被编码为一系列wchar_ts 而不是chars。

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

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

SetWindowText(hwnd, TEXT("Hello World"));  // compiles in either mode

如果您以chars 为目标,则该宏是一个只返回常规字符串文字的无操作。如果您以WCHARs 为目标,则宏会在L.

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

于 2013-04-17T18:11:36.637 回答
3

我的猜测是您正在以 Unicode 模式编译。

尝试将格式字符串包含在 _T 宏中,该宏旨在提供始终正确的方法来提供常量字符串参数,无论您是在 Unicode 还是 ANSI 模式下编译:

out.Format(_T("\nInstall32 at %s\n"), tmp);
于 2013-04-17T13:25:57.887 回答