【问题标题】:How to detect if a given PIDL is actually a .zip file or similar?如何检测给定的 PIDL 是否实际上是 .zip 文件或类似文件?
【发布时间】:2012-12-26 13:07:36
【问题描述】:

VS2010 引入了 CMFCShellTreeCtrl,它允许将文件夹浏览器树 ctrl 拖放到我们的 MFC 应用程序中。

但是,这个类似乎严重缺乏过滤功能。即它将构建容器对象列表(IShellFolder)。但似乎没有办法指定 .zip 容器不应显示在文件夹树中。

它确实提供了一个可以粗略用于此目的的虚拟:

HRESULT CMFCShellTreeCtrl::EnumObjects(HTREEITEM hParentItem, LPSHELLFOLDER pParentFolder, LPITEMIDLIST pidlParent)
{
  ASSERT_VALID(this);
  ASSERT_VALID(afxShellManager);

  LPENUMIDLIST pEnum = NULL;

  HRESULT hr = pParentFolder->EnumObjects(NULL, m_dwFlags, &pEnum);
  if (FAILED(hr) || pEnum == NULL)
  {
      return hr;
  }

  LPITEMIDLIST pidlTemp;
  DWORD dwFetched = 1;

  // Enumerate the item's PIDLs:
  while (SUCCEEDED(pEnum->Next(1, &pidlTemp, &dwFetched)) && dwFetched)
  {
      TVITEM tvItem;
      ZeroMemory(&tvItem, sizeof(tvItem));

      // Fill in the TV_ITEM structure for this item:
      tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;

      // AddRef the parent folder so it's pointer stays valid:
      pParentFolder->AddRef();

      // Put the private information in the lParam:
      LPAFX_SHELLITEMINFO pItem = (LPAFX_SHELLITEMINFO)GlobalAlloc(GPTR, sizeof(AFX_SHELLITEMINFO));
      ENSURE(pItem != NULL);

      pItem->pidlRel = pidlTemp;
      pItem->pidlFQ = afxShellManager->ConcatenateItem(pidlParent, pidlTemp);

      pItem->pParentFolder = pParentFolder;
      tvItem.lParam = (LPARAM)pItem;

      CString strItem = OnGetItemText(pItem);
      tvItem.pszText = strItem.GetBuffer(strItem.GetLength());
      tvItem.iImage = OnGetItemIcon(pItem, FALSE);
      tvItem.iSelectedImage = OnGetItemIcon(pItem, TRUE);

      // Determine if the item has children:
      DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_DISPLAYATTRMASK | SFGAO_CANRENAME | SFGAO_FILESYSANCESTOR;

      pParentFolder->GetAttributesOf(1, (LPCITEMIDLIST*) &pidlTemp, &dwAttribs);
      tvItem.cChildren = (dwAttribs & (SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR));

      // Determine if the item is shared:
      if (dwAttribs & SFGAO_SHARE)
      {
          tvItem.mask |= TVIF_STATE;
          tvItem.stateMask |= TVIS_OVERLAYMASK;
          tvItem.state |= INDEXTOOVERLAYMASK(1); //1 is the index for the shared overlay image
      }

      // Fill in the TV_INSERTSTRUCT structure for this item:
      TVINSERTSTRUCT tvInsert;

      tvInsert.item = tvItem;
      tvInsert.hInsertAfter = TVI_LAST;
      tvInsert.hParent = hParentItem;

      InsertItem(&tvInsert);
      dwFetched = 0;
  }

  pEnum->Release();
  return S_OK;
}

我感到困惑的是缺乏区分正在枚举的对象类型的能力(或者是一种更好的方法来首先控制枚举以便过滤掉诸如这些的非文件系统对象)。

可以查看被枚举项目的文本,如果它以“.zip”结尾,则简单地排除它。但是,这对我来说似乎很不稳定。毕竟,可以将任意文件夹命名为 XYZ.zip(它仍然是一个文件夹,而不是 zip 存档)。同样,未来可能还支持除 .zip 之外的其他存档类型 - 或者其他容器类型,但它们实际上并不是文件夹。

我不想消除“网络”和“计算机”之类的有效节点。只是那些有问题的,例如“Downloads\Foobar.zip”

对于这个漫无边际的问题,我深表歉意。任何有助于提高我的理解和创造性方法以了解哪些类型的对象是 Microsoft 的 shell 命名空间的一部分,以及如何有效地使用它们,我们将不胜感激!

【问题讨论】:

  • 我认为,只需检查该项目是否为文件?
  • “计算机”和“网络”未通过该测试。我不想排除重要的非文件系统节点。只是存档容器或类似容器。
  • 嗯,我的意思是,如果它是真实文件,则排除它(并且满足您不应该包含的文件的标准)
  • 这就是我所没有的:一种检测 IS X 档案的方法?操作系统似乎并没有通过访问此信息提供太多帮助。我可以询问它的文件属性,它告诉我它是否是一个文件夹(不幸的是,它被标记为“是”)。但是文件没有特定的标志 - 所以我不能说它既是文件又是文件夹。只是它是一个文件夹:(
  • 您可以随时打开文件并读取前几个字节以查看它是否“看起来像一个 zip 文件”。如果他们想绝对确定的话,大多数事情就是这样做的。当然,另一种选择是忽略某些白痴将目录命名为“foo.zip”的事实。这不太可能发生......

标签: c++ mfc visual-studio-2012 windows-shell


【解决方案1】:

一个 zip 文件/文件夹将包含 SFGAO_STREAM/SFGAO_DROPTARGET 和 SFGAO_FOLDER,因此如果您可以将 shell 项作为流读取,那么它可能不是目录。另一种判断方法是使用 SHGetPathFromIDList+PathIsDirectory,但这仅适用于具有文件系统路径的 pidl。

还有其他类型的可浏览文件,例如已保存的搜索(如果您浏览文件,完成枚举项目需要很长时间),因此您可能也想考虑如何处理这些文件。

【讨论】:

  • 这两种技术都适用于我的目的,在 Windows 7(希望是 XP..8)下。
猜你喜欢
  • 2012-08-22
  • 2017-12-16
  • 1970-01-01
  • 2012-06-18
  • 2019-08-12
  • 2011-11-10
  • 1970-01-01
  • 1970-01-01
  • 2010-09-30
相关资源
最近更新 更多