Sign in to follow this  
Followers 0

Get Systray Icons [Solved]

11 posts in this topic

Posted (edited)

For my current Project (to be renamed accordingly on the next release :D ) I'm looking for a way to acquire the Systray Icons. For fetching infos on the Systray buttons I use wraithdu's excellent Using the _SysTrayGetButtonInfo() function it is possible to get the Imagelist index for the buttons, but as the Imagelist is being created by explorer.exe there seems to be no easy way to access that list. I've found some postings around the net mentioning dll injection... but I find that idea quite disturbing. Currently I'm fetching the file icons of the underlying processes, but sadly those often do not match the ones display in the Systray.

I've run out of ideas on this one, maybe someone else has done some investigation on this already :oops:?

Edited by KaFu

Share this post


Link to post
Share on other sites



Posted

Anyone :D?

Share this post


Link to post
Share on other sites

Posted

Ok, 1 idea, maybe you can use _SysTray UDF to find out the path to the exe, extract the ico from the exe, or extract the bmp, convert it to ico and use it?

Currently I'm fetching the file icons of the underlying processes, but sadly those often do not match the ones display in the Systray.

Yeah, that might happen, oh well, its better some icon than no icon.

Share this post


Link to post
Share on other sites

Posted (edited)

Thanks for the feedback, that's exactly what I'm currently doing, but fetching the ones that are actually displayed would be much nicer. Searched a little more and only found solutions where custom dlls are being injected into the “explorer.exe” process. I neither can create such a dll nor do I think it’s a nice behavior :D...

Edited by KaFu

Share this post


Link to post
Share on other sites

Posted

For my current Project (to be renamed accordingly on the next release :) ) I'm looking for a way to acquire the Systray Icons. For fetching infos on the Systray buttons I use wraithdu's excellent Using the _SysTrayGetButtonInfo() function it is possible to get the Imagelist index for the buttons, but as the Imagelist is being created by explorer.exe there seems to be no easy way to access that list. I've found some postings around the net mentioning dll injection... but I find that idea quite disturbing. Currently I'm fetching the file icons of the underlying processes, but sadly those often do not match the ones display in the Systray.

I've run out of ideas on this one, maybe someone else has done some investigation on this already ;)?

Greetings KaFu

You can get most* of the icons from the notification area using shared memory code.

The traydata struct in the TBBUTTON/TBBUTTONINFO structs Param element has a handle to the toolbar button icon.

The traydata struct is undocumented but works in XP, Vista and Win 7 x86/x64

I use this in my _RefreshNotificationAreaIcons and _RefreshTrayIcon() UDFs

The SysTray UDF only retrieves the owner window handle from the traydata struct.

Get and use Imagelist From Another Process

http://www.xtremevbtalk.com/showthread.php?t=128730

I ran the above struct code, and it does return some systray (XP) imagelist info (image count, icon size, etc.).

The bitmap and memdc handles as mentioned are not accessible outside of the explorer process.

The imagelist struct is different from the standard imagelist, _GUICtrlToolbar_GetImageList returns a handle,

but ImageList_GetImageCount() returns 0.

*As pointed out in that vb forum, some apps that frequently update their icons (temp, cpu use, etc.)

will update the icon image in the notification areas imagelist with Shell_NotifyIcon + NIM_MODIFY,

then destroy the icon handle, so the notification area imagelist has the current icon, but the handle in the traydata struct is not valid.

Taskmgr replacements Process Explorer and Process Hacker 2 have process history tray icons that return invalid icon handles.

I wrote some tray icon retrieval code a few years back, here's an updated example.

For XP/2003,Vista/2008,Win7/2008R2 x86/x64

;coded by rover 2k11
#NoTrayIcon
#Include <WinAPIEx.au3>
#include <WinAPI.au3>
#include <Process.au3>
#include <GuiListView.au3>
#include <GuiImageList.au3>
#include <Constants.au3>
#Include <APIConstants.au3>
#include <WindowsConstants.au3>
#Include <ToolBarConstants.au3>
#Include <ProcessConstants.au3>
#include <MemoryConstants.au3>

Opt("MustDeclareVars", 1)

