托管 shell 缩略图提供程序未被调用

managed shell Thumbnail provider doesn't get called

我正在尝试为 Windows COM IThumbnailProvider 接口编写处理程序,以便为自定义文件类型(.URLtest 进行测试)生成缩略图。使用 C#.Net.
无论我尝试什么,我的 ThumbnailProvider class / handler dll 都不会被调用(基于我的简单文本文件日志记录)。我的 .URLtest 文件显示在资源管理器中,就像它们没有分配缩略图提供程序一样。

我正在 Windows 7 64
下测试 我将缩略图提供程序库编译为 x86 和 x64
使用正确的 RegAsm
注册每个版本 "C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" ThumbUrlThumbnailProvider.dll /codebase
"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe" ThumbUrlThumbnailProvider.dll /codebase
这创建了所有这些注册表数据

[HKEY_CLASSES_ROOT]

  [ThumbUrlHandler.ThumbnailProvider]
    @="ThumbUrlHandler.ThumbnailProvider"
    [CLSID]
      @="{E7CBDB01-06C9-4C8F-A061-2EDCE8598F99}"

  [CLSID]
    [{E7CBDB01-06C9-4C8F-A061-2EDCE8598F99}]
      @="ThumbUrlHandler.ThumbnailProvider"
      DisableProcessIsolation=1                          <<< added by me as one attempt to make things work

      [Implemented Categories]
        [{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}]

      [InprocServer32]
        @="mscoree.dll"
        "ThreadingModel"="Both"
        "Class"="ThumbUrlHandler.ThumbnailProvider"
        "Assembly"="ThumbUrlThumbnailProvider, Version=0.1.0.0, Culture=neutral, PublicKeyToken=bb72a5681544afed"
        "RuntimeVersion"="v4.0.30319"
        "CodeBase"="file:///W:/bin/x64/Debug/ThumbUrlThumbnailProvider.dll"

        [0.1.0.0]
          "Class"="ThumbUrlHandler.ThumbnailProvider"
          "Assembly"="ThumbUrlThumbnailProvider, Version=0.1.0.0, Culture=neutral, PublicKeyToken=bb72a5681544afed"
          "RuntimeVersion"="v4.0.30319"
          "CodeBase"="file:///W:/bin/x64/Debug/ThumbUrlThumbnailProvider.dll"

      [ProgId]
        @="ThumbUrlHandler.ThumbnailProvider"

  [Record]
    [{40FD8D55-6F91-3A5C-A049-6664E649643C}]             <<< no idea why this is registered
      [0.1.0.0]
        "Class"="ThumbUrlHandler.WTS_ALPHATYPE"
        "Assembly"="ThumbUrlThumbnailProvider, Version=0.1.0.0, Culture=neutral, PublicKeyToken=bb72a5681544afed"
        "RuntimeVersion"="v4.0.30319"
        "CodeBase"="file:///W:/bin/x64/Debug/ThumbUrlThumbnailProvider.dll"

我创建了文件类型注册表项

[HKEY_CLASSES_ROOT]
  [.URLtest]
    "PerceivedType"="Image"
    "Content Type"="image/jpeg"

    [ShellEx]
      [{e357fccd-a995-4576-b01f-234630154e96}]              <<<< IThumbnailProvider COM interface CLSID
        @="{E7CBDB01-06C9-4C8F-A061-2EDCE8598F99}"          <<<< my ThumbnailProvider class CLSID

我的简单测试拇指提供商代码

// COM interfaces
public enum WTS_ALPHATYPE
{ .. }

[ComVisible(true)]
[Guid("e357fccd-a995-4576-b01f-234630154e96")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThumbnailProvider
{
    void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType);
}

