Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations wOOdy-Soft on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Get icon from exe using hwnd ?!? 4

Status
Not open for further replies.

BogdanMBM

Programmer
Aug 6, 2003
213
RO
Hi everyone! It's been a wile :) ...

I'm currently working on an application that should implement multiple desktops. I have a treeview that I fill with the existing desktops and under each desktop i add child nodes representing the windows. No problem so far, but I would like to add to each node that represents a window the icon of that window (like in TaskBar). Can anyone help me with this, please?
I'm trying to handle this by using LoadIcon API, but I can't get it to work. Following is my code (I've removed the declarations part, to be short):

-In a form with a treeview:
Private Sub Form_Load()
tvWindows.Nodes.Add , , "Primary Desktop", "Primary Desktop"
EnumWindows AddressOf EnumVisibleWindowsProc, ByVal 0&
tvWindows.Nodes.Item("Primary Desktop").Expanded = True
End Sub

- in a module:
Public Function EnumVisibleWindowsProc(ByVal hWnd As Long, ByVal lParam As Long) As Boolean
Dim MyNode As Node
Dim MyIcon As Long
Dim strTextFereastra As String, Ret As Long, MyClassName As String
Ret = GetWindowTextLength(hWnd)
strTextFereastra = Space(Ret)
GetWindowText hWnd, strTextFereastra, Ret + 1
MyClassName = Space(256)
GetClassName hWnd, MyClassName, 256
If strTextFereastra <> "" And IsWindowVisible(hWnd) <> 0 Then
MyIcon = LoadIcon(hWnd, IDI_APPLICATION)
'Form1.ImageList1.ListImages.Add , , LoadIcon(hWnd, IDI_APPLICATION)
Set MyNode = Form1.tvWindows.Nodes.Add("Primary Desktop", tvwChild, "a" & hWnd, strTextFereastra, MyIcon)

End If
EnumVisibleWindowsProc = True
End Function

Notice the use of LoadIcon, but it doesn't work. It always returns "0".
Thanks!

Hope I've been helpful,
Bogdan Muresan.
 
You should send the WM_GETICON message to a window to query its icon. Sometimes, a window fails to return the window handle in response to this message. (For example, if the window is hung). In that case you should use the GetClassLong function with GCL_HICONSM offset to query the window handle.
This is a typical way icon handles are retrieved.

After getting the icon handle, you should use the OleCreatePictureIndirect function to convert that icon handle to a VB Picture object, so that you can add it to an imagelist and use it in your treeview.

See the following code.
___
[tt]
Option Explicit

Private Declare Function SendMessageTimeout Lib "user32" Alias "SendMessageTimeoutA" (ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal fuFlags As Long, ByVal uTimeout As Long, lpdwResult As Long) As Long
Const WM_GETICON = &H7F
Const ICON_SMALL = 0
Const SMTO_ABORTIFHUNG = &H2

Private Declare Function GetClassLong Lib "user32" Alias "GetClassLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Const GCL_HICONSM = -34

Private Declare Sub CLSIDFromString Lib "ole32" (lpsz As Any, pclsid As Any)

Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicArray As Any, RefIID As Any, ByVal OwnsHandle As Long, IPic As Any) As Long
Private Type PictDesc
cbSizeofStruct As Long
PicType As Long
hImage As Long
xExt As Long
yExt As Long
End Type

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Function HandleToPicture(hIcon As Long, PicType As Long) As IPicture
Dim pd As PictDesc, IPic(15) As Byte
If hIcon = 0 Then Exit Function
pd.cbSizeofStruct = Len(pd)
pd.PicType = PicType
pd.hImage = hIcon
CLSIDFromString ByVal StrPtr("{00020400-0000-0000-C000-000000000046}"), IPic(0)
OleCreatePictureIndirect pd, IPic(0), -1, HandleToPicture
End Function

Function Window2Icon(hwnd As Long) As IPicture
Dim hIcon As Long
SendMessageTimeout hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, hIcon
If hIcon = 0 Then hIcon = GetClassLong(hwnd, GCL_HICONSM)
Set Window2Icon = HandleToPicture(hIcon, vbPicTypeIcon)
End Function