Global Const $PROCESS_ACCESS = BitOR($PROCESS_VM_OPERATION, $PROCESS_VM_READ)
Global $iTbar = 0
Global $hOwnerWin, $hTrayWnd, $hTrayNotifyWnd, $hSysPager, $hToolbar, _
  $iDLLUser32 = DllOpen("user32.dll"), $iDLLKrnl32 = DllOpen("kernel32.dll"), _
  $tTBBUTTON, $pTBBUTTON, $iTBBUTTON, $tTRAYDATA, $pTRAYDATA, $iTRAYDATA, _
  $iImg = 0, $iIdx = 0, $iCount, $iPIDExp, $hProcess, $pAddress, $iPID, $aRet, $sRet, $hIconTray, $hIcon
$hTrayWnd = WinGetHandle("[CLASS:Shell_TrayWnd]")
$hTrayNotifyWnd = ControlGetHandle($hTrayWnd, "", "[CLASS:TrayNotifyWnd]")
$hSysPager = ControlGetHandle($hTrayNotifyWnd, "", "[CLASS:SysPager]")
$hToolbar = ControlGetHandle($hSysPager, "", "[CLASS:ToolbarWindow32; INSTANCE:1]");User Promoted Notification Area/Notification Area/SysTray

Switch @OSVersion
Case "WIN_2008R2", "WIN_7"
  Switch $iTbar
   Case 1 ;Overflow Notification Area
	$hToolbar = ControlGetHandle(WinGetHandle("[CLASS:NotifyIconOverflowWindow]"), "", "[CLASS:ToolbarWindow32; INSTANCE:1]")
   Case 2 ;System Promoted Notification Area
	$hToolbar = ControlGetHandle($hTrayNotifyWnd, "", "[CLASS:ToolbarWindow32; INSTANCE:2]")
  EndSwitch
EndSwitch

$tTBBUTTON = DllStructCreate("int Bitmap;int Command;byte State;byte Style;align;dword_ptr Param;int_ptr String")
$pTBBUTTON = DllStructGetPtr($tTBBUTTON)
$iTBBUTTON = DllStructGetSize($tTBBUTTON)
$tTRAYDATA = DllStructCreate("hwnd hwnd;uint uID;uint uCallbackMessage;dword Reserved[2];ptr hIcon")
$pTRAYDATA = DllStructGetPtr($tTRAYDATA)
$iTRAYDATA = DllStructGetSize($tTRAYDATA)
$iPIDExp = WinGetProcess($hToolbar)
If @error Or $iPIDExp = -1 Then exit
$aRet = DllCall($iDLLKrnl32, "ptr", "OpenProcess", "dword", _
  $PROCESS_ACCESS, "int", 0, "int", $iPIDExp)
If @error Or $aRet[0] = 0 Then Exit
$hProcess = $aRet[0]
$aRet = DllCall($iDLLUser32, "lparam", "SendMessageW", "hwnd", _
  $hToolbar, "int", $TB_BUTTONCOUNT, "wparam", 0, "lparam", 0)
If @error Or $aRet[0] < 1  Then
DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hProcess)
Exit
EndIf
$iCount = $aRet[0] - 1
$aRet = DllCall($iDLLKrnl32, "ptr", "VirtualAllocEx", "ptr", $hProcess, "ptr", 0, "ulong_ptr", _
  $iTBBUTTON, "dword", BitOR($MEM_RESERVE, $MEM_COMMIT), "dword", $PAGE_READWRITE)
If @error Or $aRet[0] = 0 Then
DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hProcess)
Exit
EndIf
$pAddress = $aRet[0]

GUICreate("Notification Icons", 600, 600)
GUISetBkColor(0x494949)
Global $hListView = GUICtrlCreateListView("", 2, 2, 596, 596)
_GUICtrlListView_SetExtendedListViewStyle($hListView, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_SUBITEMIMAGES))
Global $hImage = _GUIImageList_Create(16,16,5,5)
_GUICtrlListView_SetImageList($hListView, $hImage, 1)
_GUICtrlListView_AddColumn($hListView, "Tray", 38)
_GUICtrlListView_AddColumn($hListView, "Proc", 38)
_GUICtrlListView_AddColumn($hListView, "Process", 138)
_GUICtrlListView_AddColumn($hListView, "Module", 138)
_GUICtrlListView_SetColumnWidth($hListView, 3, $LVSCW_AUTOSIZE_USEHEADER)
GUISetState()