[ComVisible(true)]
[Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInitializeWithStream
{
    void Initialize(IStream stream, int grfMode);
}

// my thumbnail provider class
[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
[ProgId("ThumbUrlHandler.ThumbnailProvider")]
[Guid("E7CBDB01-06C9-4C8F-A061-2EDCE8598F99")] 
public class ThumbnailProvider : IThumbnailProvider, IInitializeWithStream
{
    public void Initialize(IStream stream, int grfMode)
    {
        // instantly dispose the COM stream, as I don't need it for the test
        Marshal.ReleaseComObject(stream);
    }

    public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType)
    {
        bitmapType = WTS_ALPHATYPE.WTSAT_RGB;
        var outBitmap = new Bitmap(cx, cx, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
        hBitmap = outBitmap.GetHbitmap();
    }
}

我尝试重新启动资源管理器进程。
我尝试创建新的 .URLtest 文件来绕过缩略图缓存。
我的处理程序仍然被忽略。
在几次资源管理器重新启动后,我注意到现在我的 x64 ThumbUrlThumbnailProvider.dll 被资源管理器锁定了。但是仍然没有显示文件的缩略图,也没有调用 ThumbnailProvider 方法。

有人知道 Windows 是否仍支持此界面和方法吗?
有人看到我的代码或注册表数据有任何错误吗?
我没主意了。

我设法使我的 shell 扩展程序正常工作。 这不是一个真正的答案,但至少是我学到的东西的集合,可能对其他人有帮助。

有两个主要问题:

  1. 在部分开发过程中,我的文件类型或 dll 的注册可能有错误,因此 shell 确实无法识别和调用它。
  2. 修复此问题后,调用了代码,但无声地崩溃了。
    Shell 运行 将您的代码置于隔离进程中,并且它无权访问文件系统(可能还有很多其他内容)。 如果您尝试访问它,例如编写调试日志文件,您的代码将无提示地崩溃。
    如果您尝试使用写入日志文件的跟踪侦听器(如 System.Diagnostics.TextWriterTraceListener),也会发生同样的情况!

因此,实际上并没有一种简单的方法来检测您的代码是否甚至 shell 运行,并验证您的注册表注册部分是否正确。
我尝试将 Explorer 进程附加到 VisualStudio,但我的断点从未命中,即使我的扩展代码是 运行.
您可以做的是在程序集注册中选择退出“进程隔离”。请参阅我的 DisableProcessIsolation=1 参数和上下文问题。你的程序集将由资源管理器进程直接加载,你可以在 sysinternals Process Explorer 实用程序中看到它。如果您的注册是正确的。
这可能需要资源管理器进程重新启动才能工作。
如果您不选择退出,您的程序集将只加载很短的时间然后再次卸载,因此您将无法发现它,或者正如我在其他地方读到的那样,它可能由 [=55= 以外的其他进程加载] 完全。

我学到的其他一些东西:

  • 出于某种原因,我的代码中的 try catch 块没有捕获到异常。代码仍然崩溃,缩略图提供程序不工作。
  • windows 事件日志中未报告任何错误
  • 您可以将程序集编译为 AnyCPU,并使用 32 位和 64 位 RegAsm 注册它。它至少适用于 64 位资源管理器 (Windows)。 不过,我还没有测试 32 位进程是否也可以 运行。
  • 一些在我的 dll 被测试应用程序调用时有效的代码在 shell 调用相同的 dll 时不起作用。例如 IStream 包装器。
  • shell 在将文件锁传递给托管 shell 扩展程序时不释放文件锁存在问题。 您可以在 and this blog post 中阅读更多相关信息。 该博客提到没有官方解决方案,并且托管 shell 扩展不受 Microsoft 官方支持。
    我的肮脏解决方案是将整个文件内容加载到单独的 MemoryStream(不保留对原始文件或 IStream 的引用)并在 Initialize() 方法中调用 Marshal.ReleaseComObject() 并且不保留对原始 IStream 指针的任何引用。
    Simon Mourier 在他上面的评论中提到使用 Marshal.ReleaseComObject() 不是一个好主意,但这是我能够找到的唯一解决方案。
  • 不管你用什么方法解决这个问题,都不要在第一个 GetThumbnail() 方法调用中去掉你的文件数据/流访问。 Shell 可能会保留您的缩略图提供程序实例并调用 GetThumbnail() 多次,以适应不同的缩略图大小。