Private Sub Form_Load()
'start notepad
Shell "notepad.exe"
DoEvents

'get its handle
Dim hNP As Long
hNP = FindWindow("Notepad", vbNullString)

'steal its icon
Me.Picture = Window2Icon(hNP)
End Sub[/tt]
 
>to return the window handle
>to query the window handle

Oops! I meant the icon handle.
 
First point: sadly you are getting the use of LoadIcon completely wrong - it doesn't take an hWnd, and IDI_APPLICATIOn (which you can only use when the first parameter is null) simply returns the system-defined default appliation icon. You should probably be looking at the ExtractIcon or ExtractIconEx API calls, which in turn means that you need a full path name, which means that you probably need to look at the GetModuleFileName API

Second Point: your final parameter in Nodes.Add is the handle of an icon, when it should actually be the index to an image in an imagelist (although I can se from the commented out line that you did realise this at some point, even though the commented out line would not have worked, because you need to be adding a Picture object, not an hIcon))
 
Hey Guyz, that's great!
Strongm, you are absolutly wrigth. I did get it all wrong.
Hypetia, your example works great.
A star for bowth of you!
But! Yes, there is a but:

Here's my revised code with the one from Hypetia:

Public Function EnumVisibleWindowsProc(ByVal hwnd As Long, ByVal lParam As Long) As Boolean
Dim MyNode As Node
Dim MyIcon As Long
Dim strTextFereastra As String, Ret As Long, MyClassName As String
Static intCounter As Integer
Ret = GetWindowTextLength(hwnd)
strTextFereastra = Space(Ret)
GetWindowText hwnd, strTextFereastra, Ret + 1
MyClassName = Space(256)
GetClassName hwnd, MyClassName, 256
If strTextFereastra <> "" And IsWindowVisible(hwnd) <> 0 And strTextFereastra = "Form1" Then
Form1.Picture = Window2Icon(hwnd)
Form1.ImageList1.ListImages.Add intCounter + 1, "I" & intCounter + 1, Form1.Picture
Set MyNode = Form1.tvWindows.Nodes.Add("Primary Desktop", tvwChild, "a" & hwnd, strTextFereastra, Form1.ImageList1.ListImages(intCounter + 1).Key)
intCounter = intCounter + 1
End If
EnumVisibleWindowsProc = True
End Function

'/ From Hypetia:
Function HandleToPicture(hIcon As Long, PicType As Long) As IPicture
Dim pd As PictDesc, IPic(15) As Byte
If hIcon = 0 Then Exit Function
pd.cbSizeofStruct = Len(pd)
pd.PicType = PicType
pd.hImage = hIcon
CLSIDFromString ByVal StrPtr("{00020400-0000-0000-C000-000000000046}"), IPic(0)
OleCreatePictureIndirect pd, IPic(0), -1, HandleToPicture
End Function

Function Window2Icon(hwnd As Long) As IPicture
Dim hIcon As Long
SendMessageTimeout hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, hIcon
If hIcon = 0 Then hIcon = GetClassLong(hwnd, GCL_HICONSM)
Set Window2Icon = HandleToPicture(hIcon, vbPicTypeIcon)
End Function
' End of "From Hypetia /"

Notice the following 2 lines:
Form1.Picture = Window2Icon(hwnd)
Form1.ImageList1.ListImages.Add intCounter + 1, "I" & intCounter + 1, Form1.Picture

I also put this: "And strTextFereastra = "Form1"" in the "IF" statement, just to run this for my form only. Here's the problem: after executing "Form1.Picture = Window2Icon(hwnd)", the Form1.Picture is "0".
Cant tell why :(

For icons from other processes it's also "0". I originaly suspected that it only work for the same process or for child processes, that's why I put that condition ("And strTextFereastra = "Form1"") to capture the icon from my own form, but it still doesn't work (returns 0).
What am I doing wrong here?!?

Thank you.

Hope I've been helpful,
Bogdan Muresan.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top