我们知道,Windows扩展了传统的文件目录特性,增加了虚拟目录的支持,将传统的目录结构演变成了外壳名字空间。
打开资源管理器,我们可以看到,外壳的根目录是桌面,桌面下是各种虚拟目录和实际存储媒介上的目录结构。我们要枚举实际的目录时很容易,一般都使用FindFirstFile/FindNextFile来枚举,但是如果要枚举这种外壳虚拟目录则不行。
在外壳空间中,每个目录是以叫ITEMIDLIST的结构组合存放的,它的定义如下:
typedef struct _ITEMIDLIST {
SHITEMID mkid;
} ITEMIDLIST;
typedef struct _SHITEMID {
USHORT cb;
BYTE abID[1];
} SHITEMID, * LPSHITEMID;
可见实际的结构是SHITEMID,每一级目录由一个SHITEMID表示,因此一个路径就由一组SHITEMID来表示,如“C:MyDocsMyFile.htm”表示为:
组合的结尾是双字节0。由于路径分绝对路径和相对路径,所以ITEMIDLIST也有绝对和相对之分。也就是ITEMIDLIST包含的SHITEMID的组合的差别:全部或者后面的某一部分。实际上每个abID跟目录名称是一一对应的(但是我不知道是以何种算法算出来的)。桌面则比较特殊,它的ITEMIDLIST只有一项SHITEMID,只有一个为0的cb成员。
了解了ITEMIDLIST,我们可以开始枚举所有目录了。
整个外壳空间是以com的形式组织的,包含若干个接口,其中最主要的是IShellFolder接口和IEnumIdList接口。我们先得到桌面的IShellFolder接口,再利用IEnumIdList接口就可以枚举出所有的目录了。
/*为简单起见,以下代码未考虑错误处理等*/
LPMALLOC lpMalloc=NULL;
SHGetMalloc(&lpMalloc);
SHGetDesktopFolder(&lpDesktop);
LPITEMIDLIST pidl=(LPITEMIDLIST)lpMalloc->Alloc(sizeof(USHORT)); *((USHORT*)pidl)=0;
LPSHELLFOLDER lpDesktop=NULL;
SHGetDesktopFolder(&lpDesktop);
LPENUMIDLIST lpEnum=NULL;
lpDesktop->EnumObjects(0,SHCONTF_FOLDERS|SHCONTF_INCLUDEHIDDEN,&lpEnum);//只枚举目录
然后用lpEnum的Next方法就可以得到所有的子目录的相对ITEMIDLIST(对于桌面的子目录,绝对ITEMIDLIST和相对ITEMIDLIST是一样的),然后利用IShellFolder的BindToObject的方法得到子目录的IShellFolder接口,依次往下类推即可。也可以先合成绝对ITEMIDLIST,直接用桌面接口来得到任意级子目录的IShellFolder接口。
另外,可以用各个目录的绝对ITEMIDLIST来调用SHGetFileInfo以得到目录的显示名称和图标。
LPITEMIDLIST Append(LPCITEMIDLIST pidlBase, LPCITEMIDLIST pidlAdd) {
if(pidlBase == NULL)
return NULL;
if(pidlAdd == NULL)
return MakeCopy(pidlBase);
LPITEMIDLIST pidlNew;
UINT cb1 = GetSize(pidlBase) - sizeof(pidlBase->mkid.cb);
UINT cb2 = GetSize(pidlAdd);
pidlNew = (LPITEMIDLIST)lpMalloc->Alloc(cb1 + cb2);
if (pidlNew) {
CopyMemory(pidlNew, pidlBase, cb1);
CopyMemory(((LPSTR)pidlNew) + cb1, pidlAdd, cb2); }
return pidlNew; }
BOOL GetFolderName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl, CString &name) {
SHFILEINFO sfi;
SHGetFileInfo((LPCTSTR)pidl,0,&sfi,sizeof(SHFILEINFO),SHGFI_DISPLAYNAME|SHGFI_PIDL);
name=sfi.szDisplayName;
return TRUE; }
int GetFolderIcon(LPITEMIDLIST pidl) {
SHFILEINFO sfi;
flag=(SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_PIDL);
SHGetFileInfo((LPCTSTR)pidl,0,&sfi,sizeof(SHFILEINFO),SHGFI_SYSICONINDEX|
SHGFI_SMALLICON|SHGFI_PIDL);
return sfi