使用我的缩略图提供程序后,资源管理器不会释放文件 [英] Explorer Wont Release Files After Using My Thumbnail Provider

查看:41
本文介绍了使用我的缩略图提供程序后,资源管理器不会释放文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为文件类型设置了缩略图提供程序.

项目是用

  • C#
  • .NET 4.5

我运行的是 Windows x64

我的提供者按预期成功生成缩略图,我可以移动、删除、复制等文件.锁定问题似乎是由文件放置在文件夹中引起的.此时,移动、删除等,文件夹都显示错误,文件正在使用".

如果您熟悉,我已经确认使用 Sysinternal 的 Process Explorer 来探索锁定文件.

我尝试了 2 种方法来尝试解决此问题...

  1. 自己实现了 IThumbnailProviderIInitializeWithStream.
  2. 使用第 3 方 Sharpshell

两者都遇到同样的问题,文件没有被释放.

在 Sharpshell 的 github 上,也已经开始指定这个问题.https://github.com/dwmkerr/sharpshell/issues/78

我像这样关联注册表中的文件类型

HKEY_CLASSES_ROOT----.qb----贝莱克斯----{e357fccd-a995-4576-b01f-234630154e96}:我的CLSID...

我也尝试过...

HKEY_CLASSES_ROOT----.qb-----PersistentHandler:我的CLSID...

两者都导致了这个问题的产生.

如果我要实现 IExtractImage 代替...我会看到同样的问题吗?

我知道 C# 不正式"支持这样做,这是我的问题所在吗?如果我用 C++ 实现这个,我会遇到同样的问题吗?

我想提一下,大约 1 分钟后文件似乎被释放了,事情又恢复了正常.

缩略图创建

一些字节被读入缓冲区......然后从中生成图像.

public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType){...一堆其他代码使用 (MemoryStream steam = new MemoryStream(buffer))使用 (var image = new Bitmap(steam))使用 (var scaled = new Bitmap(image, cx, cx)){hBitmap = scaled.GetHbitmap();hBitmap = (IntPtr)(hBitmap.ToInt64());}}

编辑 2:

做了一些更多的测试,我调用了 DeleteObject(hBitmap),(即使这会破坏缩略图),文件仍然被锁定.我什至从 GetThumbnail 中删除了所有代码......只是给出了相同的结果,文件被锁定.一定有更多的事情发生吗?

解决方案

原来您需要释放从 IInitializeWithStream 获得的 COM IStream 对象.

我通过阅读有关处理 COM 对象的更多信息得出了这个结论.

释放 COM 对象的正确方式?

我按照 MS 的示例说明如何包装 IStream

https://msdn.microsoft.com/en-us/library/jj200585%28v=vs.85%29.aspx

