开发者

WM_GETICON sometimes returns no icon handle

I'm trying to show all window titles including the corresponding icons, much like Windows Task Manager does. This works only to a certain extent though - although I am able to get the windows' title bar texts, the icon is not always available.

For getting the icon I pass the WM_GETICON开发者_运维问答 message to SendMessage (source):

Public Const WM_GETICON As UInteger = &H7F
Public Function GetWindowIcon(ByVal WindowHandle As IntPtr) As Icon
    Dim IconHandle As IntPtr = SendMessage(WindowHandle, WM_GETICON, 0, 0)
    If Not IconHandle = IntPtr.Zero Then
        Return Icon.FromHandle(IconHandle)
    Else
        Return Nothing
    End If
End Function

For some windows, this returns just the correct icon. For others, it returns Nothing since IconHandle is equal to 0. In Windows Task Manager and on the taskbar they show up just fine.

What could be the cause of this, and how might I go about solving this?


Using some copy-and-pasting and messing around, I ended up with the following code... But it now works for all windows.

Basically, it tries WM_GETICON to get a large icon. If it fails, it calls GetClassLong, which sometimes includes the icon. Otherwise, WM_GETICON is used to get the small icon. In the first two cases, I had to convert it into a Bitmap, resize it to 16x16 (I need that size), and then convert it back to an Icon.

Public Function GetClassLongPtr(ByVal hWnd As IntPtr, ByVal nIndex As Integer) As IntPtr
    If IntPtr.Size > 4 Then
        Return GetClassLongPtr64(hWnd, nIndex)
    Else
        Return New IntPtr(GetClassLongPtr32(hWnd, nIndex))
    End If
End Function

<DllImport("user32.dll", EntryPoint:="GetClassLong")> _
Public Function GetClassLongPtr32(ByVal hWnd As IntPtr, ByVal nIndex As Integer) As UInteger
End Function

<DllImport("user32.dll", EntryPoint:="GetClassLongPtr")> _
Public Function GetClassLongPtr64(ByVal hWnd As IntPtr, ByVal nIndex As Integer) As IntPtr
End Function

<DllImport("user32.dll")> _
Public Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Boolean, ByVal lParam As Int32) As Integer
End Function


Public Const WM_GETICON As UInteger = &H7F
Public Function GetWindowIcon(ByVal WindowHandle As IntPtr) As Icon
    Dim IconHandle As IntPtr = SendMessage(WindowHandle, WM_GETICON, 1, 0)
    If Not IconHandle = IntPtr.Zero Then
        Dim _icon = Icon.FromHandle(IconHandle)
        Dim bmp = _icon.ToBitmap
        Dim scale_factor As Single = 16 / _icon.Size.Width

        ' Make a bitmap for the result.
        Dim bm_dest As New Bitmap( _
            CInt(bmp.Width * scale_factor), _
            CInt(bmp.Height * scale_factor))

        ' Make a Graphics object for the result Bitmap.
        Dim gr_dest As Graphics = Graphics.FromImage(bm_dest)

        ' Copy the source image into the destination bitmap.
        gr_dest.DrawImage(bmp, 0, 0, _
            bm_dest.Width + 1, _
            bm_dest.Height + 1)

        Return MakeIcon(bm_dest, 16, False)
        'Return Icon.FromHandle(IconHandle)
    Else
        IconHandle = GetClassLongPtr(WindowHandle, -34)
        If Not IconHandle = IntPtr.Zero Then
            Dim _icon = Icon.FromHandle(IconHandle)

            Dim bmp = _icon.ToBitmap
            Dim scale_factor As Single = 16 / _icon.Size.Width

            ' Make a bitmap for the result.
            Dim bm_dest As New Bitmap( _
                CInt(bmp.Width * scale_factor), _
                CInt(bmp.Height * scale_factor))

            ' Make a Graphics object for the result Bitmap.
            Dim gr_dest As Graphics = Graphics.FromImage(bm_dest)

            ' Copy the source image into the destination bitmap.
            gr_dest.DrawImage(bmp, 0, 0, _
                bm_dest.Width + 1, _
                bm_dest.Height + 1)

            Return MakeIcon(bm_dest, 16, False)
        Else
            IconHandle = SendMessage(WindowHandle, WM_GETICON, 1, 0)
            If Not IconHandle = IntPtr.Zero Then
                Dim _icon = Icon.FromHandle(IconHandle)
                Dim bmp = _icon.ToBitmap
                Dim scale_factor As Single = 16 / _icon.Size.Width

                ' Make a bitmap for the result.
                Dim bm_dest As New Bitmap( _
                    CInt(bmp.Width * scale_factor), _
                    CInt(bmp.Height * scale_factor))

                ' Make a Graphics object for the result Bitmap.
                Dim gr_dest As Graphics = Graphics.FromImage(bm_dest)

                ' Copy the source image into the destination bitmap.
                gr_dest.DrawImage(bmp, 0, 0, _
                    bm_dest.Width + 1, _
                    bm_dest.Height + 1)

                Return MakeIcon(bm_dest, 16, False)
            Else
                Return Nothing
            End If
        End If
    End If
End Function



''' <summary>
''' Converts an image into an icon.
''' </summary>
''' <param name="img">The image that shall become an icon</param>
''' <param name="size">The width and height of the icon. Standard
''' sizes are 16x16, 32x32, 48x48, 64x64.</param>
''' <param name="keepAspectRatio">Whether the image should be squashed into a
''' square or whether whitespace should be put around it.</param>
''' <returns>An icon!!</returns>
Private Function MakeIcon(ByVal img As Image, ByVal size As Integer, ByVal keepAspectRatio As Boolean) As Icon
    Dim square As New Bitmap(size, size)
    ' create new bitmap
    Dim g As Graphics = Graphics.FromImage(square)
    ' allow drawing to it
    Dim x As Integer, y As Integer, w As Integer, h As Integer
    ' dimensions for new image
    If Not keepAspectRatio OrElse img.Height = img.Width Then
        ' just fill the square
        x = 0
        y = 0
        ' set x and y to 0
        ' set width and height to size
        w = size
        h = size
    Else
        ' work out the aspect ratio
        Dim r As Single = CSng(img.Width) / CSng(img.Height)

        ' set dimensions accordingly to fit inside size^2 square
        If r > 1 Then
            ' w is bigger, so divide h by r
            w = size
            h = CInt(Math.Truncate(CSng(size) / r))
            x = 0
            ' center the image
            y = (size - h) \ 2
        Else
            ' h is bigger, so multiply w by r
            w = CInt(Math.Truncate(CSng(size) * r))
            h = size
            y = 0
            ' center the image
            x = (size - w) \ 2
        End If
    End If

    ' make the image shrink nicely by using HighQualityBicubic mode
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default
    g.DrawImage(img, x, y, w, h)
    ' draw image with specified dimensions
    'g.Flush()
    ' make sure all drawing operations complete before we get the icon
    ' following line would work directly on any image, but then
    ' it wouldn't look as nice.
    Return Icon.FromHandle(square.GetHicon())
End Function
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