Skip to content

Instantly share code, notes, and snippets.

@tmplinshi
Created July 3, 2017 05:07
Show Gist options
  • Save tmplinshi/83c52a9dffe65c105803d026ca1a07da to your computer and use it in GitHub Desktop.
Save tmplinshi/83c52a9dffe65c105803d026ca1a07da to your computer and use it in GitHub Desktop.
; ----------------------------------------------------------------------------------------------------------------------
; Name ..........: TrayIcon library
; Description ...: Provide some useful functions to deal with Tray icons.
; AHK Version ...: AHK_L 1.1.13.01 x32/64 Unicode
; Original Author: Sean (http://goo.gl/dh0xIX) (http://www.autohotkey.com/forum/viewtopic.php?t=17314)
; Update Author .: Cyruz (http://ciroprincipe.info) (http://ahkscript.org/boards/viewtopic.php?f=6&t=1229)
; Mod Author ....: Fanatic Guru
; License .......: WTFPL - http://www.wtfpl.net/txt/copying/
; Version Date...: 2014 - 01 - 16
; Note ..........: Many people have updated Sean's original work including me but Cyruz's version seemed the most straight
; ...............: forward update for 64 bit so I adapted it with some of the features from my Fanatic Guru version.
; ----------------------------------------------------------------------------------------------------------------------
; ----------------------------------------------------------------------------------------------------------------------
; Function ......: TrayIcon_GetInfo
; Description ...: Get a series of useful information about tray icons.
; Parameters ....: sExeName - The exe for which we are searching the tray icon data. Leave it empty to receive data for
; ...............: all tray icons.
; Return ........: oTrayIcon_GetInfo - An array of objects containing tray icons data. Any entry is structured like this:
; ...............: oTrayIcon_GetInfo[A_Index].idx - 0 based tray icon index.
; ...............: oTrayIcon_GetInfo[A_Index].cmdID - Command identifier associated with the button.
; ...............: oTrayIcon_GetInfo[A_Index].pID - Process ID.
; ...............: oTrayIcon_GetInfo[A_Index].uID - Application defined identifier for the icon.
; ...............: oTrayIcon_GetInfo[A_Index].msgID - Application defined callback message.
; ...............: oTrayIcon_GetInfo[A_Index].hIcon - Handle to the tray icon.
; ...............: oTrayIcon_GetInfo[A_Index].hWnd - Window handle.
; ...............: oTrayIcon_GetInfo[A_Index].Class - Window class.
; ...............: oTrayIcon_GetInfo[A_Index].Process - Process executable.
; ...............: oTrayIcon_GetInfo[A_Index].Tray - Tray Type (Shell_TrayWnd or NotifyIconOverflowWindow).
; ...............: oTrayIcon_GetInfo[A_Index].tooltip - Tray icon tooltip.
; Info ..........: TB_BUTTONCOUNT message - http://goo.gl/DVxpsg
; ...............: TB_GETBUTTON message - http://goo.gl/2oiOsl
; ...............: TBBUTTON structure - http://goo.gl/EIE21Z
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_GetInfo(sExeName := "")
{
Setting_A_DetectHiddenWindows := A_DetectHiddenWindows
DetectHiddenWindows, On
oTrayIcon_GetInfo := {}
For key, sTray in ["Shell_TrayWnd","NotifyIconOverflowWindow"]
{
idxTB := TrayIcon_GetTrayBar()
WinGet, pidTaskbar, PID, ahk_class %sTray%
hProc := DllCall("OpenProcess", UInt, 0x38, Int, 0, UInt, pidTaskbar)
pRB := DllCall("VirtualAllocEx", Ptr, hProc, Ptr, 0, UInt, 20, UInt, 0x1000, UInt, 0x4)
SendMessage, 0x418, 0, 0, ToolbarWindow32%idxTB%, ahk_class %sTray% ; TB_BUTTONCOUNT
szBtn := VarSetCapacity(btn, (A_Is64bitOS ? 32 : 24))
szNfo := VarSetCapacity(nfo, (A_Is64bitOS ? 32 : 24))
szTip := VarSetCapacity(tip, 128 * 2)
Loop, %ErrorLevel%
{
SendMessage, 0x417, A_Index - 1, pRB, ToolbarWindow32%idxTB%, ahk_class %sTray% ; TB_GETBUTTON
DllCall("ReadProcessMemory", Ptr, hProc, Ptr, pRB, Ptr, &btn, UInt, szBtn, UInt, 0)
iBitmap := NumGet(btn, 0)
cmdID := NumGet(btn, 4)
statyle := NumGet(btn, 8)
dwData := NumGet(btn, (A_Is64bitOS ? 16 : 12))
iString := NumGet(btn, (A_Is64bitOS ? 24 : 16))
DllCall("ReadProcessMemory", Ptr, hProc, Ptr, dwData, Ptr, &nfo, UInt, szNfo, UInt, 0)
hWnd := NumGet(nfo, 0)
uID := NumGet(nfo, (A_Is64bitOS ? 8 : 4))
msgID := NumGet(nfo, (A_Is64bitOS ? 12 : 8))
hIcon := NumGet(nfo, (A_Is64bitOS ? 24 : 20))
WinGet, pID, PID, ahk_id %hWnd%
WinGet, sProcess, ProcessName, ahk_id %hWnd%
WinGetClass, sClass, ahk_id %hWnd%
If !sExeName || (sExeName = sProcess) || (sExeName = pID)
{
DllCall("ReadProcessMemory", Ptr, hProc, Ptr, iString, Ptr, &tip, UInt, szTip, UInt, 0)
Index := (oTrayIcon_GetInfo.MaxIndex()>0 ? oTrayIcon_GetInfo.MaxIndex()+1 : 1)
oTrayIcon_GetInfo[Index,"idx"] := A_Index - 1
oTrayIcon_GetInfo[Index,"cmdID"] := cmdID
oTrayIcon_GetInfo[Index,"pID"] := pID
oTrayIcon_GetInfo[Index,"uID"] := uID
oTrayIcon_GetInfo[Index,"msgID"] := msgID
oTrayIcon_GetInfo[Index,"hIcon"] := hIcon
oTrayIcon_GetInfo[Index,"hWnd"] := hWnd
oTrayIcon_GetInfo[Index,"Class"] := sClass
oTrayIcon_GetInfo[Index,"Process"] := sProcess
oTrayIcon_GetInfo[Index,"Tooltip"] := StrGet(&tip, "UTF-16")
oTrayIcon_GetInfo[Index,"Tray"] := sTray
}
}
DllCall("VirtualFreeEx", "Uint", hProc, "Uint", pProc, "Uint", 0, "Uint", 0x8000)
DllCall("CloseHandle", "Uint", hProc)
}
DetectHiddenWindows, %Setting_A_DetectHiddenWindows%
Return oTrayIcon_GetInfo
}
; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Hide
; Description ..: Hide or unhide a tray icon.
; Parameters ...: cmdID - Command identifier associated with the button.
; ..............: bHide - True for hide, False for unhide.
; ..............: sTray - 1 or Shell_TrayWnd || 0 or NotifyIconOverflowWindow.
; Info .........: TB_HIDEBUTTON message - http://goo.gl/oelsAa
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Hide(cmdID, sTray := "Shell_TrayWnd", bHide:=True)
{
(sTray == 0 ? sTray := "NotifyIconOverflowWindow" : sTray == 1 ? sTray := "Shell_TrayWnd" : )
Setting_A_DetectHiddenWindows := A_DetectHiddenWindows
DetectHiddenWindows, On
idxTB := TrayIcon_GetTrayBar()
SendMessage, 0x404, cmdID, bHide, ToolbarWindow32%idxTB%, ahk_class %sTray% ; TB_HIDEBUTTON
SendMessage, 0x1A, 0, 0, , ahk_class %sTray%
DetectHiddenWindows, %Setting_A_DetectHiddenWindows%
}
; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Delete
; Description ..: Delete a tray icon.
; Parameters ...: idx - 0 based tray icon index.
; ..............: sTray - 1 or Shell_TrayWnd || 0 or NotifyIconOverflowWindow.
; Info .........: TB_DELETEBUTTON message - http://goo.gl/L0pY4R
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Delete(idx, sTray := "Shell_TrayWnd")
{
(sTray == 0 ? sTray := "NotifyIconOverflowWindow" : sTray == 1 ? sTray := "Shell_TrayWnd" : )
Setting_A_DetectHiddenWindows := A_DetectHiddenWindows
DetectHiddenWindows, On
idxTB := TrayIcon_GetTrayBar()
SendMessage, 0x416, idx, 0, ToolbarWindow32%idxTB%, ahk_class %sTray% ; TB_DELETEBUTTON
SendMessage, 0x1A, 0, 0, , ahk_class %sTray%
DetectHiddenWindows, %Setting_A_DetectHiddenWindows%
}
; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Remove
; Description ..: Remove a tray icon.
; Parameters ...: hWnd, uID.
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Remove(hWnd, uID)
{
NumPut(VarSetCapacity(NID,(A_IsUnicode ? 2 : 1) * 384 + A_PtrSize * 5 + 40,0), NID)
NumPut(hWnd , NID, (A_PtrSize == 4 ? 4 : 8 ))
NumPut(uID , NID, (A_PtrSize == 4 ? 8 : 16 ))
Return DllCall("shell32\Shell_NotifyIcon", "Uint", 0x2, "Uint", &NID)
}
; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Move
; Description ..: Move a tray icon.
; Parameters ...: idxOld - 0 based index of the tray icon to move.
; ..............: idxNew - 0 based index where to move the tray icon.
; ..............: sTray - 1 or Shell_TrayWnd || 0 or NotifyIconOverflowWindow.
; Info .........: TB_MOVEBUTTON message - http://goo.gl/1F6wPw
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Move(idxOld, idxNew, sTray := "Shell_TrayWnd")
{
(sTray == 0 ? sTray := "NotifyIconOverflowWindow" : sTray == 1 ? sTray := "Shell_TrayWnd" : )
Setting_A_DetectHiddenWindows := A_DetectHiddenWindows
DetectHiddenWindows, On
idxTB := TrayIcon_GetTrayBar()
SendMessage, 0x452, idxOld, idxNew, ToolbarWindow32%idxTB%, ahk_class %sTray% ; TB_MOVEBUTTON
DetectHiddenWindows, %Setting_A_DetectHiddenWindows%
}
; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_GetTrayBar
; Description ..: Get the tray icon handle.
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_GetTrayBar()
{
Setting_A_DetectHiddenWindows := A_DetectHiddenWindows
DetectHiddenWindows, On
WinGet, ControlList, ControlList, ahk_class Shell_TrayWnd
RegExMatch(ControlList, "(?<=ToolbarWindow32)\d+(?!.*ToolbarWindow32)", nTB)
Loop, %nTB%
{
ControlGet, hWnd, hWnd,, ToolbarWindow32%A_Index%, ahk_class Shell_TrayWnd
hParent := DllCall( "GetParent", Ptr, hWnd )
WinGetClass, sClass, ahk_id %hParent%
If (sClass <> "SysPager")
Continue
idxTB := A_Index
Break
}
DetectHiddenWindows, %Setting_A_DetectHiddenWindows%
Return idxTB
}
; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_GetHotItem
; Description ..: Get the index of tray's hot item.
; Info .........: TB_GETHOTITEM message - http://goo.gl/g70qO2
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_GetHotItem()
{
idxTB := TrayIcon_GetTrayBar()
SendMessage, 0x447, 0, 0, ToolbarWindow32%idxTB%, ahk_class Shell_TrayWnd ; TB_GETHOTITEM
Return ErrorLevel << 32 >> 32
}
; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Button
; Description ..: Simulate mouse button click on a tray icon.
; Parameters ...: sExeName - Executable Process Name of tray icon.
; ..............: sButton - Mouse button to simulate (L, M, R).
; ..............: bDouble - True to double click, false to single click.
; ..............: index - Index of tray icon to click if more than one match.
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Button(sExeName, sButton := "L", bDouble := false, index := 1)
{
Setting_A_DetectHiddenWindows := A_DetectHiddenWindows
DetectHiddenWindows, On
WM_MOUSEMOVE = 0x0200
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
WM_LBUTTONDBLCLK = 0x0203
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP = 0x0205
WM_RBUTTONDBLCLK = 0x0206
WM_MBUTTONDOWN = 0x0207
WM_MBUTTONUP = 0x0208
WM_MBUTTONDBLCLK = 0x0209
sButton := "WM_" sButton "BUTTON"
oIcons := {}
oIcons := TrayIcon_GetInfo(sExeName)
msgID := oIcons[index].msgID
uID := oIcons[index].uID
hWnd := oIcons[index].hWnd
if bDouble
PostMessage, msgID, uID, %sButton%DBLCLK, , ahk_id %hWnd%
else
{
PostMessage, msgID, uID, %sButton%DOWN, , ahk_id %hWnd%
PostMessage, msgID, uID, %sButton%UP, , ahk_id %hWnd%
}
DetectHiddenWindows, %Setting_A_DetectHiddenWindows%
return
}
@Otiel
Copy link

Otiel commented Jan 30, 2020

The latest version and original topic is available on the AutoHotkey forums.

@TaroDumpling
Copy link

TaroDumpling commented Jun 12, 2023

@tmplinshi I'm new to autohotkey, would you mind to give me an example of how to use TrayIcon_GetInfo(sExeName := "") to find out the cmdID? And after I find that, do I just modify the cmdID in this code TrayIcon_Hide(cmdID, sTray := "Shell_TrayWnd", bHide:=True)? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment