SHGetFolderPath已被弃用:有没有替代方法来检索Windows文件夹的路径?

8

SHGetFolderPath()函数从Windows Vista开始已经被弃用:http://msdn.microsoft.com/en-us/library/bb762181%28v=VS.85%29.aspx

在Windows中,检索应用程序文件夹路径的替代方法是什么?

SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath)

除此之外,为什么在使用这个函数时我会收到那些错误信息:
Error   1   error C2065: 'CSIDL_COMMON_APPDATA' : undeclared identifier 

Error   2   error C3861: 'SHGetFolderPath': identifier not found

请查看以下链接:http://msdn.microsoft.com/en-us/library/windows/desktop/bb762181(v=vs.85).aspx,http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188(v=vs.85).aspx和http://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx。 - enhzflep
5个回答

15
该替代方法在您提供的文档中有介绍。它是 SHGetKnownFolderPath
然而,SHGetKnownFolderPath 仅适用于 Vista 或更高版本。因此,如果您使用加载时链接,并运行调用 SHGetKnownFolderPath 的 XP 程序,则该程序将无法启动。如果您想支持 XP,则这显然是一个问题。
现在,您可以切换到运行时链接 SHGetKnownFolderPath。在调用它之前进行版本检查,如果该函数不可用,则回退到 SHGetFolderPath
个人而言,我不会过于担心这种废弃。微软以保持向后兼容性而著称。不要指望 SHGetFolderPath 很快消失。您将发现 SHGetFolderPath 存在于 Windows 8 中,我希望它在未来 10 年内的任何 Windows 版本中仍然存在。我的建议是坚持使用加载时链接,并在放弃对 XP 的支持时才切换到 SHGetKnownFolderPath
您的另一个问题是如何调用 SHGetFolderPath。您需要遵守 MSDN 文档主题底部列出的要求。具体来说,包括 Shlobj.h 并将 Shlobj.lib 传递给链接器。

但是如何进入应用程序数据文件夹? - user88004
2
那是完全不同的问题。我更新了答案来涵盖它,但我建议您将来一次只提一个问题。 - David Heffernan
@DavidHeffernan - 这是非常好的解释。我已经更新了一些新的 VBA 代码,针对 Win 10+ 平台的操作系统。谢谢! - Leo Gurdian

2

它直接链接在顶部,SHGetKnownFolderPath

在新的API中,CSIDL_COMMON_APPDATA被FOLDERID_ProgramData所取代。


但是如何进入应用程序数据文件夹? - user88004

1
当我向已经工作的解决方案中添加了几个新的头文件时,我遇到了相同的一系列错误。我已经调用了SHGetFolderPath并且还包括了#include <ShlObj.h>,但它在一个不同的头文件中。在我向其中添加新的库头文件之前,该解决方案可以编译而没有任何错误。
我尝试使用SHGetKnownFolderPath()替换SHGetFolderPath(),但这只是将未找到标识符错误重定向到SHGetKnownFolderPath
在调用SHGetFolderPath的类的头文件中添加#include <ShlObj.h>,错误就消失了,解决方案再次成功编译。
this页面所述,在Windows Vista或更高版本的操作系统上调用SHGetFolderPath将在内部调用SHGetKnownFolderPath

0

我已经在Windows 10 PC上使用Visual Studio 2015 Enterprise测试了SHGetFolderPath(),并且编译和查找当前用户的主文件夹工作正常。在Windows Dev Center页面上关于SHGetFolderPath()SHGetFolderPath function有以下说明:

注意 自Windows Vista以来,此函数仅是SHGetKnownFolderPath的包装器。 CSIDL值被翻译为其关联的KNOWNFOLDERID,然后调用SHGetKnownFolderPath。新应用程序应使用已知文件夹系统而不是仅支持向后兼容性的旧CSIDL系统。

正如David Heffman在他的回答中指出的那样,微软有一个长期保持向后兼容性的历史,特别是当他们可以采用旧函数并将其重定向到具有适当参数的新函数时。 CSIDL 值似乎有相应的 KNOWNFOLDERID 值。请参见此 CSIDL 常量表,其中包含简要注释和相应的 KNOWNFOLDERID 值。

以下是使用该函数的示例。此用法检索当前用户的用户文件夹(例如,在 Windows 7 下为 "C:\Users\myuser\Documents"),然后使用 PathAppend() 函数将文件夹名称添加到路径末尾。

TCHAR   achDevice[MAX_PATH];
HRESULT  hr;
// include file ShlObj.h contains list of CSIDL defines however only a subset
// are supported with Windows 7 and later.
// for the 3rd argument, hToken, can be a specified Access Token or SSID for
// a user other than the current user. Using NULL gives us the current user.
if (SUCCEEDED(hr = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, achDevice))) {
    // append a folder name to the user's Documents directory.
    // the Path Handling functions are pretty handy.
    PathAppend(achDevice, L"xxx");
}

可能的一个失败原因是一个或多个无效参数(hr == E_INVALIDARG)。返回值为S_OK表示调用成功。

有一些CSIDL常量可以用于修改函数的结果,例如使用按位OR运算符的CSIDL_FLAG_CREATE。我不确定这些运算符在Windows 7及更高版本中的工作情况如何。

在Windows 7及更高版本中,支持的CSIDL常量有限制。在Active Directory或类似环境中的复杂、远程挂载、重定向和/或共享文件夹中可能存在一些问题需要解决。

另请参阅KNOWNFOLDERID,其中包括一个表格,指出了CSIDLSHGetFolderPath()的一些限制。以下是表格中一些有用的CSIDL常量的示例。

CSIDL_LOCAL_APPDATA - %USERPROFILE%\AppData\Local
CSIDL_MYDOCUMENTS - %USERPROFILE%\Document
CSIDL_PERSONAL -    %USERPROFILE%\Documents
CSIDL_FONTS -       %windir%\Fonts
CSIDL_MYMUSIC -     %USERPROFILE%\Music
CSIDL_MYPICTURES -  %USERPROFILE%\Pictures
CSIDL_COMMON_APPDATA - %ALLUSERSPROFILE% (%ProgramData%, %SystemDrive%\ProgramData)
CSIDL_COMMON_DOCUMENTS -    %PUBLIC%\Documents

顺便提一下,Shell Path Handling Functions 是一个不错的库,可以用来操作文件路径。

另外还可以参考 在哪里放置常见的可写应用程序文件?


0

来自微软,另一种选择是“SHGetKnownFolderPath” https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpatha

从我的角度来看,这些函数适用于c、c++和类似的语言。 在powershell中,我只读取注册表: PS> cd hkcu:\Software\Microsoft\Windows\CurrentVersion\Explorer\ PS> dir

这里查看“Shell Folders”和“User Shell Folders”。

顺便说一句:这些是获取值的功能。我认为这是相当安全的。对于设置值而言,最好不要直接使用注册表,因为那会破坏你的日子。使用这些“目录”的资源管理器 -> 属性将它们移动,还将移动其内容。不幸的是,我不知道在powershell中使用该钩子的方法。


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