如何在 SHGetKnownFolderPath API 中使用 CString 对象来获取程序数据路径

1 c++ mfc

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

你能告诉我这是否是使用它的正确方法CString吗?如果不是,那么使用 获取 ProgramData 路径的最佳解决方案是SHGetKnownFolderPath()什么?

我使用了下面的 2 个样本并且都有效,但我不确定它们是否正确。

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

或者:

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

Rem*_*eau 6

这两种方法都不正确。

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

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

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

假设该内部指针的地址与CString对象本身的地址相同。 CString不会operator&像您假设的那样重写以返回其内部缓冲区指针的地址。

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

而你假设CString析构函数的使用CoTaskMemFree()(或兼容功能)以释放其内部的字符缓冲区。这是不安全的假设。

而你假设的内存布局CString公司的内部字符缓冲区是通过返回的内存块的内存布局兼容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);
}
Run Code Online (Sandbox Code Playgroud)

如果您想自动销毁返回的内存块,尤其是如果CString分配失败并出现异常,请使用智能指针std::unique_ptr(参见std::unique_ptr、deleters 和 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;  
}
Run Code Online (Sandbox Code Playgroud)