从 PE 文件或图标里获取并绘制大尺寸的图标(大于 32×32)

最近在创作一个类似 Windows 资源管理器的程序。有几个难关:动态加载图标与 ListView 的结合、获取文件详细信息(三个时间、大小[不是用 FileLen,所以可以支持 Double 级别的大小]、类型和图标)、弹出标准资源管理器菜单、获取超过 32×32 的图标。

其中只剩下最后这个没有攻破。今天在 Google 上查找,终于找到了突破口!感谢第一个发现这个函数的网友。

或许你会问:不是有 ExtractIcon 和 ExtractIconEx 吗?为什么费尽心思来找这个?这是由于 ExtractIcon 和 ExtractIconEx 只能提取 16×16、32×32 的图标,对于 Windows XP 的 48×48 图标已经无能为力,更别说 Windows Vista 的 256×256 的图标了。所以这个函数是专门用来提取大图标和不规则图标的(如 Windows Vista 的 ImageRes.dll 中第一个图标,大小是 40×40)。

现在附代码如下,可以不用 LoadIcon 直接获得 PE 文件或图标中的指定大小的图标。

Option Explicit

Private Declare Function PrivateExtractIcons Lib "user32" _
    Alias "PrivateExtractIconsA" (ByVal sFile As String, ByVal nIconIndex As Long, _
    ByVal cxIcon As Long, ByVal cyIcon As Long, ByVal phicon As Long, piconid As Long, _
    ByVal nIcons As Long, ByVal flags As Long) As Long

'精华!这个函数一般是找不到的!有了这个,不用使用 LoadIcon、ExtractIcon、ExtractIconEx 了
Public Declare Function DrawIconEx Lib "user32" (ByVal hDC As Long, ByVal xLeft As Long, ByVal yTop As Long, ByVal hIcon As Long, ByVal cxWidth As Long, ByVal cyWidth As Long, ByVal istepIfAniCur As Long, ByVal hbrFlickerFreeDraw As Long, ByVal diFlags As Long) As Long
Public Declare Function DestroyIcon Lib "user32" (ByVal hIcon As Long) As Long
Public Declare Function GetIconInfo Lib "user32" (ByVal hIcon As Long, piconinfo As ICONINFO) As Long

Public Type ICONINFO
    fIcon As Long
    xHotspot As Long
    yHotspot As Long
    hbmMask As Long
    hbmColor As Long
End Type

Public Const DI_NORMAL = &H3&
Public Const LR_DEFAULTCOLOR = &H0&
Public Const LR_DEFAULTSIZE = &H40

'封装之后的函数
Public Sub DrawIconToDC(ByVal PE_Icon As String, ByVal IconIndex As Long, ByVal hDC As Long, cX As Long, cY As Long, X As Long, Y As Long)
    'PE_Icon 是 PE 文件(*.exe;*.dll;*.ocx;*.vxd;*.cpl 等等)或图标文件的文件名
    'IconIndex 是图标的索引,以绝对值为准(如 0=0,-1=1)
    'hDC 是目标 DC(Device Context,设备上下文)。可以使用 GetDC(hWindow) 获取一个窗口的 DC。
    'cX 是欲加载的图标宽度
    'cY 是欲加载的图标高度
    'X 是绘制在目标上的 X 坐标(模式由 hDC 所指的设备所决定)
    'Y 是绘制在目标上的 Y 坐标‍(模式由 hDC 所指的设备所决定)
    Dim lRet As Long
    Dim phicon As Long
    Dim picon As Long
    'Dim cX As Long '欲加载的图标宽度
    'Dim cY As Long '欲加载的图标高度
                    'Windows 会自动根据 cX 和 cY 的值决定加载哪个图标(若有多种格式)
                    '如,存在 48×48、32×32 图标时,cX=36, cY=32 将加载 48×48 的图标,
                    '并按照 36×32 的大小输出

    'MsgBox PrivateExtractIcons("C:\Windows\System32\imageres.dll", -1, 0, 0, 0, picon, 1, 0)
    lRet = PrivateExtractIcons(PE_Icon, 2, cX, cY, VarPtr(phicon), picon, 1, LR_DEFAULTCOLOR) 'Or LR_DEFAULTSIZE)
    'MsgBox "Return val:" & lRet, vbInformation
    'Dim pII As ICONINFO
    'GetIconInfo phicon, pII
    DrawIconEx Me.hDC, X, Y, phicon, 0, 0, 0, 0, DI_NORMAL
    'Print "cX:" & pII.xHotspot * 2 & vbCrLf & "cY:" & pII.yHotspot * 2
    'MsgBox picon
    '必须销毁图标,因为 Windows 不会帮你
    DestroyIcon phicon
End Sub

使用方法:

Call DrawIconToDC("C:\Windows\System32\cmd.exe", 0, Picture1.hDC, 48, 48, 0, 0)
分享到 评论