【问题标题】:How to get the IPreviewHandler for a file extension?如何获取文件扩展名的 IPreviewHandler?
【发布时间】:2014-07-01 05:52:42
【问题描述】:

如何获取特定文件扩展名的 shell IPreviewHandler

背景

Windows 允许开发人员为其自定义文件类型创建 预览处理程序

预览处理程序在选择一个项目以显示“视图”阅读窗格中显示文件的内容的轻量级,丰富,只读预览时。这是在不启动文件的关联应用程序的情况下完成的。

预览处理程序是托管应用程序。主机包括 Windows Vista 或 Microsoft Outlook 2007 中的 Windows 资源管理器。

我想利用现有的IPreviewHandler 基础设施来获取文件的缩略图。

在流中

问题是我的文件没有放在 shell 命名空间中(即它们没有放在硬盘上)。它们位于内存中,可通过IStream 访问。这意味着我不能使用旧的IExtractImage 接口;因为它不支持从 Stream 加载文件。

幸运的是,这就是现代 IPreviewHandler 支持(推荐和更喜欢)从 Stream 加载数据,并建议反对从文件加载预览的原因:

此方法优于 Initialize,因为它能够使用无法通过 Win32 路径访问的流,例如具有 .zip 文件扩展名的压缩文件的内容。

那我怎么得到它?

没有关于正确方法获取与特定扩展关联的IPreviewHandler 的文档。但是,如果我了解如何注册IPreviewHandlerand read the contract from the other side

HKEY_CLASSES_ROOT
  .xyz
     (Default) = xyzfile

HKEY_CLASSES_ROOT
   xyzfile
      shellex
         {8895b1c6-b41f-4c1c-a562-0d564250836f} //IPreviewHandler subkey
             (Default) = [clsid of the IPreviewHandler]

鉴于我知道扩展程序,我应该能够遵循相同的路线。让我们用一个真实世界的例子,.jpg 文件:

请注意,该文件有一个预览。请注意,我包含第二个屏幕截图只是为了强调预览不是来自硬盘驱动器上的文件的想法。

让我们得到spellunking

首先它是一个.jpg 文件:

HKEY_CLASSES_ROOT
   .jpg
      (Default) = ACDC_JPG

HKEY_CLASSES_ROOT
   ACDC_JPG
      ShellEx
         {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}
         ContextMenuHandlers

等等,预览处理程序没有 {8895b1c6-b41f-4c1c-a562-0d564250836f} 子键。这一定意味着我们无法获得.jpg 文件的缩略图。

还原为荒谬

真正的问题

细心的读者会意识到我要问的实际问题是:

如何获得仅包含在流中的图像的预览?

虽然这是一个有用的问题,也是我遇到的真正问题,但对如何使用 IPreviewHandler 进行回答也是一个有用的问题。

所以请随意回答;或两者兼而有之!

阅读奖励

【问题讨论】:

  • 将流保存到临时文件是否可以接受?在这种情况下,您只需为 .Net like this 使用现成的预览控件即可。
  • 你真的需要 IPreviewHandler 吗?看起来 IThumbnailProvider 扩展是您所需要的。即使在 IExtractImage 的情况下,您也可以尝试使用 IInitializeWithStream 或 IPersistStream 初始化扩展(至少 Windows 资源管理器会尝试)。
  • Hosting Preview Handlers in Windows Forms Applications 的评论指出AssocQueryString 可用于此目的。 ASSOCSTR_SHELLEXTENSION 可用于请求实现特定接口的 shell 扩展,例如 IPreviewHandler。 (将其作为评论发布,因为我无法验证它,并且在这是一个有用的答案之前仍然需要填写一些细节。)
  • 以 Outlook 为例(类似于您的第二个屏幕截图所示),附件确实作为临时文件保存在磁盘上,因此可以通过许多其他方式提取预览。
  • 请注意,.jpgPerceivedTypeimage。见AssocGetPerceivedType MSDN。如果这回答了您的问题,请告诉我。

标签: windows windows-shell ipreviewhandler


【解决方案1】:

@hvd 给出了正确的答案。

文件类型有一个 ShellEx 键,带有 {guid} 子键。每个{guid} 键代表一个特定的InterfaceID

有许多标准的 shell 接口可以与文件类型相关联:

  • {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}IExtractImage
  • {953BB1EE-93B4-11d1-98A3-00C04FB687DA}IExtractImage2
  • {e357fccd-a995-4576-b01f-234630154e96}IThumbnailProvider
  • {8895b1c6-b41f-4c1c-a562-0d564250836f}IPreviewHandler

不支持对未记录的注册表项进行探索

例如,如果我想查找与.jpg 文件关联的 IPreviewHandlerclsid,我会查看:

HKEY_CLASSES_ROOT/.jpg/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid]

但这不是我能看到的唯一地方。我也可以进去看看:

HKEY_CLASSES_ROOT/.jpg
   (default) = jpgfile
HKEY_CLASSES_ROOT/jpgfile/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid]

但这不是我能看到的唯一地方。我也可以进去看看:

HKEY_CLASSES_ROOT/SystemFileAssociations/.jpg/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid] 

但这不是我能看到的唯一地方。我也可以进去看看:

HKEY_CLASSES_ROOT/SystemFileAssociations/jpegfile/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid]

但这不是我能看到的唯一地方。如果我认为该文件是图像,我也可以查看:

HKEY_CLASSES_ROOT/SystemFileAssociations/image/ShellEx/{8895b1c6-b41f-4c1c-a562-0d564250836f}
   (default) = [clsid]

我是如何找到这些位置的?我是否只遵循记录和支持的位置?不,我使用 Process Monitor 监视 Explorer,因为它正在寻找 IThumbnailProvider

不要使用无证拼写

所以现在我想为自己的文件类型使用标准的 shell 接口。这意味着我必须抓取这些位置。但是,为什么要以无证、不受支持的方式抓取这些位置。为什么要从the thing 的高处招致the guy 的愤怒?使用AssocQueryString:

Guid GetShellClsidForFileType(String fileExtension, Guid interfaceID)
{
    //E.g.:
    //   String fileExtension = ".jpg"
    //   Guid   interfaceID   = "{8895b1c6-b41f-4c1c-a562-0d564250836f}"; //IExtractImage

    //The interface we're after - in string form
    String szInterfaceID := GuidToString(interfaceID);

    //Buffer to receive the clsid string
    DWORD bufferSize := 1024; //more than enough to hold a 38-character clsid
    String buffer;
    SetLength(buffer, bufferSize);

    HRESULT hr := AssocQueryString(
          ASSOCF_INIT_DEFAULTTOSTAR, 
          ASSOCSTR_SHELLEXTENSION, //for finding shell extensions
          fileExtension, //e.g. ".txt"
          szInterfaceID, //e.g. "{8895b1c6-b41f-4c1c-a562-0d564250836f}"
          buffer,        //will receive the clsid string
          @bufferSize);
   if (hr <> S_OK) 
      return Guid.Empty;

   Guid clsid;
   HRESULT hr = CLSIDFromString(buffer, out clsid);
   if (hr <> NOERROR) 
      return Guid.Empty;

   return clsid;
}

所以要为.xps 文件获取IPreviewHandlerclsid

Guid clsid = GetShellClsidForFileType(".xps", IPreviewHandler);

如何获取文件扩展名的IPreviewHandler?

有了以上所有内容,我们现在可以回答这个问题了:

IPreviewHandler GetPreviewHandlerForFileType(String extension)
{
    //Extension: the file type to return IPreviewHandler for (e.g. ".xps")
    Guid previewHandlerClassID = GetShellClsidForFileType(extension, IPreviewHandler);

    //Create the COM object
    IUnknown unk = CreateComObject(previewHandlerClassID);

    //Return the actual IPreviewHanler interface (not IUnknown)
    return (IPreviewhandler)unk;
}

【讨论】:

  • 如果你包含了 pinvoke 方法,以及与该方法一起使用的常量定义,那就太好了。
  • @BruceBurge 这不是 C# CLR 代码;它是本机 Win32。
  • 哈,很抱歉 := 可能应该放弃它,话虽如此,将它移植到 C#(如果你愿意的话)会非常有帮助!
  • 请注意,AssocQueryString 无法查找使用“metro”API(或本周所称的任何名称)实现的预览处理程序。一个示例是 Microsoft 的混合现实查看器(包含在 Windows 中),它为 FBX 3D 对象提供了一个预览处理程序(一旦您通过打开方式>选择默认将它们分配给它)。它们似乎以某种新奇的方式注册(并且似乎并非所有必需的信息/映射都在注册表中),并且文件资源管理器似乎使用自己的代码而不是 shell 提供给其他人使用的 API。非常令人沮丧。
  • 现在有些文件根本没有预览处理程序(.txt 和所有图像类型,如 .png、.jpg 或现代的 .heic、.webp 等)。 Shell 为此做的是回退 IThumbnailProvider 支持,因此如果查询“{8895b1c6-b41f-4c1c-a562-0d564250836f}”失败,它还将查询“{e357fccd-a995-4576-b01f-234630154e96}”(这是 IID_IThumbnailProvider )。
猜你喜欢
  • 2014-04-14
  • 1970-01-01
  • 2012-07-23
  • 2017-12-22
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多