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

问题描述 投票:0回答:3

我使用 Win32 SHGetFileInfo 来获取属于某个文件的图标的句柄。 stackoverflow 上也有很多关于如何做到这一点的描述,例如: Get Iconsused by shell

调用该函数后,您将获得一个带有图标句柄的结构。使用静态方法 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的描述中警告(点击查看)

使用该方法时,必须使用Win32 API中的DestroyIcon方法处理掉原来的图标,以保证资源得到释放。

并在Icon.Dispose(点击查看)

释放此图标使用的所有资源。

问题:

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

c# .net winapi icons idisposable
3个回答
10
投票

.NET Icon 类非常笨拙,需要您自己处理。有关 SHFILEICON 的 MSDN 文章对此毫不掩饰,您必须调用 DestroyIcon()。 Icon.FromHandle() 的 MSDN 文章也是如此。调用 DestroyIcon 的确切时间也很重要,它必须延迟到某些代码复制了该图标,或者直到您不再需要该图标并且通常会调用其 Dispose() 方法。 注意 MSDN 文章中的代码片段,它显示了提前调用 DestroyIcon() 的场景。在那种“特定”情况下可以,因为它被分配给 Form.Icon 属性。这是一个极端的情况,肯定不是您想要做的。

处理这个问题的唯一真正合适的方法是重写 Icon.FromHandle() 的行为并强制对象获取本机图标句柄的所有权。这样当你处置它时它会自动调用 DestroyIcon()。这需要一个 hack,让你做到这一点的图标构造函数是internal。反射来救援,您可以使用这篇文章中的代码

,注意 GetConstructor() 调用。通过编写一个执行此操作一百万次的小单元测试来开始感觉良好。如果你讨厌它,那么编写你自己的 IDisposable 包装器,这样你就可以处理图标

pinvoke DestroyIcon()。 OP添加。这个答案有一个错误。由于所有的评论,只见树木,见森林变得很残酷。因此我决定编辑这个答案。 (对不起,如果我冒犯了某人)


7
投票
.Net源代码已上线:
System.Drawing.Icon.cs

看看

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,正如备注部分所述

我在这方面经历了无尽的悲伤 - 我一直在尝试为表单的图标(以及任务栏中的图标)设置动画,而不会泄漏资源。

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

7
投票
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 与 Icon.Dispose

    

© www.soinside.com 2019 - 2024. All rights reserved.