For $iID = 0 To $iCount
If IsHWnd($hToolbar) = False Then ExitLoop
$aRet = DllCall($iDLLUser32, "lparam", "SendMessageW", "hwnd", $hToolbar, _
   "int", $TB_GETBUTTON, "wparam", $iID, "lparam", $pAddress)
If @error Or $aRet[0] <> 1 Then ContinueLoop
$aRet = DllCall($iDLLKrnl32, "int", "ReadProcessMemory", "ptr", $hProcess, _
   "ptr", $pAddress, "ptr", $pTBBUTTON, "ulong_ptr", $iTBBUTTON, "ulong_ptr*", -1)
If @error Or $aRet[5] <> $iTBBUTTON Then ContinueLoop
;If BitAND(DllStructGetData($tTBBUTTON, "State"), $TBSTATE_HIDDEN) = $TBSTATE_HIDDEN Then ContinueLoop
$aRet = DllCall($iDLLKrnl32, "int", "ReadProcessMemory", "ptr", $hProcess, _
   "dword_ptr", DllStructGetData($tTBBUTTON, "Param"), "ptr", $pTRAYDATA, "ulong_ptr", $iTRAYDATA, "ulong_ptr*", -1)
If @error Or $aRet[5] <> $iTRAYDATA Then ContinueLoop
$hOwnerWin = DllStructGetData($tTRAYDATA, 1)
If @error Or $hOwnerWin = 0 Then ContinueLoop
$iPID = WinGetProcess($hOwnerWin)
If @error Or $iPID = -1 Then ContinueLoop
$hIconTray = DllStructGetData($tTRAYDATA, 5) ;returned icon handles shared
$sRet = ""
If Not _IsIconHandle($hIconTray, $sRet) Then
  ;ConsoleWrite('!VarGetType($hIconTray) = ' & VarGetType($hIconTray) & @CRLF)
  If IsPtr($hIconTray) Then $hIconTray = _WinAPI_Create32BitHICON($hIconTray)
  ;some icons like HDDLife's temp icons return an invalid icon handle, and won't display, but will when formatted by _WinAPI_Create32BitHICON
  If Not _IsIconHandle($hIconTray, $sRet) Then
   $hIconTray = _GetTrayIconHandle($hOwnerWin)
   ConsoleWrite("- Invalid Icon handle: OwnerWin: " & _WinAPI_GetClassName($hOwnerWin) &  "  Process Name: " & _ProcessGetName($iPID) & @crlf)
  EndIf
EndIf
;$hIconTray = _WinAPI_CopyImage($hIconTray, $IMAGE_ICON, 16, 16)
_GUIImageList_ReplaceIcon($hImage, -1, $hIconTray)
$hIcon = _WinAPI_ShellExtractAssociatedIcon(_WinAPI_GetProcessFileName($iPID), 1)
$iImg = _GUIImageList_ReplaceIcon($hImage, -1, $hIcon)
_GUICtrlListView_InsertItem($hListView, "", -1, $iImg-1)
_GUICtrlListView_AddSubItem($hListView, $iIdx, "", 1, $iImg)
_GUICtrlListView_AddSubItem($hListView, $iIdx, _ProcessGetName($iPID), 2)
If $sRet = "" Then $sRet = _WinAPI_GetProcessFileName($iPID)
_GUICtrlListView_AddSubItem($hListView, $iIdx, $sRet, 3)
$iIdx += 1
;ToolTip(_WinAPI_GetClassName($hOwnerWin), 1030, 850, $sRet, $hIconTray, 5)
;Sleep(500)
_WinAPI_DestroyIcon($hIcon)
_WinAPI_DestroyIcon($hIconTray) ;does not destroy traydata handles
Next

DllCall($iDLLKrnl32, "int", "VirtualFreeEx", "ptr", $hProcess, "ptr", $pAddress, "ulong_ptr", 0, "dword", $MEM_RELEASE)
DllCall($iDLLKrnl32, "int", "CloseHandle", "ptr", $hProcess)
DllClose($iDLLUser32)
DllClose($iDLLKrnl32)
ToolTip("")

