【问题标题】:How to use CString object in SHGetKnownFolderPath API to get programdata path如何在 SHGetKnownFolderPath API 中使用 CString 对象获取程序数据路径
【发布时间】:2017-03-16 18:11:52
【问题描述】:

我使用下面的代码通过SHGetKnowFolderPath() 获取 ProgramData 路径。

您能否告诉我将它与CString 一起使用是否正确?如果没有,使用SHGetKnownFolderPath() 获取 ProgramData 路径的最佳解决方案是什么?

我使用了下面的 2 个示例,两者都有效,但我不确定它们是否正确。

CString szProgramDataPath;  
szProgramDataPath.GetBuffer(MAX_PATH);  
if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, (PWSTR*)&szProgramDataPath)))
{
    cout << _T("Failed, error is: ") << GetLastError();
}

或者:

CString szProgramDataPath;  
if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, (PWSTR*)&szProgramDataPath)))
{
    cout << _T("Failed, error is: ") << GetLastError();
}

【问题讨论】:

  • 以这种方式使用GetBuffer()时,请确保您在之后调用ReleaseBuffer()
  • @Danny_ds 从技术上讲,只有当缓冲区的内容发生变化并且新的字符串长度与更改前不同时,您才需要调用ReleaseBuffer()。否则调用ReleaseBuffer() 是可选的,因为它基本上是无操作的。但是,以防万一,在GetBuffer() 之后调用ReleaseBuffer() 始终是一个好习惯。也许有一天 MS 重新设计了 GetBuffer() 来分配一个临时内存块,ReleaseBuffer() 将它合并到主字符串中。谁知道呢。
  • @RemyLebeau 再次正确 :)

标签: c++ mfc


【解决方案1】:

这两种方法都不正确。

SHGetKnownFolderPath() 返回一个指向新分配的字符串的指针,使用完毕后您必须使用CoTaskMemFree() 释放它。

这两个代码示例都试图使用operator&amp; 直接访问CString 指向其字符数据的内部指针,希望CString 将拥有SHGetKnownFolderPath() 返回的指针的所有权。

这是错误的并且非常危险!有几个原因...

假设该内部指针的地址与CString 对象本身的地址相同。 CString 不会覆盖 operator&amp; 以返回其内部缓冲区指针的地址,就像您假设它所做的那样。

即使是这样,第一个代码示例仍然存在问题,因为它存在内存泄漏。您正在使用GetBuffer() 预先分配字符缓冲区(之后不调用ReleaseBuffer())。 SHGetKnownFolderPath() 在重新分配指向返回的内存块的指针之前不会释放该缓冲区,因此您最终会泄漏较早的内存块。

假设CStrings 析构函数使用CoTaskMemFree()(或兼容函数)来释放其内部字符缓冲区。这是不安全的假设。

假设CStrings 内部字符缓冲区的内存布局与SHGetKnownFolderPath() 返回的内存块的内存布局兼容。实际上并非如此,因为CString 为其字符数据(以及其他内容)实现了一个引用计数器。您认为该计数器存储在哪里?对,在字符缓冲区内部!由于SHGetKnownFolderPath() 不返回与CString 的数据格式兼容的内存缓冲区,因此您将损坏您的CString 数据并在稍后尝试访问内存中不存在的内容时导致问题。有关`CString 的内部细节的更多信息,请参阅CString In A Nutshell

简而言之,CString 并非旨在以您尝试的方式使用。您正在做出一些非常危险的假设,从而导致代码中出现未定义的行为

您不能直接访问CString 的内部缓冲区指针以将其重新分配给不同的地址。 正确的解决方案看起来更像这样:

CString szProgramDataPath;  
LPWSTR pProgramDataPath;

if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &pProgramDataPath)))
{
    // DON'T use _T() here! std::cout expects char* strings only...
    cout << "Failed, error is: " << GetLastError();
}
else
{
    szProgramDataPath = pProgramDataPath;  
    CoTaskMemFree(pProgramDataPath);
}

如果您想自动销毁返回的内存块,特别是如果CString 分配因异常而失败,请使用类似std::unique_ptr 的智能指针(请参阅std::unique_ptr, deleters and the Win32 API),例如:

CString szProgramDataPath;  
LPWSTR pProgramDataPath;

if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &pProgramDataPath)))
{
    // DON'T use _T() here! std::cout expects char* strings only...
    cout << "Failed, error is: " << GetLastError();
}
else
{
    std::unique_ptr<WCHAR, decltype(CoTaskMemFree)> deleter(pProgramDataPath, &CoTaskMemFree);
    szProgramDataPath = pProgramDataPath;  
}

【讨论】:

  • 很好的答案 +1,但这有点令人困惑:“你认为计数器存储在哪里?对,在字符缓冲区本身内部”,因为计数器 (和大小等)是该类的成员,位于指向内部字符缓冲区的指针旁边。
  • @Danny_ds 不符合我在回答中链接到的“CString in a Nutshell”文章。除非微软在写这篇文章后重新设计了CString。但是将引用计数(和其他数据属性)移到数据缓冲区之外是没有意义的,它会使整个重新计数系统和复制构造函数/赋值更难管理。
  • 你是对的(像往常一样:)。记得以前看过源代码好几遍,一定是忽略了或者忘记了这一点。感谢您提神醒脑!
【解决方案2】:

语法是:

HRESULT SHGetKnownFolderPath(
   _In_     REFKNOWNFOLDERID rfid,
   _In_     DWORD            dwFlags,
   _In_opt_ HANDLE           hToken,
   _Out_    PWSTR            *ppszPath
 );

注意第四个参数是PWSTR*,而不是CString

来自MSDN

ppszPath [out]
类型:PWSTR*

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

你需要一个 PWSTR 变量来传递。然后你需要释放指向的资源:

LPWSTR path = NULL;  //LP = "Long pointer" -- same as PWSTR *path;  
SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &path);

...

CoTaskMemFree(path);

在检查SHGetKnownFolderPath 成功返回后,可以直接从LPWSTR path 变量分配CString

CString szProgramDataPath = path;

【讨论】:

  • 您所说的在技术上是正确的,但并未解决实际询问的问题,即如何以正确的方式将返回的路径放入CString
【解决方案3】:

如果我有你的问题,你需要看看“mbstowcs”和“wcstombs”函数,它们在 wchar* 和 char* 之间转换,反之亦然

【讨论】:

    猜你喜欢
    • 2019-08-21
    • 2018-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-20
    • 2020-05-25
    • 2022-01-18
    • 2011-05-16
    相关资源
    最近更新 更多