You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用ExtractIconExW多次致内存泄漏崩溃,原因是什么?

调用ExtractIconExW多次后内存崩溃的解决办法

核心问题

  1. 销毁句柄用错函数:图标句柄必须用user32.dll中的DestroyIcon销毁,DeleteObject仅适用于GDI对象(如画笔、位图),对图标句柄无效,导致句柄持续泄漏,最终触发"内存不足"错误。
  2. 参数类型错误:ExtractIconEx的图标索引要求为整数类型,你直接传递分割后的字符串s(1),会引发隐式转换异常或获取错误的图标。
  3. 句柄提前销毁:CreateBitmapSourceFromHIcon不会接管图标句柄的生命周期,你在Finally块中直接销毁icon句柄,会导致返回的BitmapSource引用已失效的句柄,引发随机崩溃。

修复代码

1. 修正函数声明

替换DeleteObject为DestroyIcon的DllImport声明:

<DllImport("Shell32.dll", EntryPoint:="ExtractIconExW", CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>
Public Shared Function ExtractIconEx(sFile As String, iIndex As Integer, ByRef piLargeVersion As IntPtr, ByRef piSmallVersion As IntPtr, amountIcons As Integer) As Integer
End Function

<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
End Function

2. 修正ExtractIcon函数实现

Public Shared Function ExtractIcon(ref As String) As BitmapSource
    Dim s() As String = Split(ref, ",")
    Dim iconPath As String = s(0)
    ' 将索引转为整数,默认使用0
    Dim iconIndex As Integer = If(s.Length > 1, Integer.Parse(s(1)), 0)
    Dim largeIcon As IntPtr = IntPtr.Zero
    Dim smallIcon As IntPtr = IntPtr.Zero
    Dim resultBitmap As BitmapSource = Nothing

    Try
        Functions.ExtractIconEx(iconPath, iconIndex, largeIcon, smallIcon, 1)

        If smallIcon <> IntPtr.Zero Then
            ' 通过GDI+中转,将图标数据复制到WPF BitmapSource,避免句柄依赖
            Using gdiIcon As New System.Drawing.Icon(smallIcon)
                Using gdiBmp As System.Drawing.Bitmap = gdiIcon.ToBitmap()
                    resultBitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                        gdiBmp.GetHbitmap(),
                        IntPtr.Zero,
                        Int32Rect.Empty,
                        BitmapSizeOptions.FromEmptyOptions())
                    resultBitmap.Freeze() ' 冻结后提升性能,支持跨线程访问
                End Using
            End Using
            ' 数据已复制,可安全销毁原图标句柄
            Functions.DestroyIcon(smallIcon)
        End If
    Finally
        ' 无论是否成功,大图标句柄必须销毁
        If largeIcon <> IntPtr.Zero Then
            Functions.DestroyIcon(largeIcon)
        End If
    End Try

    Return resultBitmap
End Function

关键说明

  • 通过System.Drawing.Icon中转,可将图标数据复制到WPF的BitmapSource中,原句柄能立即销毁,彻底避免泄漏。
  • 所有通过ExtractIconEx获取的句柄(包括未使用的大图标句柄),都必须用DestroyIcon销毁。
  • 冻结BitmapSource可避免后续操作引发的线程安全问题,同时提升渲染性能。

内容的提问来源于stack exchange,提问作者miss programmer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 03:11:18