While GUIGetMsg() <> -3
WEnd
_GUIImageList_Destroy($hImage)
Exit

Func _GetTrayIconHandle($hWnd, $hDLLUser32 = "User32.dll")
If Not IsHWnd($hWnd) Then Return SetError(1, 1, -1)
Local $iPID, $ParentPID, $aWinList
$hIcon = _GetIcon($hWnd, $hDLLUser32)
If Not @error And $hIcon <> -1 Then Return SetError(0, 0, $hIcon)
$ParentPID = WinGetProcess($hWnd)
If @error Or $ParentPID = -1 Then Return SetError(2, 2, -1)
$aWinList = WinList()
For $i = 1 to $aWinList[0][0]
  $iPID = WinGetProcess($aWinList[$i][1])
  If @error Or $iPID = -1 Then ContinueLoop
  If $iPID = $ParentPID Then
   $hIcon = _GetIcon($aWinList[$i][1], $hDLLUser32)
   If Not @error And $hIcon <> -1 Then Return SetError(0, 0, $hIcon)
  EndIf
Next
Return SetError(2, 2, -1)
EndFunc

;modified from ModernMenu Win2Tray - by Holger Kotsch
Func _GetIcon(ByRef $hWnd, $hDLLUser32 = "User32.dll")
If Not IsHWnd($hWnd) Then Return SetError(1, 1, -1)
Local Const $GCL_HICON = -14
Local Const $GCL_HICONSM = -34
Local $hIcon
$hIcon = _SendMessage($hWnd, $WM_GETICON, 2, 0)
If Not @error And _IsIconHandle($hIcon, $sRet) = True Then Return SetError(0, 0, Ptr($hIcon))
$hIcon = _SendMessage($hWnd, $WM_GETICON, 0, 0)
If Not @error And _IsIconHandle($hIcon, $sRet) = True Then Return SetError(0, 0, Ptr($hIcon))
$hIcon = _WinAPI_GetClassLongEx($hWnd, $GCL_HICONSM)
If Not @error And _IsIconHandle($hIcon, $sRet) = True Then Return SetError(0, 0, Ptr($hIcon))
$hIcon = _WinAPI_GetClassLongEx($hWnd, $GCL_HICON)
If Not @error And _IsIconHandle($hIcon, $sRet) = True Then Return SetError(0, 0, Ptr($hIcon))
Return SetError(2, 2, -1)
EndFunc

Func _IsIconHandle($hIcon, ByRef $sMod)
;rover 2k11
Local $aRet
Switch @OSVersion
  Case "WIN_2008R2", "WIN_7", "WIN_2008", "WIN_VISTA"
   $aRet = _WinAPI_GetIconInfoEx($hIcon)
   If @error Then Return SetError(1, 1, False)
   DllCall("gdi32.dll", "bool", "DeleteObject", "ptr", $aRet[3])
   DllCall("gdi32.dll", "bool", "DeleteObject", "ptr", $aRet[4])
   If $aRet[0] = 0 Then Return SetError(2, 2, False)
   $sMod = $aRet[6]
   Return SetError(0, 0, True)
  Case "WIN_2003", "WIN_XP"
   Local $tIconInfo = DllStructCreate("int fIcon;int xHotspot;int yHotspot;ptr hbmMask;ptr hbmColor")
   Local $aRet = DllCall("User32.dll", "int", "GetIconInfo", "ptr", $hIcon, "ptr", DllStructGetPtr($tIconInfo))
   If @error Or UBound($aRet) <> 3 Or $aRet[0] = 0 Then Return SetError(1, 1, False)
   DllCall("gdi32.dll", "bool", "DeleteObject", "ptr", DllStructGetData($tIconInfo, 4))
   DllCall("gdi32.dll", "bool", "DeleteObject", "ptr", DllStructGetData($tIconInfo, 5))
   If Not DllStructGetData($tIconInfo, 1) Then Return SetError(2, 2, False)
   Return SetError(0, 0, True)
