Skip to content

Instantly share code, notes, and snippets.

@Otiel
Last active August 1, 2022 16:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Otiel/24248595ac4b30366a2228a7ac5ec964 to your computer and use it in GitHub Desktop.
Save Otiel/24248595ac4b30366a2228a7ac5ec964 to your computer and use it in GitHub Desktop.
AutoHotkey script to show or hide Slack desktop app
#Include TrayIcon.ahk
#s::
; Win S
; Show/hide Slack
DetectHiddenWindows, Off
Process, Exist, slack.exe
slackPid = %ErrorLevel%
IfWinNotExist, ahk_pid %slackPid%
{
TrayIcon_Button("slack.exe")
}
Else
{
IfWinActive, ahk_pid %slackPid%
{
WinClose, ahk_pid %slackPid%
}
Else
{
WinActivate, ahk_pid %slackPid%
}
}
return
; ----------------------------------------------------------------------------------------------------------------------
; Name ..........: TrayIcon library
; Description ...: Provide some useful functions to deal with Tray icons.
; AHK Version ...: AHK_L 1.1.22.02 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 Authors ...: Fanatic Guru, RiseUp
; License .......: WTFPL - http://www.wtfpl.net/txt/copying/
; Version Date...: 2017 - 11 - 24
; 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.
; Update 20160120: Went through all the data types in the DLL and NumGet and matched them up to MSDN which fixed IDcmd.
; Update 20160308: Fix for Windows 10 NotifyIconOverflowWindow
; Update 20171124: (RiseUp) Added extra Shell_TrayWnd to account for odd numbering (N) in ToolbarWindow32N
; ----------------------------------------------------------------------------------------------------------------------
; ----------------------------------------------------------------------------------------------------------------------
; 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].IDcmd - 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 := "")
{
DetectHiddenWindows, % (Setting_A_DetectHiddenWindows := A_DetectHiddenWindows) ? "On" :
oTrayIcon_GetInfo := {}
For key, sTray in ["NotifyIconOverflowWindow", "Shell_TrayWnd", "Shell_TrayWnd"]
{
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, UPtr, 20, UInt, 0x1000, UInt, 0x4)
if (SubStr(A_OSVersion,1,2)=10)
SendMessage, 0x418, 0, 0, ToolbarWindow32%key%, ahk_class %sTray% ; TB_BUTTONCOUNT
else
SendMessage, 0x418, 0, 0, ToolbarWindow32%idxTB%, ahk_class %sTray% ; TB_BUTTONCOUNT
szBtn := VarSetCapacity(btn, (A_Is64bitOS ? 32 : 20), 0)
szNfo := VarSetCapacity(nfo, (A_Is64bitOS ? 32 : 24), 0)
szTip := VarSetCapacity(tip, 128 * 2, 0)
Loop, %ErrorLevel%
{
if (SubStr(A_OSVersion,1,2)=10)
SendMessage, 0x417, A_Index - 1, pRB, ToolbarWindow32%key%, ahk_class %sTray% ; TB_GETBUTTON
else
SendMessage, 0x417, A_Index - 1, pRB, ToolbarWindow32%idxTB%, ahk_class %sTray% ; TB_GETBUTTON
DllCall("ReadProcessMemory", Ptr, hProc, Ptr, pRB, Ptr, &btn, UPtr, szBtn, UPtr, 0)
iBitmap := NumGet(btn, 0, "Int")
IDcmd := NumGet(btn, 4, "Int")
statyle := NumGet(btn, 8)
dwData := NumGet(btn, (A_Is64bitOS ? 16 : 12))
iString := NumGet(btn, (A_Is64bitOS ? 24 : 16), "Ptr")
DllCall("ReadProcessMemory", Ptr, hProc, Ptr, dwData, Ptr, &nfo, UPtr, szNfo, UPtr, 0)
hWnd := NumGet(nfo, 0, "Ptr")
uID := NumGet(nfo, (A_Is64bitOS ? 8 : 4), "UInt")
msgID := NumGet(nfo, (A_Is64bitOS ? 12 : 8))
hIcon := NumGet(nfo, (A_Is64bitOS ? 24 : 20), "Ptr")
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, UPtr, szTip, UPtr, 0)
Index := (oTrayIcon_GetInfo.MaxIndex()>0 ? oTrayIcon_GetInfo.MaxIndex()+1 : 1)
oTrayIcon_GetInfo[Index,"idx"] := A_Index - 1
oTrayIcon_GetInfo[Index,"IDcmd"] := IDcmd
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", Ptr, hProc, Ptr, pProc, UPtr, 0, Uint, 0x8000)
DllCall("CloseHandle", Ptr, hProc)
}
DetectHiddenWindows, %Setting_A_DetectHiddenWindows%
Return oTrayIcon_GetInfo
}
; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Hide
; Description ..: Hide or unhide a tray icon.
; Parameters ...: IDcmd - 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(IDcmd, sTray := "Shell_TrayWnd", bHide:=True)
{
(sTray == 0 ? sTray := "NotifyIconOverflowWindow" : sTray == 1 ? sTray := "Shell_TrayWnd" : )
DetectHiddenWindows, % (Setting_A_DetectHiddenWindows := A_DetectHiddenWindows) ? "On" :
idxTB := TrayIcon_GetTrayBar()
SendMessage, 0x404, IDcmd, 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" : )
DetectHiddenWindows, % (Setting_A_DetectHiddenWindows := A_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" : )
DetectHiddenWindows, % (Setting_A_DetectHiddenWindows := A_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()
{
DetectHiddenWindows, % (Setting_A_DetectHiddenWindows := A_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)
{
DetectHiddenWindows, % (Setting_A_DetectHiddenWindows := A_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
}
@ryanbuening
Copy link

Works great! Thanks!

@raveren
Copy link

raveren commented Aug 1, 2022

Works great, but I didn't like the usage of the needless library - this is just as fast and no dependencies:

; WIN+S - show/hide Slack
#s::
    Process, Exist, slack.exe
    slackPid = %ErrorLevel%

    IfWinNotExist, ahk_pid %slackPid%
    {
        Run, %A_AppData%\..\Local\slack\slack.exe
    }
    else
    {
        IfWinActive, ahk_pid %slackPid%
        {
            WinClose, ahk_pid %slackPid%
        } else {
            WinActivate, ahk_pid %slackPid%
        }
    }
return

@Otiel
Copy link
Author

Otiel commented Aug 1, 2022

    Run, %A_AppData%\..\Local\slack\slack.exe

Running the process is slower than clicking on the systray icon.

@raveren
Copy link

raveren commented Aug 1, 2022

Hardly, did you take a look at what the 'click the systray icon' script does? It's a HUGE amount of processing, building array of all processes, using process hooks and dll calls...

Anyway, it's instantaneous on my machine.

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