Icon.FromHandle:我应该处理它,还是调用DestroyIcon?

时间:2015-06-22 12:09:00

标签: c# .net winapi icons idisposable

我使用Win32 SHGetFileInfo获取属于某个文件的图标的句柄。有很多描述如何执行此操作,也可以在stackoverflow上执行,例如:Get icons used by shell

调用该函数后,您有一个带有Icon图标句柄的结构。使用静态方法Icon.FromHandle我可以将它转换为System.Drawing.Icon类的对象。该类实现System.IDisposable。正确的用法如下:

using (Icon icon = Icon.FromHandle(shFileInfo.hIcon))
{
    // do what you need to do with the icon
}

离开using语句后,将放置图标对象。

MSDN在Icon.FromHandle (click to view)的说明中警告:

  

使用此方法时,必须使用Win32 API中的DestroyIcon方法处置原始图标,以确保释放资源。

并在Icon.Dispose (click to view)

  

释放此Icon使用的所有资源。

问题:

Dispose()对象是否足够,或者我应该同时调用Dispose()和DestroyIcon,还是调用DestroyIcon而不是Disposing对象?

3 个答案:

答案 0 :(得分:6)

.NET Icon类非常笨拙,需要自己处理。 SHFILEICON的MSDN文章没有任何关于它的内容,你必须调用DestroyIcon()。 Icon.FromHandle()的MSDN文章也是如此。你调用DestroyIcon的确切时间也很重要,它必须延迟,直到某些代码已经制作了图标副本或者你不再需要图标并且通常会调用它Dispose()方法。

请注意MSDN文章中的代码段,它显示了早期调用DestroyIcon()的场景。好的是特定的案例,因为它被分配给Form.Icon属性。一个角落的情况,肯定不是你想做的。

处理此问题的唯一正确方法是覆盖Icon.FromHandle()的行为并强制对象获取本机图标句柄的所有权。这样当你处理它时它会自动调用DestroyIcon()。这需要一个hack,允许你这样做的Icon构造函数是 internal 。反思救援,你可以使用代码from this post,注意GetConstructor()调用。通过编写一个这样做一百万次的小单元测试,开始感觉良好。如果你讨厌它,那么编写你自己的IDisposable包装器,这样你就可以配置图标 pinvoke DestroyIcon()。

答案 1 :(得分:3)

  

OP增加。这个答案有一个错误。由于所有的评论,看到森林穿过树木变得苛刻。因此我决定编辑这个答案。 (对不起,如果我冒犯了某人)

.net源代码在线:http://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Icon.cs,81a28d20524554ae

看一下Icon.FromHandle:

public static Icon FromHandle(IntPtr handle)
{
    IntSecurity.ObjectFromWin32Handle.Demand();
    return new Icon(handle);
}
internal Icon(IntPtr handle) : this(handle, false)
{
}
internal Icon(IntPtr handle, bool takeOwnership)
{
    if (handle == IntPtr.Zero)
    {
        throw new ArgumentException(SR.GetString(SR.InvalidGDIHandle,
              (typeof(Icon)).Name));
    }
    this.handle = handle;
    this.ownHandle = takeOwnership;
}

请注意,在Icon.FromHandle之后,ownHandle为false。

让我们看一下Dispose:

void Dispose(bool disposing)
{
    if (handle != IntPtr.Zero)
    {
        DestroyHandle();
    }
}

internal void DestroyHandle()
{
    if (ownHandle)
    {
        SafeNativeMethods.DestroyIcon(new HandleRef(this, handle));
        handle = IntPtr.Zero;
    }
}

结论:在Icon.FromHandle之后,字段ownHandle为false,因此Dispose / FromHandle不会调用DestroyIcon

  

因此:如果你使用Icon.FromHandle创建一个Icon,你必须Dispose()Icon以及调用DestroyIcon,就像备注部分所说的那样

答案 2 :(得分:3)

我在这方面没有悲伤 - 我一直试图在不泄漏资源的情况下为表单的图标(以及任务栏中的图标)设置动画。

当我处理图标(如MSDN上建议的)资源泄露时,当我使用“DestroyIcon”时,所有后续更新都失败了。下面的代码以正确的顺序显示所有内容。

API声明:

[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);

最终解决方案:

IntPtr iconHandle = dynamicBitmap.GetHicon();
Icon tempManagedRes = Icon.FromHandle(iconHandle);
this.Icon = (Icon)tempManagedRes.Clone();
tempManagedRes.Dispose();
DestroyIcon(iconHandle);

也发表在这个问题: Win32.DestroyIcon vs. Icon.Dispose