EndSwitch
EndFunc
Zedna, KaFu, Mat and 1 other like this

Share this post


Link to post
Share on other sites

Posted (edited)

Wuuhhaaa ;)... exactly what I was looking for :)!

I saw the shared memory technics in the SysTray UDF, but was not sure if this can be used (and more importantly how :)) to fetch the underlying icons. Of course there are always programs acting differently then others, not returning a valid handle. For those I'll add a fallback the parent Window Icon.

Thanks a bunch for the great example!

Enjoy your Christmas season ;)...

Edit:

"For those I'll add a fallback the parent Window Icon."... Which you've already added ;), saw this one later.

Edit 2:

I just noticed that actually I've read that post on xtremevbtalk.com before on my search for a solution! I just couldn't make any working AutoIt translation of it :D ...

Edited by KaFu

Share this post


Link to post
Share on other sites

Posted

Hi Rover, can get it to work on Win7 :) . I'll keep on trying and will post when I suceed, but maybe you're seeing the issue quicker than I do. What I've already noticed is that the statement "Switch $iTbar" is always false as $iTbar is 0 in all cases.

Share this post


Link to post
Share on other sites

Posted

The excellent script from rover is running properly only on my Win7 x64 system when I run it as x64 code using #AutoIt3Wrapper_UseX64=y.

Otherwise the list is empy!

Br,

UEZ

Share this post


Link to post
Share on other sites

Posted (edited)

Now that's a good observation I hadn't thought about :). I'm also running on Win7-64 bit. On the other hand the _Systray UDF (first post) uses the same technique to access the explorer.exe data and it works fine running as 32bit. But maybe the problem is not related to the technique as such, but to the data to be extracted (structures wrong?).

Edited by KaFu

Share this post


Link to post
Share on other sites

Posted

The problem was related to x86 <> x64 structure definitions :). UEZ, thanks for pointing the direction!

Merry Christmas to everyone!

#NoTrayIcon
#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****
; http://www.autoitscript.com/forum/topic/134538-get-systray-icons-solved/page__view__findpost__p__948301
; coded by rover 2k11

#include <..WinAPIEx_3.5SMF_Func_WinAPIEx__v3.5_Beta.au3>
#include <WinAPI.au3>
#include <Process.au3>
#include <GuiListView.au3>
#include <GuiImageList.au3>
#include <Constants.au3>
#include <..WinAPIEx_3.5SMF_Func_WinAPIEx__v3.5_Beta_APIConstants.au3>
#include <WindowsConstants.au3>
#include <ToolBarConstants.au3>
#include <ProcessConstants.au3>
#include <MemoryConstants.au3>

Opt("MustDeclareVars", 1)

Global Const $PROCESS_ACCESS = BitOR($PROCESS_VM_OPERATION, $PROCESS_VM_READ)
Global $hOwnerWin, $hTrayWnd, $hTrayNotifyWnd, $hSysPager, $hToolbar, _
        $iDLLUser32 = DllOpen("user32.dll"), $iDLLKrnl32 = DllOpen("kernel32.dll"), _
        $tTBBUTTON, $pTBBUTTON, $iTBBUTTON, $tTRAYDATA, $pTRAYDATA, $iTRAYDATA, _
        $iImg = 0, $iIdx = 0, $iCount, $iPIDExp, $hProcess, $pAddress, $iPID, $aRet, $sRet, $hIconTray, $hIcon

$hTrayWnd = WinGetHandle("[CLASS:Shell_TrayWnd]")
$hTrayNotifyWnd = ControlGetHandle($hTrayWnd, "", "[CLASS:TrayNotifyWnd]")
$hSysPager = ControlGetHandle($hTrayNotifyWnd, "", "[CLASS:SysPager]")
$hToolbar = ControlGetHandle($hSysPager, "", "[CLASS:ToolbarWindow32; INSTANCE:1]") ; User Promoted Notification Area/Notification Area/SysTray

Global $taglocalTBBUTTON
If @OSArch = "X86" Then
    $taglocalTBBUTTON = "int iBitmap;int idCommand;byte fsState;byte fsStyle;byte bReserved[2];dword_ptr dwData;int_ptr iString"
Else ; X64
    $taglocalTBBUTTON = "int iBitmap;int idCommand;byte fsState;byte fsStyle;byte bReserved[6];dword_ptr dwData;int_ptr iString"
EndIf
$tTBBUTTON = DllStructCreate($taglocalTBBUTTON)
$pTBBUTTON = DllStructGetPtr($tTBBUTTON)
$iTBBUTTON = DllStructGetSize($tTBBUTTON)

If @OSArch = "X86" Then
    $tTRAYDATA = DllStructCreate("hwnd hwnd;uint uID;uint uCallbackMessage;dword Reserved[2];hwnd hIcon")
Else
    $tTRAYDATA = DllStructCreate("int64 hwnd;uint uID;uint uCallbackMessage;dword Reserved[2];int64 hIcon")
EndIf

$pTRAYDATA = DllStructGetPtr($tTRAYDATA)
$iTRAYDATA = DllStructGetSize($tTRAYDATA)
ConsoleWrite("$iTRAYDATA " & $iTRAYDATA & @CRLF)
$iPIDExp = WinGetProcess($hToolbar)
If @error Or $iPIDExp = -1 Then Exit
$aRet = DllCall($iDLLKrnl32, "ptr", "OpenProcess", "dword", _
        $PROCESS_ACCESS, "int", 0, "int", $iPIDExp)
If @error Or $aRet[0] = 0 Then Exit
$hProcess = $aRet[0]
$aRet = DllCall($iDLLUser32, "lparam", "SendMessageW", "hwnd", _
        $hToolbar, "int", $TB_BUTTONCOUNT, "wparam", 0, "lparam", 0)
If @error Or $aRet[0] < 1 Then
    DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hProcess)
    Exit
EndIf
$iCount = $aRet[0] - 1
$aRet = DllCall($iDLLKrnl32, "ptr", "VirtualAllocEx", "ptr", $hProcess, "ptr", 0, "ulong_ptr", _
        $iTBBUTTON, "dword", BitOR($MEM_RESERVE, $MEM_COMMIT), "dword", $PAGE_READWRITE)
If @error Or $aRet[0] = 0 Then
    DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hProcess)
    Exit
EndIf
$pAddress = $aRet[0]

GUICreate("Notification Icons", 600, 600)
GUISetBkColor(0x494949)
Global $hListView = GUICtrlCreateListView("", 2, 2, 596, 596)
_GUICtrlListView_SetExtendedListViewStyle($hListView, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_SUBITEMIMAGES))
Global $hImage = _GUIImageList_Create(16, 16, 5, 5)
_GUICtrlListView_SetImageList($hListView, $hImage, 1)
_GUICtrlListView_AddColumn($hListView, "Tray", 38)
_GUICtrlListView_AddColumn($hListView, "Proc", 38)
_GUICtrlListView_AddColumn($hListView, "Process", 138)
_GUICtrlListView_AddColumn($hListView, "Module", 138)
_GUICtrlListView_SetColumnWidth($hListView, 3, $LVSCW_AUTOSIZE_USEHEADER)
GUISetState()

