我使用 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方法处理掉原来的图标,以保证资源得到释放。
释放此图标使用的所有资源。
问题:
Dispose() 对象就足够了,还是应该同时调用Dispose() 和DestroyIcon,或者调用DestroyIcon 而不是Dispose 对象?
.NET Icon 类非常笨拙,需要您自己处理。有关 SHFILEICON 的 MSDN 文章对此毫不掩饰,您必须调用 DestroyIcon()。 Icon.FromHandle() 的 MSDN 文章也是如此。调用 DestroyIcon 的确切时间也很重要,它必须延迟到某些代码复制了该图标,或者直到您不再需要该图标并且通常会调用其 Dispose() 方法。 注意 MSDN 文章中的代码片段,它显示了提前调用 DestroyIcon() 的场景。在那种“特定”情况下可以,因为它被分配给 Form.Icon 属性。这是一个极端的情况,肯定不是您想要做的。
处理这个问题的唯一真正合适的方法是重写 Icon.FromHandle() 的行为并强制对象获取本机图标句柄的所有权。这样当你处置它时它会自动调用 DestroyIcon()。这需要一个 hack,让你做到这一点的图标构造函数是internal。反射来救援,您可以使用这篇文章中的代码
,注意 GetConstructor() 调用。通过编写一个执行此操作一百万次的小单元测试来开始感觉良好。如果你讨厌它,那么编写你自己的 IDisposable 包装器,这样你就可以处理图标和 pinvoke DestroyIcon()。 OP添加。这个答案有一个错误。由于所有的评论,只见树木,见森林变得很残酷。因此我决定编辑这个答案。 (对不起,如果我冒犯了某人)
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”时,所有后续更新都失败了。下面的代码以正确的顺序显示了所有内容。我在这方面经历了无尽的悲伤 - 我一直在尝试为表单的图标(以及任务栏中的图标)设置动画,而不会泄漏资源。
[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