Jump to content
Sign in to follow this  
realMo

Turning an icon handle into PATH to Exe and Nr/ID

Recommended Posts

realMo

I need to get the right icon for all opened windows,

you know the ones on the top-left of the title bar

(or the ones that you can select when you press Alt-Tab)

and use them in my GUI with GUICtrlCreateIcon()

This is not as straightforward as I thought, because

getting the path to the executable is not enough.

The icon is often not the first icon in the executable.

(For instance c:\WINDOWS\explorer.exe will have a different

icon depending on the directory you select and whether

you have folder view open or not.)

I found a script on the forum that seemed to solve my problem,

( How to use icon handles in ListViews? Not possible? ) but it uses a ListView.

The script does find the right icons, but it uses handles to the icons.

The script then resets the icons in the ListView using the newly found handles and DllCall()s (which I don't know how to use)

Both GUICtrlCreateIcon() and GUICtrlSetImage() can only use the

filename/path of the icon to be loaded.

What I need to be able to do is either:

A: get the filename of the executable and the icon Nr/ID from the icon handle

or

B: reset an icon created with GUICtrlCreateIcon() by using an icon handle

And I have no idea how to do either

Here's a simplified version of what I'm trying to do:

#include <GUIConstants.au3>
#Include <SendMessage.au3>
#include <WindowsConstants.au3>

Opt("MustDeclareVars", 0)

;Uncomment to run some programs to illustrate the icon problem:
;ShellExecute("C:\WINDOWS\explorer.exe", " C:\")        ;==> icon: a hard disk
;ShellExecute("C:\WINDOWS\explorer.exe", " /e, C:\")    ;==> icon: an opened folder
;And if D: is you DVD-Drive:
;ShellExecute("C:\WINDOWS\explorer.exe", " C:\WINDOWS") ;==> icon: an opened folder with a magnifying glass
;ShellExecute("C:\WINDOWS\explorer.exe", " D:\")        ;==> icon: a CD/DVD

$myGUI = GUICreate("First Four Processes", 633, 476, 483, 128)

;Just four icons in this example, set them to make them visible
Global $Icons[4]
$Icons[0] = GUICtrlCreateIcon("shell32.dll", 24, 80,  40, 80, 80)
$Icons[1] = GUICtrlCreateIcon("shell32.dll", 24, 80, 144, 80, 80)
$Icons[2] = GUICtrlCreateIcon("shell32.dll", 24, 80, 246, 80, 80)
$Icons[3] = GUICtrlCreateIcon("shell32.dll", 24, 80, 348, 80, 80)
Global $x=0

$winList = WinList()
For $i = 1 To $winList[0][0]
    $winHandle = $winList[$i][1]
    $winTitle = $winList[$i][0]
    If $winTitle <> "" And IsVisible($winHandle) And _
        WinGetProcess($winHandle) <> @AutoItPID And $winHandle <> 0 Then
            $hIcon = getIconHandleByHwnd($winHandle)
            
            ;This is where I'm stuck:
            ;Possible solution A:
            ;$iconExe = _getIconExeFromHandle($hIcon)
            ;$iconNr = _getIconNrFromHandle($hIcon)
            
            ;Just the first four in this example:
            $x=$x+1
            If $x > 3 Then ExitLoop
            
            ;GUICtrlSetImage($Icons[$x], $iconExe, $iconNr)
            
            ;Or alternatively, possible solution B something like:
            ;_resetIcon($Icons[$x], $hIcon)
            

    EndIf
Next

GUISetState()

While 1
    $msg = GUIGetMsg()

    Switch $msg
        Case $GUI_EVENT_CLOSE
            ExitLoop
    EndSwitch
WEnd
Exit

;required functions

Func IsVisible($handle)
    If BitAND(WinGetState($handle), 2) Then
        Return 1
    Else
        Return 0
    EndIf
EndFunc   ;==>IsVisible

Func getIconHandleByFile($pszPath)
    $SHGFI_ICON = 0x100        ;Get icon
    $SHGFI_SMALLICON = 0x1     ;Get Small Icon

    $dwFileAttributes = 0
    $psfi = DllStructCreate("uint;int;dword;char[260];char[80]") ; thanks Holger!
    $cbSizeFileInfo = DllStructGetSize($psfi)
    $uFlags = BitOR($SHGFI_ICON, $SHGFI_SMALLICON)

    $hImgSmall = DllCall('shell32.dll', 'int', 'SHGetFileInfo', _
            'str', $pszPath, _
            'uint', $dwFileAttributes, _
            'ptr', DllStructGetPtr($psfi), _
            'uint', $cbSizeFileInfo, _
            'uint', $uFlags)

    Return DllStructGetData($psfi, 1)
EndFunc   ;==>getIconHandleByFile

Func getIconHandleByHwnd($hwnd)
    $hwnd = HWnd($hwnd)
;    $WM_GETICON = 0x7F
    $GCL_HICONSM = -34
    $GCL_HICON = -14
    $IDI_APPLICATION = 32512
    $hIcon = 0
    $hIcon = _SendMessage ($hwnd, $WM_GETICON, False, 0)
    If ($hIcon == 0) Then $hIcon = _SendMessage ($hwnd, $WM_GETICON, True, 0)
    If ($hIcon == 0) Then
        $hIcon = DllCall("user32.dll", "int", "GetClassLong", "hwnd", $hwnd, "int", $GCL_HICONSM)
        $hIcon = $hIcon[0]
    EndIf
    If ($hIcon == 0) Then
        $hIcon = DllCall("user32.dll", "int", "GetClassLong", "hwnd", $hwnd, "int", $GCL_HICON)
        $hIcon = $hIcon[0]
    EndIf
    If ($hIcon == 0) Then
        $pid = WinGetProcess(HWnd($hwnd))
        $path = getExePathByPid($pid)
        $hIcon = getIconHandleByFile($path)
    EndIf
    Return $hIcon
EndFunc   ;==>getIconHandleByHwnd

Func getExePathByPid($pid)
    $objWMIService = ObjGet("winmgmts:\\.\root\CIMV2")
    $colItems = $objWMIService.ExecQuery ("SELECT * FROM Win32_Process WHERE ProcessId = " & $pid, "WQL", 0x10 + 0x20)
    If IsObj($colItems) Then
        For $objItem In $colItems
            Return $objItem.ExecutablePath
        Next
    EndIf
EndFunc   ;==>getExePathByPid

There may be a a third solution that I've overlooked.

Any suggestions on how to do this or even where to look for an answer would be greatly appreciated.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×