For $iID = 0 To $iCount
    If IsHWnd($hToolbar) = False Then ExitLoop
    $aRet = DllCall($iDLLUser32, "lparam", "SendMessageW", "hwnd", $hToolbar, _
            "int", $TB_GETBUTTON, "wparam", $iID, "lparam", $pAddress)
    If @error Or $aRet[0] <> 1 Then ContinueLoop
    ConsoleWrite("Step 1" & @CRLF)

    $aRet = DllCall($iDLLKrnl32, "int", "ReadProcessMemory", "ptr", $hProcess, _
            "ptr", $pAddress, "ptr", $pTBBUTTON, "ulong", $iTBBUTTON, "ulong*", -1)
    If @error Or $aRet[5] <> $iTBBUTTON Then ContinueLoop
    ConsoleWrite("Step 2 " & $iTBBUTTON & @TAB & $aRet[5] & @CRLF)
    ;If BitAND(DllStructGetData($tTBBUTTON, "State"), $TBSTATE_HIDDEN) = $TBSTATE_HIDDEN Then ContinueLoop
    ConsoleWrite($hProcess & @tab & ptr(DllStructGetData($tTBBUTTON, "Param")) & @tab & $pTRAYDATA & @tab & $iTRAYDATA & @crlf)
    $aRet = DllCall($iDLLKrnl32, "int", "ReadProcessMemory", "ptr", $hProcess, _
            "ptr", ptr(DllStructGetData($tTBBUTTON, 6)), "ptr", $pTRAYDATA, "ulong", $iTRAYDATA, "ulong*", 0)
    ConsoleWrite("Step 3 " & $aRet[5] & @tab & $iTRAYDATA &  @CRLF)
    If @error Or $aRet[5] <> $iTRAYDATA Then ContinueLoop
    $hOwnerWin = DllStructGetData($tTRAYDATA, 1)
    If @error Or $hOwnerWin = 0 Then ContinueLoop
    ConsoleWrite("Step 4 " & $hOwnerWin & @CRLF)
    $iPID = WinGetProcess(HWnd($hOwnerWin))
    If @error Or $iPID = -1 Then ContinueLoop
    ConsoleWrite("Step 5" & @CRLF)
    $hIconTray = DllStructGetData($tTRAYDATA, 5) ;returned icon handles shared
    $sRet = ""
    If Not _IsIconHandle($hIconTray, $sRet) Then
        ;ConsoleWrite('!VarGetType($hIconTray) = ' & VarGetType($hIconTray) & @CRLF)
        If IsPtr($hIconTray) Then $hIconTray = _WinAPI_Create32BitHICON($hIconTray)
        ;some icons like HDDLife's temp icons return an invalid icon handle, and won't display, but will when formatted by _WinAPI_Create32BitHICON
        If Not _IsIconHandle($hIconTray, $sRet) Then
            $hIconTray = _GetTrayIconHandle($hOwnerWin)
            ConsoleWrite("- Invalid Icon handle: OwnerWin: " & _WinAPI_GetClassName($hOwnerWin) & "  Process Name: " & _ProcessGetName($iPID) & @CRLF)
        EndIf
    EndIf
    ;$hIconTray = _WinAPI_CopyImage($hIconTray, $IMAGE_ICON, 16, 16)
    _GUIImageList_ReplaceIcon($hImage, -1, $hIconTray)
    $hIcon = _WinAPI_ShellExtractAssociatedIcon(_WinAPI_GetProcessFileName($iPID), 1)
    $iImg = _GUIImageList_ReplaceIcon($hImage, -1, $hIcon)
    _GUICtrlListView_InsertItem($hListView, "", -1, $iImg - 1)
    _GUICtrlListView_AddSubItem($hListView, $iIdx, "", 1, $iImg)
    _GUICtrlListView_AddSubItem($hListView, $iIdx, _ProcessGetName($iPID), 2)
    If $sRet = "" Then $sRet = _WinAPI_GetProcessFileName($iPID)
    _GUICtrlListView_AddSubItem($hListView, $iIdx, $sRet, 3)
    $iIdx += 1
    ;ToolTip(_WinAPI_GetClassName($hOwnerWin), 1030, 850, $sRet, $hIconTray, 5)
    ;Sleep(500)
    _WinAPI_DestroyIcon($hIcon)
    _WinAPI_DestroyIcon($hIconTray) ;does not destroy traydata handles
Next

DllCall($iDLLKrnl32, "int", "VirtualFreeEx", "ptr", $hProcess, "ptr", $pAddress, "ulong_ptr", 0, "dword", $MEM_RELEASE)
DllCall($iDLLKrnl32, "int", "CloseHandle", "ptr", $hProcess)
DllClose($iDLLUser32)
DllClose($iDLLKrnl32)
ToolTip("")

While GUIGetMsg() <> -3
WEnd
_GUIImageList_Destroy($hImage)
Exit