<块引用>

 公共类 StreamWrapper : Stream{私有 IStream m_stream;//使用 COM IStream 初始化包装器公共流包装器(IStream 流){如果(流==空){抛出新的 ArgumentNullException();}m_stream = 流;}//.... 一堆其他代码受保护的覆盖无效处置(布尔处置){如果(m_stream != null){Marshal.ReleaseComObject(m_stream);//释放文件m_stream = 空;}}}

这是一个示例.

点击上面的链接查看StreamWrapper的实现...

[ComVisible(true), ClassInterface(ClassInterfaceType.None)][ProgId("mythumbnailer.provider"), Guid("insert-your-guid-here")]公共类 QBThumbnailProvider : IThumbnailProvider, IInitializeWithStream{#region IInitializeWithStream私有StreamWrapper流{获取;放;}公共无效初始化(IStream流,int grfMode){//IStream 传递给我们的包装器,它处理我们的清理工作this.stream = new StreamWrapper(stream);}#endregion#region IThumbnailProviderpublic void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType){hBitmap = IntPtr.Zero;bitmapType = WTS_ALPHATYPE.WTSAT_ARGB;尝试{//... 一堆其他代码//以某种方式设置 hBitmap使用(MemoryStream流=新MemoryStream(缓冲区))使用 (var image = new Bitmap(stream))使用 (var scaled = new Bitmap(image, cx, cx)){hBitmap = scaled.GetHbitmap();}}捕获(异常前){}//释放 IStream COM 对象流处理();}#endregion}

基本上归结为两行代码

Marshal.ReleaseComObject(your_istream);//释放文件your_istream = null;

附注

使用 scaled.GetHbitmap(); 创建的 GDI 位图可能需要处理掉,但我找不到不丢失创建的缩略图的方法.

I've set up a thumbnail provider for a file type.

The project is made with

  • C#
  • .NET 4.5

And I am running Windows x64

My provider successfully generates the thumbnail as expected and I can move, delete, copy, ect, the file. The issue of the locking seems to be caused by the file being placed in a folder. At that point, moving, deleting, ect, the folder all show the error, "File In Use".

I have confirmed Explore locking the file using Sysinternal's Process Explorer, if you are familiar with it.

I've tried 2 approaches to try to resolve this...

  1. implemented IThumbnailProvider and IInitializeWithStream myself.
  2. used 3rd party Sharpshell

Both suffer from this same issue, the file not being released.

On Sharpshell's github, an issue has been started specifying this too. https://github.com/dwmkerr/sharpshell/issues/78

I associate the file type in the registry like so

HKEY_CLASSES_ROOT
---- .qb
      ----shellex
          ----{e357fccd-a995-4576-b01f-234630154e96} : my CLSID...

I have also tried instead...

HKEY_CLASSES_ROOT
---- .qb
     -----PersistentHandler : my CLSID...

Both result in this issue being created.

If I was to implement IExtractImage instead... will I see the same issue?

I know C# isn't "officially" supported to do this, is that where my issue lies? If I was to implement this in C++ would I wind up with the same issue?

EDIT:

I'd like to mention the file after around 1 minute seems to get freed, and things go back to normal.

Thumbnail Creation

Some bytes are read into a buffer... then then image is generated from that.

public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE   bitmapType)
{
    ... bunch of other code
    using (MemoryStream steam = new MemoryStream(buffer))
    using (var image = new Bitmap(steam))
    using (var scaled = new Bitmap(image, cx, cx))
    {
        hBitmap = scaled.GetHbitmap();
        hBitmap = (IntPtr)(hBitmap.ToInt64());
    }
}

EDIT 2:

Doing some more testing, I called DeleteObject(hBitmap), (even though this destroys the thumbnail), and the file is still locked. I even removed all the code from GetThumbnail... just gives the same result, file locked. There has to be something more going on?

解决方案

Turns out you need to release the COM IStream object that you get from IInitializeWithStream.

I came to this conclusion by reading more about disposing COM objects.

Proper way of releasing COM objects?

I followed MS's example on how to wrap IStream

https://msdn.microsoft.com/en-us/library/jj200585%28v=vs.85%29.aspx

    public class StreamWrapper : Stream
    {
        private IStream m_stream;

       // initialize the wrapper with the COM IStream
        public StreamWrapper(IStream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException();
            }

           m_stream = stream;
        }

       // .... bunch of other code

       protected override void Dispose(bool disposing)
        {
            if (m_stream != null)
            {
                Marshal.ReleaseComObject(m_stream); // releases the file
                m_stream = null;
            }
        }
    }

Here's a sample.

Follow the link above to see StreamWrapper's implementation...

[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
[ProgId("mythumbnailer.provider"), Guid("insert-your-guid-here")]
public class QBThumbnailProvider : IThumbnailProvider, IInitializeWithStream
{
    #region IInitializeWithStream

    private StreamWrapper stream{ get; set; }

    public void Initialize(IStream stream, int grfMode)
    {
        // IStream passed to our wrapper which handles our clean up
        this.stream = new StreamWrapper(stream);
    }

    #endregion

    #region IThumbnailProvider

    public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType)
    {
        hBitmap = IntPtr.Zero;
        bitmapType = WTS_ALPHATYPE.WTSAT_ARGB;

        try
        {
            //... bunch of other code

            // set the hBitmap somehow
            using (MemoryStream stream = new MemoryStream(buffer))
            using (var image = new Bitmap(stream))
            using (var scaled = new Bitmap(image, cx, cx))
            {
                hBitmap = scaled.GetHbitmap();
            }
        }
        catch (Exception ex)
        {
        }

        // release the IStream COM object
        stream.Dispose();
    }
    #endregion
}

Basically it comes down to two lines of code

Marshal.ReleaseComObject(your_istream); // releases the file
your_istream = null;

Side Note

The GDI Bitmap created with scaled.GetHbitmap(); probably needs to disposed of, but I can't find a way to do it without loosing the created thumbnail.

这篇关于使用我的缩略图提供程序后,资源管理器不会释放文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