C++ 如何在SHGetKnownFolderPath API中使用CString对象获取程序数据路径
我使用下面的代码使用SHGetKnowFolderPath获取ProgramData路径 你能告诉我这是否是使用CString的正确方法吗?如果没有,那么使用SHGetKnownFolderPath获取ProgramData路径的最佳解决方案是什么 我使用了下面的两个示例,两个都有效,但我不确定它们是否正确C++ 如何在SHGetKnownFolderPath API中使用CString对象获取程序数据路径,c++,mfc,C++,Mfc,我使用下面的代码使用SHGetKnowFolderPath获取ProgramData路径 你能告诉我这是否是使用CString的正确方法吗?如果没有,那么使用SHGetKnownFolderPath获取ProgramData路径的最佳解决方案是什么 我使用了下面的两个示例,两个都有效,但我不确定它们是否正确 CString szProgramDataPath; szProgramDataPath.GetBuffer(MAX_PATH); if (FAILED(SHGetKnownFold
CString szProgramDataPath;
szProgramDataPath.GetBuffer(MAX_PATH);
if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, (PWSTR*)&szProgramDataPath)))
{
cout << _T("Failed, error is: ") << GetLastError();
}
或:
语法是:
HRESULT SHGetKnownFolderPath(
_In_ REFKNOWNFOLDERID rfid,
_In_ DWORD dwFlags,
_In_opt_ HANDLE hToken,
_Out_ PWSTR *ppszPath
);
注意,第四个参数是PWSTR*而不是CString
发件人:
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;
语法是:
HRESULT SHGetKnownFolderPath(
_In_ REFKNOWNFOLDERID rfid,
_In_ DWORD dwFlags,
_In_opt_ HANDLE hToken,
_Out_ PWSTR *ppszPath
);
注意,第四个参数是PWSTR*而不是CString
发件人:
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;
这两种方法都不正确
SHGetKnownFolderPath返回指向新分配字符串的指针,在使用完该字符串后,必须使用CoTaskMemFree释放该字符串
这两个代码示例都试图使用operator&直接访问CString的内部指针,指向其字符数据,希望CString将拥有SHGetKnownFolderPath返回的指针
这是错误的,非常危险!有几个原因
假设该内部指针的地址与CString对象本身的地址相同。CString不会重写运算符&返回其内部缓冲区指针的地址,就像您假设的那样
即使它这样做了,第一个代码示例仍然被破坏,因为它存在内存泄漏。您正在使用GetBuffer预分配字符缓冲区,之后不调用ReleaseBuffer。SHGetKnownFolderPath在将指针重新分配到返回的内存块之前不会释放该缓冲区,因此最终会泄漏先前的内存块
假设CStrings析构函数使用CoTaskMemFree或compatible函数来释放其内部字符缓冲区。这是不安全的假设
假设CStrings内部字符缓冲区的内存布局与SHGetKnownFolderPath返回的内存块的内存布局兼容。事实并非如此,因为CString为其字符数据和其他内容实现了一个引用计数器。你认为那个计数器存放在哪里?对,在角色缓冲区内部!由于SHGetKnownFolderPath不返回与CString的数据格式兼容的内存缓冲区,因此您将损坏CString数据,并在以后尝试访问内存中不存在的内容时导致问题。有关“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);
}
这两种方法都不正确
SHGetKnownFolderPath返回指向新分配字符串的指针,在使用完该字符串后,必须使用CoTaskMemFree释放该字符串
这两个代码示例都试图使用operator&直接访问CString的内部指针,指向其字符数据,希望CString将拥有SHGetKnownFolderPath返回的指针
这是错误的,非常危险!有几个原因
假设该内部指针的地址与CString对象本身的地址相同。CString不会重写运算符&返回其内部缓冲区指针的地址,就像您假设的那样
甚至
如果是这样,第一个代码示例仍然会被破坏,因为它存在内存泄漏。您正在使用GetBuffer预分配字符缓冲区,之后不调用ReleaseBuffer。SHGetKnownFolderPath在将指针重新分配到返回的内存块之前不会释放该缓冲区,因此最终会泄漏先前的内存块
假设CStrings析构函数使用CoTaskMemFree或compatible函数来释放其内部字符缓冲区。这是不安全的假设
假设CStrings内部字符缓冲区的内存布局与SHGetKnownFolderPath返回的内存块的内存布局兼容。事实并非如此,因为CString为其字符数据和其他内容实现了一个引用计数器。你认为那个计数器存放在哪里?对,在角色缓冲区内部!由于SHGetKnownFolderPath不返回与CString的数据格式兼容的内存缓冲区,因此您将损坏CString数据,并在以后尝试访问内存中不存在的内容时导致问题。有关“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);
}
如果我有你的问题,你需要看看mbstowcs和wcstombs函数,它们在wchar*和char*之间转换,反之亦然如果我有你的问题,你需要看看mbstowcs和wcstombs函数,它们在wchar*和char*之间转换,反之亦然,确保以后调用ReleaseBuffer。@从技术上讲,只有当缓冲区的内容发生更改且新字符串长度与更改前不同时,才需要调用ReleaseBuffer。否则调用ReleaseBuffer是可选的,因为它基本上是不可操作的。但是,在GetBuffer之后调用ReleaseBuffer总是一种好的做法,以防万一。也许有一天MS会重新设计GetBuffer来分配一个临时内存块,然后ReleaseBuffer将其合并到主字符串中。谁知道呢。@RemyLebeau又对了:用这种方式使用GetBuffer时,一定要在以后调用ReleaseBuffer。@Danny\d从技术上讲,只有当缓冲区的内容发生变化并且新的字符串长度与变化前不同时,才需要调用ReleaseBuffer。否则调用ReleaseBuffer是可选的,因为它基本上是不可操作的。但是,在GetBuffer之后调用ReleaseBuffer总是一种好的做法,以防万一。也许有一天MS会重新设计GetBuffer来分配一个临时内存块,然后ReleaseBuffer将其合并到主字符串中。谁知道呢。@RemyLebeau又对了:很好的答案+1,但这有点让人困惑:你认为计数器存储在哪里?对,在字符缓冲区内部,因为计数器和大小等是类的一个成员,在指向内部字符缓冲区的指针旁边。@Danny_不符合我在回答中链接到的一篇简单文章中的CString。除非微软在那篇文章发表后重新设计了CString。但是,将引用计数和其他数据属性移到数据缓冲区之外是没有意义的,这会使整个重新计数系统和复制构造函数/赋值更难管理。您像往常一样是对的:。我记得在过去几次阅读源代码,一定是忽略了或忘记了这一点。谢谢你让我头脑清醒!很好的答案+1,但这有点让人困惑:你认为计数器存储在哪里?对,在字符缓冲区内部,因为计数器和大小等是类的一个成员,在指向内部字符缓冲区的指针旁边。@Danny_不符合我在回答中链接到的一篇简单文章中的CString。除非微软在那篇文章发表后重新设计了CString。但是,将引用计数和其他数据属性移到数据缓冲区之外是没有意义的,这会使整个重新计数系统和复制构造函数/赋值更难管理。您像往常一样是对的:。我记得在过去几次阅读源代码,一定是忽略了或忘记了这一点。谢谢你让我头脑清醒!您所说的在技术上是正确的,但并没有解决实际提出的如何以正确的方式将返回路径导入CString的问题。您所说的在技术上是正确的,但没有解决实际提出的如何以正确的方式将返回路径导入CString的问题。