设为首页 加入收藏

TOP

枚举Windows系统中的外壳名字空间(shell namespace)
2014-11-23 20:26:36 】 浏览:331
Tags:枚举 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”表示为:

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
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇使用VC实现JPG图片加载 下一篇DDK3790环境配置,让新手编译第一..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目