c++ - 如何使用 SHGetKnownFolderPath API 中的 CString 对象获取程序数据路径

标签 c++ mfc

我使用下面的代码通过 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();
}

最佳答案

这两种方法都不正确。

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

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

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

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

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

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

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

简而言之,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;  
}

关于c++ - 如何使用 SHGetKnownFolderPath API 中的 CString 对象获取程序数据路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42841832/

相关文章:

c++ - 请解释在c++线程中条件变量的使用,以及为什么我们需要同时使用 `unique_lock`和 `mutex`

c++ - OCX AfxOleRegisterTypeLib失败,错误0x80040200

c++ - 如果其他进程被杀死,则杀死我的进程

c++ - Windows 上使用 C++ 的异步对话框

c++ - 在 MFC 中调整控件大小

c++ - 数据未存储在数组中

c++ - 在 VC 2010 中禁用 C++0x 功能?

c++ - 什么是 CInternetSession 的必要清理

c++ - 从qt中的另一个线程运行qtconcurrent时如何关闭程序

c++ - `-1.$e+000` 是什么意思?