简体   繁体   中英

Explorer Wont Release Files After Using My Thumbnail Provider

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM