枚举Windows系统中的外壳名字空间(shell namespace)
我们知道,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补充:软件开发 , Vc ,