翻译自MSDN 2005 -> Win32 和 COM 开发 -> User Interface -> Windows User Experience -> Windows Shell -> Shell Programmer's Guide -> Shell Basics -> Shell Basics: Programming the Shell -> Getting a Folder's ID
在使用Shell对象前,需要有一种标识它的方法,也就是要获取其PIDL,或者对于文件系统对象,获取其路径。本文介绍两种较简单的获取对象ID的方法。
还有一种更强大的适合任何文件夹的方法,就是使用IShellFolder接口,详情见Getting Information About the Contents of a Folder。
SHBrowseForFolder对话框
要让用户可以浏览名字空间然后选择一个文件夹,可以简单地调用SHBrowseForFolder。调用这个函数会显示一个对话框,其界面跟通用的打开或者另存为对话框相似。
用户选择某文件夹后,函数会返回其全限定PIDL和显示名。如果选定的文件夹位于文件系统中,可以用SHGetPathFromIDList把PIDL转换成文件夹路径。SHBrowseForFolder也可以指定根文件夹,以限制用户可以选取文件夹的范围,因为只有指定的根文件夹下层的文件夹才显示在对话框中。下图是一个根文件夹设置为Program Files的SHBrowseForFolder对话框。
本文后面会提供一个关于如何使用SHBrowseForFolder的简单例子。
特殊文件夹和CSIDL
系统中一些常用的文件夹经过了特殊设计。这些文件夹具有良好定义的用途,并且其中大部分在所有系统中都存在。即使有些文件夹当前不存在,其名字和位置还是定义了的,可以在未来增加。这些特殊文件夹包括所有的系统标准虚拟文件夹,像打印机、我的文档、网上邻居等。当然也包括一些标准的文件系统文件夹,如程序(Program Files)和系统(System)文件夹。
虽然这些文件夹是系统的标准组件,但它们的名称和在名字空间中的位置却可能不同。比如说,系统文件夹在某些系统上是C:\Winnt\System32,在另一些系统上却是C:\Windows\System32。过去是用环境变量来确定特殊文件夹的名字和位置的,现在Shell提供了更健壮和灵活的方法来标识这些特殊文件夹,即CSIDL。现在应该使用CSIDL了,而不是环境变量。
CSIDL提供了标识和定位特殊文件夹的统一方法。与环境变量不同的是,CSIDL不仅可以用于文件系统文件夹,还可以用于虚拟文件夹。每个特殊文件夹都分配了一个唯一的CSIDL,比如说,分配给程序文件夹的是CSIDL_PROGRAM_FILES,分配给网上邻居的是CSIDL_NETWORK。
CSIDL与一些Shell函数结合使用可以获取特殊文件夹的PIDL,或者其路径。如果请求的特殊文件夹在系统中还不存在,可以结合CSIDL_FLAG_CREATE标志来创建它。CSIDL可以传给下列函数:
- SHGetFolderLocation 获取某个特殊文件夹的PIDL
- SHGetFolderPath 获取某文件系统特殊文件夹的路径
这两个函数在5.0版的Shell中引入,取代了原来的SHGetSpecialFolderLocation和SHGetSpecialFolderPath函数。要在5.0版以前的Shell中使用这两个函数,需要包含可重新发布的DLL: ShFolder.dll。
使用CSIDL和SHBrowseForFolder的简单例子
下面的示例函数PidlBrowse展示了如何获取Shell分配器IMalloc接口,如何使用CSIDL获取文件夹的PIDL,如何用SHBrowseForFolder让用户选择一个文件夹,函数返回选定文件夹的PIDL和显示名。
LPITEMIDLIST PidlBrowse(HWND hwnd, int nCSIDL, LPSTR pszDisplayName) { LPITEMIDLIST pidlRoot = NULL; LPITEMIDLIST pidlSelected = NULL; BROWSEINFO bi = {0}; LPMALLOC pMalloc = NULL;
SHGetMalloc(&pMalloc);
if(nCSIDL) { SHGetFolderLocation(hwnd, nCSIDL, NULL, NULL, &pidlRoot); }
else { pidlRoot = NULL; }
bi.hwndOwner = hwnd; bi.pidlRoot = pidlRoot; bi.pszDisplayName = pszDisplayName; bi.lpszTitle = "Choose a folder"; bi.ulFlags = 0; bi.lpfn = NULL; bi.lParam = 0;
pidlSelected = SHBrowseForFolder(&bi);
if(pidlRoot) { pMalloc->Free(pidlRoot); } pMalloc->Release(); return pidlSelected; } |
调用者传入一个窗口句柄,这是SHBrowseForFolder需要的;nCSIDL参数是可选的,它用于指定根文件夹,只有层次结构中根文件夹以下的文件夹才会显示。本文前面的那幅图片就是以CSIDL_PROGRAM_FILES作为nCSIDL参数值调用函数时产生的。调用者还需要传入由pszDisplayName参数指定的字符串缓冲区,用于在函数返回时保存选定文件夹的显示名。
PidlBrowse首先调用SHGetMalloc来获取Shell分配器接口指针。虽然PidlBrowse本身没有分配PIDL,但随后需要用IMalloc接口来释放(别处分配的)PIDL。如果调用者用CSIDL指定了某根文件夹,PidlBrowse会调用SHGetFolderLocation获取文件夹的PIDL。然后PidlBrowse会为BROWSEINFO结构体各成员指定合适的值,并调用SHBrowseForFolder。
用户选择某文件夹后,SHBrowseForFolder会返回其PIDL。选定文件夹的显示名在BROWSEINFO结构体的pszDisplayName成员中返回,然后通过PidlBrowse函数的pszDisplayName参数返回给调用者。最后,PidlBrowse释放根PIDL,释放IMalloc接口,返回选定文件夹的PIDL给调用者。