rovershen 阅读(1006) 评论(0)

我们知道,Windows扩展了传统的文件目录特性,增加了虚拟目录的支持,将传统的目录结构演变成了外壳名字空间。

打开资源管理器,我们可以看到,外壳的根目录是桌面,桌面下是各种虚拟目录和实际存储媒介上的目录结构。我们要枚举实际的目录时很容易,一般都使用FindFirstFile/FindNextFile来枚举,但是如果要枚举这种外壳虚拟目录则不行。

在外壳空间中,每个目录是以叫ITEMIDLIST的结构组合存放的,它的定义如下:

typedef struct _ITEMIDLIST {
    SHITEMID mkid;
} ITEMIDLIST;

typedef struct _SHITEMID {
    USHORT cb;
    BYTE   abID[1];
} SHITEMID, * LPSHITEMID;

可见实际的结构是SHITEMID,每一级目录由一个SHITEMID表示,因此一个路径就由一组SHITEMID来表示,如“C:\MyDocs\MyFile.htm”表示为:

A schematic illustration of a PIDL

组合的结尾是双字节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.iIcon;
}
BOOL GetSubFolder(LPITEMIDLIST pidl, LPSHELLFOLDER &lpFolder)
{
 HRESULT hres=lpDesktop->BindToObject(pidl,0,IID_IShellFolder,(void**)&lpFolder);
 return SUCCEEDED(hres);
}
具体实现,可以参看文章中的ExplorerTree类

发表评论
切换编辑模式