“浏览文件夹”对话框及源代码






4.60/5 (5投票s)
2001年1月22日

117226

1745
使用的Shell接口。IShellFolder, IEnumIDList, 等。

引言
此代码演示了Windows Shell接口的使用。
使用的Shell接口和函数
接口是
- IShellFolder
- IEnumIDList
Shell函数是
- SHGetMalloc
- SHGetDesktopFolder
- SHGetFileInfo
- SHGetSpecialFolderLocation
背后的想法
我以前从未接触过Windows Shell API,这是我第一次尝试。我想通过使用提供的和文档化的Shell接口来实现一种通用的检索Shell命名空间的方法。
这个示例是SHBrowseForFolder Shell函数的替代品。这段代码并没有实现SHBrowseForFolder的所有功能,但它提供了导航命名空间和选择所需路径的基本功能。
源代码被分成了Shell扩展DLL BFF{D}.DLL 和一个示例应用程序 App{D}.exe。DLL包含了你所需要的一切。示例应用程序展示了如何使用DLL的功能。
如何使用
当您需要调用"浏览文件夹"对话框时,请在源代码中加入以下内容:
CBrowseForFolderDlg::BFFINFO info; memset(&info, 0, sizeof(info)); info.m_pParent = this; strcpy(info.m_szTitle, "Browse For Folder"); strcpy(info.m_szMsg, "Select the Voice Mail Folder"); info.m_pidlRoot = m_pidlMainPath; /* Line 1 */ info.m_pidlSelected = m_pidlSelected; info.m_nFlags |= BFF_IDL_ROOT | BFF_IDL_SELECTED; /* Line 2 */ info.m_nFlags |= (m_bIncFiles) ? BFF_INCLUDE_FILES : info.m_nFlags; info.m_nFlags |= (m_bSysOnly) ? BFF_SYSTEM_ONLY : info.m_nFlags; CBrowseForFolderDlg dlg(&info); if (dlg.DoModal() == IDCANCEL) return; // release memory for the previous default SHFree(m_pidlSelected); // allocate and copy newly selected m_pidlSelected = dlg.GetTreeSelectedFullIdl();
上面的代码假设您已经预先分配并赋值了m_pidlMainPath和m_pidlSelected。如果您希望使用以null结尾的字符串,而不是ITEMIDLIST,您可以如下修改第1和第2行:
  strcpy(info.m_szRoot, "c:\\windows\\system");
  info.m_nFlags |= BFF_CHAR_ROOT;
请注意,您也可以指定网络路径。
  strcpy(info.m_szRoot, "\\\\ABERRESFORD\\views");
  info.m_nFlags |= BFF_CHAR_ROOT;
或者CSIDL值。
info.m_pidlRoot = CSIDL_DESKTOP; info.m_nFlags |= BFF_CSIDL_ROOT;
CBrowseForFolderDlg::BFFINFO结构中的m_pidlSelected和m_szSelected成员变量也适用同样的规则。只需确保所选路径是您基础根目录的子路径,否则您将收到指定的路径不存在的消息。
我在对话框中添加了一个"设置为根目录"按钮,并为了调试目的将其设为不可见。您可以在演示应用程序中使其可见并动态设置根目录。
您还必须将BFF{D}.LIB库添加到您的链接列表中。"D"代表您正在链接的是DLL的调试版本。
就是这样。您应该能够运行您的应用程序并使用CBrowseForFolderDlg类了。
数据与视图分离
我在这段示例中应用了“访问者”设计模式。我引入了一个抽象的ITreeNode接口,并将其所有成员变量设置为虚拟。ITreeNode代表"数据"。大部分功能存在于CShellTreeNode类中。CShellTreeNode使用Shell API来访问Shell对象。"视图"是MFC的CTreeCtrl类,而"访问者"是CTreeVisitor类,它有一个可派生的CUITreeVisitor。CUITreeVisitor不知道CTreeCtrl的哪些方法需要处理才能使其工作并反映基于ITreeNode构建的层次结构。CUITreeVisitor不了解具体的CShellTreeNode,只处理ITreeNode公开的方法,因此您可以轻松地更改此接口的实现。
CShellHelper类由CShellTreeNode类使用,并且是所有"静态"数据的管理者。也就是说,在应用程序的生命周期内不会改变的数据。此类附加到全局系统图像列表(在WinNT上我相信,每个进程都有自己的系统图像列表副本),并允许其他人读/写此列表。
数据检索
我只提取选定节点的直接子节点(如果存在)。您可以通过使用ITreeNode::SetFindDepth()方法指定搜索深度。深度<1>表示只获取直接子节点。显示树
为了实现树的高速显示,我使用了一种广为人知的显示技术。其核心思想是,您只提供当前可见的信息。CTreeCtrl在需要每个可见节点的文本和图标时会发送一个消息 - CTreeCtrl::OnGetdispinfo。TVITEM::lParam成员指向ITreeNode的一个实例。只有这时,我才为树控件提供图像列表中的文本和图标ID。根据我上面描述的数据检索方法,每当用户展开一个节点时,我将请求ITreeNode去获取其直接子节点。然后,我将请求CUITreeVisitor去访问(显示)在树控件中找到的节点。这通过CTreeCtrl每次展开一个带子节点的节点时调用的方法CTreeCtrl::OnItemexpanding()来完成。因此,无论用户只展开一层,还是点击了数字小键盘上的"*"按钮,相同的算法都能完成工作。排序
Shell对象以降序显示。系统文件夹的优先级高于文件。这是使用微软的标准方法完成的。有一个您需要设置的回调函数,当微软算法需要比较正在排序列表中的两个项目时会调用它。在我们的例子中,这是静态的CFileTreeCtrl::CompFunc()方法。更新
我已更新源代码和图片。尽情享用!如果您发现错误或有任何改进建议,请告诉我。再次更新... (2001/01/30)
我修复了几个错误。其中一个相当棘手。因此,列表控件在WinNT上无法正常工作。现在都已修复。现在您可以将Shell命名空间中的任何节点指定为Shell浏览器的基础根目录。CBrowseForFolderDlg::BFFINFO结构已扩展以支持任意根目录。您可以指定ITEMIDLIST、CSIDL或char*形式的基础根目录。默认选择也一样。您可以输入网络路径,同步按钮应该可以正常工作。