本节介绍一个示例,说明如何检索特殊文件夹的位置,走一个项目标识符列表,并使用IShellFolder界面检索显示名称。该示例是一个控制台应用程序,用于打印用户必须打开以访问“程序”文件夹的文件夹的显示名称。要显示它们,应用程序将执行以下步骤:
1.使用SHGetSpecialFolderLocation功能读取程序文件夹的PIDL(获取项目标识符列表的指针)。
2.使用SHGetDesktopFolder功能绑定到桌面文件夹(检索文件夹的IShellFolder界面)。
3.按如下所示列出项目标识符列表和过程元素:打印子文件夹的显示名称,绑定到子文件夹,并释放父文件夹的IShellFolder界面。
在执行上述任何步骤之前,应用程序使用SHGetMalloc函数来检索指向shell的IMalloc接口的指针,它将保存在以下全局变量中。
//指向shell的IMalloc接口的全局指针。
LPMALLOC g_pMalloc;
以下示例显示了应用程序的主要功能。此函数执行前面描述的所有步骤,尽管它调用应用程序定义的GetNextItemID和CopyItemID函数来移动项目标识符列表和应用程序定义的PrintStrRet函数来打印显示名称。这些应用程序定义函数的代码将按照主函数的代码显示。
// main - 应用程序的entrypoint函数
int __cdecl main()
{
LPITEMIDLIST pidlPrograms;
LPSHELLFOLDER pFolder;
//获取shell的分配器。
if(!SUCCEEDED(SHGetMalloc(& g_pMalloc)))
return 1;
//获取程序文件夹的PIDL。
if(SUCCEEDED(SHGetSpecialFolderLocation(NULL,
CSIDL_PROGRAMS,& pidlPrograms))){
//从桌面文件夹开始。
if(SUCCEEDED(SHGetDesktopFolder(& pFolder))){
LPITEMIDLIST pidl;
//处理列表中的每个项目标识符。
for (pidl = pidlPrograms; pidl != NULL;
pidl = GetNextItemID(pidl)) {
STRRET sName;
LPSHELLFOLDER pSubFolder;
LPITEMIDLIST pidlCopy;
//将项目标识符本身复制到列表中。
if ((pidlCopy = CopyItemID(pidl)) == NULL)
break;
//显示子文件夹的名称。
if(SUCCEEDED(pFolder- > lpVtbl- > GetDisplayNameOf(
pFolder,pidlCopy,SHGDN_INFOLDER,
& SNAME)))
PrintStrRet(pidlCopy, &sName);
//绑定到子文件夹。
if(!SUCCEEDED(pFolder- > lpVtbl- > BindToObject(
pFolder,pidlCopy,NULL,
IID_IShellFolder pSubFolder)))
g_pMalloc->lpVtbl->Free(g_pMalloc, pidlCopy);
break;
}
//释放项目标识符的副本。
g_pMalloc->lpVtbl->Free(g_pMalloc, pidlCopy);
//释放父文件夹并指向
//子文件夹。
pFolder->lpVtbl->Release(pFolder);
pFolder = pSubFolder;
}
//释放绑定到的最后一个文件夹。
if (pFolder != NULL)
pFolder->lpVtbl->Release(pFolder);
}
//为程序文件夹释放PIDL。
g_pMalloc->lpVtbl->Free(g_pMalloc, pidlPrograms);
}
//释放shell的分配器。
g_pMalloc->lpVtbl->Release(g_pMalloc);
return 0;
}
以下是GetNextItemID函数。给定一个指向项目标识符列表中的元素的指针,该函数返回一个指向下一个元素的指针(如果没有更多元素,则返回NULL)。主要函数调用此函数来移动“程序”文件夹的项目标识符列表。
// GetNextItemID - 指向项标识符中的下一个元素
//列表。
//如果成功返回PIDL,如果在列表的末尾返回NULL。
// pdil - 上一个元素
LPITEMIDLIST GetNextItemID(LPITEMIDLIST pidl)
{
//获取指定项目标识符的大小。
int cb = pidl->mkid.cb;
//如果大小为零,它是列表的结尾。
if (cb == 0)
return NULL;
//将cb添加到pidl(转换为逐字节增加)。
pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb);
//如果为空终止则返回NULL,否则返回一个pidl。
return (pidl->mkid.cb == 0) ? NULL : pidl;
}
以下是CopyItemID函数。给定指向项目标识符列表中的元素的指针,该函数分配一个仅包含指定元素的新列表,后跟终止零。主要函数使用此函数创建单元素PIDL,它将传递给IShellFolder成员函数。
// CopyItemID - 创建包含第一个的项目标识符列表
//指定列表中的项目标识符。
//如果成功返回PIDL,如果内存不足则返回NULL。
LPITEMIDLIST CopyItemID(LPITEMIDLIST PIDL)
{
//获取指定项目标识符的大小。
int cb = pidl->mkid.cb;
//分配一个新的项目标识符列表。
LPITEMIDLIST pidlNew = (LPITEMIDLIST)
g_pMalloc- > lpVtbl- > Alloc(g_pMalloc,cb + sizeof(USHORT));
if (pidlNew == NULL)
return NULL;
//复制指定的项目标识符。
CopyMemory(pidlNew, pidl, cb);
//附加终止零。
*((USHORT *) (((LPBYTE) pidlNew) + cb)) = 0;
return pidlNew;
}
IShellFolder::GetDisplayNameOf成员函数返回STRRET结构中的显示名称。显示名称可以以STRRET结构的UTYPE成员指定的三种方式之一返回。主要函数调用以下PrintStrRet函数来打印显示名称。
// PrintStrRet - 打印STRRET结构的内容。
// pidl - 如果STRRET_OFFSET包含显示名称的PIDL
// lpStr - STRRET结构的地址
void PrintStrRet(LPITEMIDLIST pidl,LPSTRRET lpStr)
{
LPSTR lpsz;
int cch;
switch(lpStr- > uType){
case STRRET_WSTR:
cch = WideCharToMultiByte(CP_OEMCP, WC_DEFAULTCHAR,
lpStr->pOleStr, -1, NULL, 0, NULL, NULL);
lpsz = (LPSTR) g_pMalloc->lpVtbl->Alloc(g_pMalloc, cch);
if (lpsz != NULL) {
WideCharToMultiByte(CP_OEMCP,WC_DEFAULTCHAR,
lpStr->pOleStr, -1, lpsz, cch, NULL, NULL);
printf("%s\n", lpsz);
g_pMalloc->lpVtbl->Free(g_pMalloc, lpsz);
}
break;
case STRRET_OFFSET:
printf("%s\n", ((char *) pidl) + lpStr->uOffset);
break;
case STRRET_CSTR:
printf("%s\n", lpStr->cStr);
break;
}
}