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
精彩评论