Explorer Wont Release Files After Using My Thumbnail Provider

▼魔方 西西 提交于 2021-01-28 05:06:05

问题


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?


回答1:


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.



来源:https://stackoverflow.com/questions/28777383/explorer-wont-release-files-after-using-my-thumbnail-provider

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!