Func _GetTrayIconHandle($hWnd, $hDLLUser32 = "User32.dll")
    If Not IsHWnd($hWnd) Then Return SetError(1, 1, -1)
    Local $iPID, $ParentPID, $aWinList
    $hIcon = _GetIcon($hWnd, $hDLLUser32)
    If Not @error And $hIcon <> -1 Then Return SetError(0, 0, $hIcon)
    $ParentPID = WinGetProcess($hWnd)
    If @error Or $ParentPID = -1 Then Return SetError(2, 2, -1)
    $aWinList = WinList()
    For $i = 1 To $aWinList[0][0]
        $iPID = WinGetProcess($aWinList[$i][1])
        If @error Or $iPID = -1 Then ContinueLoop
        If $iPID = $ParentPID Then
            $hIcon = _GetIcon($aWinList[$i][1], $hDLLUser32)
            If Not @error And $hIcon <> -1 Then Return SetError(0, 0, $hIcon)
        EndIf
    Next
    Return SetError(2, 2, -1)
EndFunc   ;==>_GetTrayIconHandle

;modified from ModernMenu Win2Tray - by Holger Kotsch
Func _GetIcon(ByRef $hWnd, $hDLLUser32 = "User32.dll")
    If Not IsHWnd($hWnd) Then Return SetError(1, 1, -1)
    Local Const $GCL_HICON = -14
    Local Const $GCL_HICONSM = -34
    Local $hIcon
    $hIcon = _SendMessage($hWnd, $WM_GETICON, 2, 0)
    If Not @error And _IsIconHandle($hIcon, $sRet) = True Then Return SetError(0, 0, Ptr($hIcon))
    $hIcon = _SendMessage($hWnd, $WM_GETICON, 0, 0)
    If Not @error And _IsIconHandle($hIcon, $sRet) = True Then Return SetError(0, 0, Ptr($hIcon))
    $hIcon = _WinAPI_GetClassLongEx($hWnd, $GCL_HICONSM)
    If Not @error And _IsIconHandle($hIcon, $sRet) = True Then Return SetError(0, 0, Ptr($hIcon))
    $hIcon = _WinAPI_GetClassLongEx($hWnd, $GCL_HICON)
    If Not @error And _IsIconHandle($hIcon, $sRet) = True Then Return SetError(0, 0, Ptr($hIcon))
    Return SetError(2, 2, -1)
EndFunc   ;==>_GetIcon

Func _IsIconHandle($hIcon, ByRef $sMod)
    ;rover 2k11
    Local $aRet
    Switch @OSVersion
        Case "WIN_2008R2", "WIN_7", "WIN_2008", "WIN_VISTA"
            $aRet = _WinAPI_GetIconInfoEx($hIcon)
            If @error Then Return SetError(1, 1, False)
            DllCall("gdi32.dll", "bool", "DeleteObject", "ptr", $aRet[3])
            DllCall("gdi32.dll", "bool", "DeleteObject", "ptr", $aRet[4])
            If $aRet[0] = 0 Then Return SetError(2, 2, False)
            $sMod = $aRet[6]
            Return SetError(0, 0, True)
        Case "WIN_2003", "WIN_XP"
            Local $tIconInfo = DllStructCreate("int fIcon;int xHotspot;int yHotspot;ptr hbmMask;ptr hbmColor")
            Local $aRet = DllCall("User32.dll", "int", "GetIconInfo", "ptr", $hIcon, "ptr", DllStructGetPtr($tIconInfo))
            If @error Or UBound($aRet) <> 3 Or $aRet[0] = 0 Then Return SetError(1, 1, False)
            DllCall("gdi32.dll", "bool", "DeleteObject", "ptr", DllStructGetData($tIconInfo, 4))
            DllCall("gdi32.dll", "bool", "DeleteObject", "ptr", DllStructGetData($tIconInfo, 5))
            If Not DllStructGetData($tIconInfo, 1) Then Return SetError(2, 2, False)
            Return SetError(0, 0, True)
    EndSwitch
EndFunc   ;==>_IsIconHandle

Share this post


Link to post
Share on other sites

Posted

Hi guys

My 64 bit mobo died, then I was away over the holidays,

so no testing for some time until i replaced it.

I changed the struct code to what KaFu posted, but it didn't work at first until I copied the entire post and saw that you convert the 64bit pointers (int64)

from the struct to 32bit with Hwnd() and Ptr() because they would otherwise be 32bit in a 32 bit process with ptr/hwnd and the explorer memory is 64bit. :)

nice fix guys

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  
Followers 0