如何在C++中将PWSTR转换为字符串?

9

I have the following code:

// Fetch Local App Data folder path.
PWSTR localAppData = (PWSTR) malloc(128);
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData);

// Find out the absolute path to chrome.exe
stringstream ss;
ss << localAppData << "/Google/Chrome/Application/chrome.exe";

stringstreamer的.str()结果为错误的008F6788/Google/Chrome/Application/chrome.exe

我似乎无法使stringstreamer起作用,因为它与strcat或wcsncat存在类型不兼容性。

如何将这个PWSTR强制转换为字符串?


看看我的答案,可以将多字节字符和宽字符相互转换。看看Andre的答案,还有另一种方法。如果想知道应该怎么做,可以咨询Praetorian或Tomalak Geret'kal。 - AJG85
5个回答

36

1. 噫!

微软表示

typedef wchar_t* LPWSTR, *PWSTR;

那么让我们从测试用例中将那些可怕的废话剔除掉,丢弃掉那些 C 语言的垃圾:

// Fetch Local App Data folder path.
wchar_t* localAppData = new wchar_t[128];
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData);

stringstream ss;
ss << localAppData << "/Google/Chrome/Application/chrome.exe";

delete[] localAppData;

2. 警告!

这里有一个严重的缺陷。

SHGetKnownFolderPath 实际上会将你提供给它的指针的值设置为指向它分配的内存的地址。你的代码存在内存泄漏问题,而我之前的片段在释放内存时存在微妙的错误。

让我们通过阅读文档来解决这个问题:

ppszPath [out]

Type: PWSTR*

当该方法返回时,包含指向以空字符结尾的Unicode字符串的指针的地址,该字符串指定已知文件夹的路径。 调用进程负责通过调用CoTaskMemFree来释放此资源,一旦不再需要该资源。 返回的路径不包括尾部反斜杠。例如,返回的是"C:\Users"而不是"C:\Users\"。

// Fetch Local App Data folder path.
wchar_t* localAppData = 0;
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData);

stringstream ss;
ss << localAppData << "/Google/Chrome/Application/chrome.exe";

CoTaskMemFree(static_cast<void*>(localAppData));

接下来,开始正文。


3. 宽字符

你的代码语法问题在于 localAppData 是一个 wchar_t 类型,而普通的 stringstream 是基于 char 工作的。

幸运的是,有一种宽字符变体叫做 wstringstream,它使用 wchar_t

(需要注意的是,这意味着你的字面常量也必须由 wchar_t 组成,使用 L 字符串字面常量前缀。)

现在是最终代码:

// Fetch Local App Data folder path.
wchar_t* localAppData = 0;
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData);

wstringstream ss;
ss << localAppData << L"/Google/Chrome/Application/chrome.exe";

CoTaskMemFree(static_cast<void*>(localAppData));

2
@AJG85:我的答案中没有向量。SHGetKnownFolderPath 会为您分配内存。 - Lightness Races in Orbit
我现在如何将 wstringstream 转换为 string?实际上,我对 c_str 感兴趣,因为我将结果用于 stat 并且我可以从 string 中获取 c_str。 - Tower
2
@ Tomalak,你的代码也有问题 :-) CoTaskMemFree接受的是void指针,而不是指向void指针的指针。 - Praetorian
2
谁知道查询操作系统路径听起来如此简单的事情会变成这样一个棘手的问题! - Praetorian
1
@Praetorian:我再说一遍:“呸!微软”;-) - Lightness Races in Orbit
显示剩余3条评论

5

PWSTR是指向宽字符字符串的指针。您需要:

// Fetch Local App Data folder path.
PWSTR localAppData = (PWSTR) malloc(128);
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData);

wstringstream ss;
ss << localAppData << L"/Google/Chrome/Application/chrome.exe";

另外,malloc的参数表示要分配的字节数,因此您正在分配一个只能容纳64个宽字符(包括NULL字符)的缓冲区。您可能需要使用malloc( 128 * sizeof(wchar_t) )
编辑:
SHGetKnownFolderPath的文档中:
当该方法返回时,ppszPath包含指向已知文件夹路径的空字符结尾的Unicode字符串的指针的地址。调用进程负责通过调用CoTaskMemFree释放此资源,一旦不再需要它。
因此,对于函数的最后一个参数,不应该分配任何内存。
wchar_t *localAppData = NULL;
::SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData);

wstringstream ss;
ss << localAppData << L"/Google/Chrome/Application/chrome.exe";
::CoTaskMemFree(localAppData);

这也会失败。你需要在字符串字面值前加上 L,例如 L"..." - André Caron
我现在如何将wstringstream转换为string?实际上,我对c_str感兴趣,因为我在使用stat - Tower
这行代码不会起作用。SHGetKnownFolderPath 分配内存并更改 localAppData 指向它的位置,如果 localAppData 是一个数组,这将是一个问题。 :) - Lightness Races in Orbit
出于好奇并且因为这很难在谷歌上搜索到:在调用API函数之前加上尾随的“::”有什么意义? - Gaspa79
@Gaspa79 这表示全局命名空间(你可以在Google上搜索)。通过在这些函数名称之前使用::,我正在使用限定名称查找,并且没有歧义,可以确定要调用哪些函数。 - Praetorian
显示剩余4条评论

3

我喜欢Praetorian使用宽字符串流的回答,但如果你想进行转换:

char str[128];
wcstombs(str, localAppData, 128);

有另一个函数也可以反向操作:
wchar_t wstr[128];
mbstowcs(wstr, "Hello World", 128);

3
谢谢你把我的用户名搞砸了 :-) - Praetorian
1
弗洛伊德口误?交换两个字母的效果真是惊人;-P - AJG85

2
你不能将宽字符字符串强制转换为字符串。原因(除了 2 字节单元与 1 字节单元的问题之外)是这种“转换”会产生歧义。
源编码是什么?目标编码是什么?在这种情况下,源编码可能是 Unicode(shell 扩展可能返回随机垃圾,如果他们没有使用 Unicode,就不能保证他们执行了有效的 X 到 Unicode 转换)。目标编码可能是 ASCII,尽管它技术上必须匹配系统的当前代码页。
如果你想进行有损的 Unicode 到 ASCII 转换,可以使用 WideCharToMultiByte()

1
大多数其他答案与人们搜索此主题想要的内容无关...谢谢。 - cb88

0

如果你不喜欢宽字符,而非常需要 ANSI 字符串,请尝试使用 wcstombs 或 WideCharToMultiByte。

调用 SHGetKnownFolderPath 时,你不需要自己分配内存,否则会导致内存泄漏。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接