本文后面会提供一个关于如何使用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给调用者。