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

多次调用SHGetFileInfo后无法返回图标句柄的问题求助

问题原因与解决方案

你遇到的是Windows图标句柄资源耗尽的问题:每次调用SHGetFileInfo获取图标句柄后,没有释放原生的hIcon句柄,系统能分配的图标句柄数量有限(约几千个),到3320次时就被耗尽,导致后续调用返回无效的0句柄。

另外你的函数还有个冗余问题:Add-Type被放在函数内部,每次调用都会重复定义IconFromFilePath类型,既浪费资源还可能引发类型重复定义错误,应该把这部分移到函数外部只执行一次。

修正后的代码

1. 先定义互操作类型(仅执行一次)

Add-Type -TypeDefinition '
    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;

    public class IconFromFilePath
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct SHFILEINFO
        {
            public IntPtr hIcon;
            public int iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        }

        [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool DestroyIcon(IntPtr hIcon);
    }
'

2. 修正后的图标获取函数

Function Icon-FromFilePath {
    Param($FilePath, [Switch]$LargeIcon)

    $Flags = 0x100  # SHGFI_ICON
    If (!$LargeIcon) { 
        $Flags += 0x1  # SHGFI_SMALLICON
    }

    $FileInfoStruct = New-Object IconFromFilePath+SHFILEINFO
    $StructSize = [System.Runtime.InteropServices.Marshal]::SizeOf($FileInfoStruct)

    $result = [IconFromFilePath]::SHGetFileInfo($FilePath, 0, [ref]$FileInfoStruct, $StructSize, $Flags)
    if ($result -eq [IntPtr]::Zero -or $FileInfoStruct.hIcon -eq [IntPtr]::Zero) {
        Write-Warning "无法获取文件 $FilePath 的图标"
        return $null
    }

    try {
        # 从原生句柄创建托管Icon对象
        $icon = [System.Drawing.Icon]::FromHandle($FileInfoStruct.hIcon)
        # 克隆图标,避免和原生句柄绑定导致失效
        $clonedIcon = [System.Drawing.Icon]::new($icon)
        return $clonedIcon
    }
    finally {
        # 必须释放SHGetFileInfo返回的原生句柄
        [void][IconFromFilePath]::DestroyIcon($FileInfoStruct.hIcon)
        # 释放临时的Icon对象
        $icon.Dispose()
    }
}

3. 修正后的测试脚本(添加资源释放)

Add-Type -AssemblyName System.Drawing, System.Windows.Forms

$counter = 0
Get-ChildItem C:\Windows\System32 | ForEach-Object {
    try {
        $counter++
        $icon = Icon-FromFilePath $_.FullName
        # 使用图标后必须释放托管资源
        if ($icon) {
            $icon.Dispose()
        }
        Write-Host "已处理第 $counter 个文件"
    }
    catch {
        Write-Host "Count: $counter"
        Write-Host "Error: $($_.Exception.Message)"
        $global:Struct_log = $FileInfoStruct
        pause
    }
}

关键说明

  • 释放原生句柄:SHGetFileInfo返回的hIcon是Windows原生资源,必须用DestroyIcon手动释放,托管的Icon对象不会自动清理这个原生句柄。
  • 克隆图标:FromHandle创建的Icon对象和原生句柄绑定,直接返回的话,后续释放原生句柄会导致Icon对象失效,因此需要克隆一个独立的托管Icon对象。
  • 释放托管资源:使用完Icon对象后调用Dispose(),避免托管内存泄漏。
  • 类型定义移到外部:Add-Type只执行一次,避免重复定义类型的冗余问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 22:31:20