【问题标题】:Icon.FromHandle: should I Dispose it, or call DestroyIcon?Icon.FromHandle:我应该 Dispose 还是调用 DestroyIcon?
【发布时间】:2015-09-07 21:17:18
【问题描述】:

我使用 Win32 SHGetFileInfo 来获取属于某个文件的图标的句柄。有很多关于如何做到这一点的描述,也在stackoverflow上,例如:Get icons used 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 (click to view)的描述中警告:

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

Icon.Dispose (click to view)

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

问题:

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

【问题讨论】:

    标签: c# .net winapi icons idisposable


    【解决方案1】:

    .NET Icon 类非常笨拙,需要自己处理。 SHFILEICON 的 MSDN 文章对此毫不含糊,您必须调用 DestroyIcon()。 Icon.FromHandle() 的 MSDN 文章也是如此。您调用 DestroyIcon 的确切时间也很重要,它必须延迟到 一些 代码复制了图标或者直到您不再需要该图标并且通常会调用它Dispose() 方法。

    注意 MSDN 文章中的代码 sn-p,它显示了提前调用 DestroyIcon() 的场景。好的,在那个 specific 情况下,因为它被分配给 Form.Icon 属性。一个极端的案例,肯定不是你想做的。

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

    【讨论】:

      【解决方案2】:

      由 OP 添加。这个答案有错误。由于所有的cmets,通过树木看到森林变得很困难。因此我决定编辑这个答案。 (对不起,如果我冒犯了某人)

      .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 创建一个图标,您将不得不 Dispose() 该图标以及调用 DestroyIcon,正如备注部分所述

      【讨论】:

      【解决方案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

      【讨论】:

      • 正确答案,确保每次调用 DestroyIcon() 时只调用 GetHicon() 一次,否则会泄漏。
      猜你喜欢
      • 1970-01-01
      • 2010-11-17
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 2023-01-10
      • 2011-11-23
      • 1970-01-01
      • 2011-11-21
      相关资源
      最近更新 更多