使用我的缩略图提供程序后,资源管理器不会释放文件 [英] Explorer Wont Release Files After Using My Thumbnail Provider
问题描述
我已经为文件类型设置了缩略图提供程序.
项目是用
- C#
- .NET 4.5
我运行的是 Windows x64
我的提供者按预期成功生成缩略图,我可以移动、删除、复制等文件.锁定问题似乎是由文件放置在文件夹中引起的.此时,移动、删除等,文件夹都显示错误,文件正在使用".
如果您熟悉,我已经确认使用 Sysinternal 的 Process Explorer 来探索锁定文件.
我尝试了 2 种方法来尝试解决此问题...
- 自己实现了
IThumbnailProvider
和IInitializeWithStream
. - 使用第 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 对象的更多信息得出了这个结论.
我按照 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...
- implemented
IThumbnailProvider
andIInitializeWithStream
myself. - 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屋!