[LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post your working scripts, libraries and tools for AHK v1.1 and older
quaritexa
Posts: 32
Joined: 09 Nov 2017, 21:03

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by quaritexa » 10 Aug 2021, 08:08

Please, fix the %sTrayPlace% in the TrayIcon_Move and TrayIcon_Delete.

quaritexa
Posts: 32
Joined: 09 Nov 2017, 21:03

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by quaritexa » 10 Aug 2021, 08:37

I ported it to the ahk v2:

Code: Select all

; ported to AHK v2.0-beta by @krasnovpro
; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=1229

; ----------------------------------------------------------------------------------------------------------------------
; Name ..........: TrayIcon library
; Description ...: Provide some useful functions to deal with Tray icons.
; AHK Version ...: AHK_L 1.1.22.02 x32/64 Unicode
; Code from .....: Sean (http://www.autohotkey.com/forum/viewtopic.php?t=17314)
; Author ........: Cyruz (http://ciroprincipe.info) (http://ahkscript.org/boards/viewtopic.php?f=6&t=1229)
; Mod from ......: Fanatic Guru - Cyruz
; License .......: WTFPL - http://www.wtfpl.net/txt/copying/
; Version Date ..: 2019.03.12
; Upd.20160120 ..: Fanatic Guru - Went through all the data types in the DLL and NumGet and matched them up to MSDN
; ...............:                which fixed idCmd.
; Upd.20160308 ..: Fanatic Guru - Fix for Windows 10 NotifyIconOverflowWindow.
; Upd.20180313 ..: Fanatic Guru - Fix problem with "VirtualFreeEx" pointed out by nnnik.
; Upd.20180313 ..: Fanatic Guru - Additional fix for previous Windows 10 NotifyIconOverflowWindow fix breaking non
; ...............:                hidden icons.
; Upd.20190312 ..: Cyruz        - Added TrayIcon_Set, code merged and refactored.
; ----------------------------------------------------------------------------------------------------------------------

; ----------------------------------------------------------------------------------------------------------------------
; 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 ........: oTrayInfo - An array of objects containing tray icons data. Any entry is structured like this:
; ...............:             oTrayInfo[A_Index].idx     - 0 based tray icon index.
; ...............:             oTrayInfo[A_Index].idcmd   - Command identifier associated with the button.
; ...............:             oTrayInfo[A_Index].pid     - Process ID.
; ...............:             oTrayInfo[A_Index].uid     - Application defined identifier for the icon.
; ...............:             oTrayInfo[A_Index].msgid   - Application defined callback message.
; ...............:             oTrayInfo[A_Index].hicon   - Handle to the tray icon.
; ...............:             oTrayInfo[A_Index].hwnd    - Window handle.
; ...............:             oTrayInfo[A_Index].class   - Window class.
; ...............:             oTrayInfo[A_Index].process - Process executable.
; ...............:             oTrayInfo[A_Index].tray    - Tray Type (Shell_TrayWnd or NotifyIconOverflowWindow).
; ...............:             oTrayInfo[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 := "") {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows(1)

  oTrayInfo    := []
  for sTray in ["Shell_TrayWnd", "NotifyIconOverflowWindow"] {
    idxTB      := TrayIcon_GetTrayBar(sTray)
    pidTaskbar := WinGetPID("ahk_class " sTray)

    hProc      := DllCall("OpenProcess",    "UInt",0x38, "Int",0, "UInt",pidTaskbar)
    pRB        := DllCall("VirtualAllocEx", "Ptr",hProc, "Ptr",0, "UPtr",20, "UInt",0x1000, "UInt",0x04)

    btn        := Buffer(A_Is64bitOS ? 32 : 20, 0)
    nfo        := Buffer(A_Is64bitOS ? 32 : 24, 0)
    tip        := Buffer(128 * 2, 0)

    res := SendMessage(TB_BUTTONCOUNT := 0x0418, 0, 0, "ToolbarWindow32" idxTB, "ahk_class " sTray)
    Loop res {
      SendMessage(TB_GETBUTTON := 0x0417, A_Index - 1, pRB, "ToolbarWindow32" idxTB, "ahk_class " sTray)

      DllCall("ReadProcessMemory", "Ptr",hProc, "Ptr",pRB, "Ptr",btn.Ptr, "UPtr",btn.Size, "UPtr",0)
      iBitmap  := NumGet(btn, 0, "Int")
      idCmd    := NumGet(btn, 4, "Int")
      fsState  := NumGet(btn, 8, "UChar")
      fsStyle  := NumGet(btn, 9, "UChar")
      dwData   := NumGet(btn, A_Is64bitOS ? 16 : 12, "UPtr")
      iString  := NumGet(btn, A_Is64bitOS ? 24 : 16, "Ptr")
      DllCall("ReadProcessMemory", "Ptr",hProc, "Ptr",dwData, "Ptr",nfo.Ptr, "UPtr",nfo.Size, "UPtr",0)

      hWnd     := NumGet(nfo, 0, "Ptr")
      uId      := NumGet(nfo, A_Is64bitOS ?  8 :  4, "UInt")
      msgId    := NumGet(nfo, A_Is64bitOS ? 12 :  8, "UPtr")
      hIcon    := NumGet(nfo, A_Is64bitOS ? 24 : 20, "Ptr")

      nPid     := WinGetPID("ahk_id " hWnd)
      sProcess := WinGetProcessName("ahk_id " hWnd)
      sClass   := WinGetClass("ahk_id " hWnd)

      if not sExeName or sExeName == sProcess or sExeName == nPid {
        DllCall("ReadProcessMemory"   , "Ptr",hProc, "Ptr",iString, "Ptr",tip.Ptr, "UPtr",tip.Size, "UPtr",0)
        oTrayInfo.Push(Map( "idx"     , A_Index - 1
                          , "idcmd"   , idCmd
                          , "pid"     , nPid
                          , "uid"     , uId
                          , "msgid"   , msgId
                          , "hicon"   , hIcon
                          , "hwnd"    , hWnd
                          , "class"   , sClass
                          , "process" , sProcess
                          , "tooltip" , StrGet(tip.Ptr, "UTF-16")
                          , "tray"    , sTray))
      }
    }
    DllCall("VirtualFreeEx", "Ptr",hProc, "Ptr",pRB, "UPtr",0, "UInt",0x8000)
    DllCall("CloseHandle",   "Ptr",hProc)
  }
  DetectHiddenWindows(dhw)
  return oTrayInfo
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Hide
; Description ..: Hide or unhide a tray icon.
; Parameters ...: idCmd - Command identifier associated with the button.
; ..............: sTray - Place where to find the icon ("Shell_TrayWnd" or "NotifyIconOverflowWindow").
; ..............: bHide - True for hide, False for unhide.
; Info .........: TB_HIDEBUTTON message - http://goo.gl/oelsAa
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Hide(idCmd, sTray := "Shell_TrayWnd", bHide := true) {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows(1)
  idxTB := TrayIcon_GetTrayBar()
  SendMessage(TB_HIDEBUTTON := 0x0404, idCmd, bHide, "ToolbarWindow32" idxTB, "ahk_class " sTray)
  SendMessage(0x001A, 0, 0,, "ahk_class " sTray)
  DetectHiddenWindows(dhw)
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Delete
; Description ..: Delete a tray icon.
; Parameters ...: idx   - 0 based tray icon index.
; ..............: sTray - Place where to find the icon ("Shell_TrayWnd" or "NotifyIconOverflowWindow").
; Info .........: TB_DELETEBUTTON message - http://goo.gl/L0pY4R
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Delete(idx, sTray := "Shell_TrayWnd") {
    dhw := A_DetectHiddenWindows
    DetectHiddenWindows(1)
    idxTB := TrayIcon_GetTrayBar()
    SendMessage(TB_DELETEBUTTON := 0x0416, idx, 0, "ToolbarWindow32" idxTB, "ahk_class " sTray)
    SendMessage(0x001A, 0, 0,, "ahk_class " sTray)
    DetectHiddenWindows(dhw)
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Remove
; Description ..: Remove a Tray icon. It should be more reliable than TrayIcon_Delete.
; Parameters ...: hWnd - Window handle.
; ..............: uId  - Application defined identifier for the icon.
; Info .........: NOTIFYICONDATA structure  - https://goo.gl/1Xuw5r
; ..............: Shell_NotifyIcon function - https://goo.gl/tTSSBM
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Remove(hWnd, uId) {
  NID := Buffer(2 * 384 + A_PtrSize * 5 + 40, 0)
  NumPut("UPtr", NID.Size, NID, 0)
  NumPut("UPtr", hWnd,     NID, A_PtrSize)
  NumPut("UPtr", uId,      NID, A_PtrSize * 2)
  return DllCall("Shell32.dll\Shell_NotifyIcon", "UInt",0x2, "UInt",NID.Ptr)
}

; ----------------------------------------------------------------------------------------------------------------------
; 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  - Place where to find the icon ("Shell_TrayWnd" or "NotifyIconOverflowWindow").
; Info .........: TB_MOVEBUTTON message - http://goo.gl/1F6wPw
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Move(idxOld, idxNew, sTray := "Shell_TrayWnd") {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows(1)
  idxTB := TrayIcon_GetTrayBar()
  SendMessage 0x452, idxOld, idxNew, "ToolbarWindow32" idxTB, "ahk_class " sTray ; TB_MOVEBUTTON := 0x452
  DetectHiddenWindows(dhw)
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Set
; Description ..: Modify icon with the given index for the given window.
; Parameters ...: hWnd       - Window handle.
; ..............: uId        - Application defined identifier for the icon.
; ..............: hIcon      - Handle to the tray icon.
; ..............: hIconSmall - Handle to the small icon, for window menubar. Optional.
; ..............: hIconBig   - Handle to the big icon, for taskbar. Optional.
; Return .......: True on success, false on failure.
; Info .........: NOTIFYICONDATA structure  - https://goo.gl/1Xuw5r
; ..............: Shell_NotifyIcon function - https://goo.gl/tTSSBM
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Set(hWnd, uId, hIcon, hIconSmall := 0, hIconBig := 0) {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows(1)
  if hIconSmall
    SendMessage(WM_SETICON := 0x0080, 0, hIconSmall,, "ahk_id " hWnd)
  if hIconBig
    SendMessage(WM_SETICON := 0x0080, 1, hIconBig,, "ahk_id " hWnd)
  DetectHiddenWindows(dhw)

  NID := Buffer(2 * 384 + A_PtrSize * 5 + 40, 0)
  NumPut("UPtr", NID.Size, NID, 0)
  NumPut("UPtr", hWnd,     NID, (A_PtrSize == 4)? 4  : 8)
  NumPut("UPtr", uId,      NID, (A_PtrSize == 4)? 8  : 16)
  NumPut("UPtr", 2,        NID, (A_PtrSize == 4)? 12 : 20)
  NumPut("UPtr", hIcon,    NID, (A_PtrSize == 4)? 20 : 32)

  return DllCall("Shell32.dll\Shell_NotifyIcon", "UInt",NIM_MODIFY := 0x1, "Ptr",NID.Ptr)
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_GetTrayBar
; Description ..: Get the tray icon handle.
; Parameters ...: sTray - Traybar to retrieve.
; Return .......: Tray icon handle.
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_GetTrayBar(sTray := "Shell_TrayWnd") {
  idxTB := "", nTB := ""
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows(1)
  for k in WinGetControls("ahk_class " sTray)
    if RegExMatch(k, "(?<=ToolbarWindow32)\d+(?!.*ToolbarWindow32)", &nTB)
      loop nTB[] {
        hWnd    := ControlGetHwnd("ToolbarWindow32" A_Index, "ahk_class " sTray)
        hParent := DllCall("GetParent", "Ptr",hWnd)
        sClass  := WinGetClass("ahk_id " hParent)
        if not (sClass == "SysPager" or sClass == "NotifyIconOverflowWindow")
          continue
        idxTB := A_Index
        break
      }
  DetectHiddenWindows(dhw)
  return idxTB
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_GetHotItem
; Description ..: Get the index of tray's hot item.
; Return .......: Index of tray's hot item.
; Info .........: TB_GETHOTITEM message - http://goo.gl/g70qO2
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_GetHotItem() {
  idxTB := TrayIcon_GetTrayBar()
  return SendMessage(TB_GETHOTITEM := 0x0447, 0, 0, "ToolbarWindow32" idxTB, "ahk_class Shell_TrayWnd") << 32 >> 32
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Button
; Description ..: Simulate mouse button click on a tray icon.
; Parameters ...: sExeName - Executable Process Name of tray icon (case sensitive).
; ..............: sButton  - Mouse button to simulate (L, M, R).
; ..............: bDouble  - True to double click, false to single click.
; ..............: nIdx     - Index of tray icon to click if more than one match.
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Button(sExeName, sButton := "L", bDouble := false, nIdx := 1) {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows(1)
  sButton := "WM_" sButton "BUTTON"
  oIcons  := TrayIcon_GetInfo(sExeName)
  if bDouble
    action("DBLCLK")
  else
    action("DOWN"), action("UP")
  DetectHiddenWindows(dhw)

  action(arg) {
    static actions := Map(  "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)
    PostMessage(oIcons[nIdx]["msgid"], oIcons[nIdx]["uid"], actions[sButton arg],, "ahk_id " oIcons[nIdx]["hwnd"])
  }
}

User avatar
JoeWinograd
Posts: 2198
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by JoeWinograd » 05 Dec 2022, 15:05

Hi @FanaticGuru,

Summary: Many functions of the TrayIcon library do not work in the latest release of Windows 11.

Details: I posted a related question here:
viewtopic.php?f=76&t=111185

I've been using the TrayIcon library for a long time, from XP through W11. Many of my programs do not have a desktop presence/windows, rather, the entire user interface is via system tray icons/menus. The TrayIcon library is critical for the proper operation of these programs. This has become a huge problem me with the latest release of W11, where many functions of the library no longer work (my W11 test system currently has Windows 11 Home, Version 22H2, Build 22623.1020).

My programs have supported W11 since its initial release on 5-Oct-2021 (as well as prior versions via the Windows Insider preview program). The TrayIcon library worked well in all releases except one, at which time I submitted this bug report via the Feedback Hub (31-Mar-2022):

In Windows Build ni_release 22581, it is no longer possible to drag-and-drop taskbar icons to different positions on the taskbar. It is also no longer possible for a program to move icons on the taskbar to different positions. That worked fine in all prior builds of W11 (and in all Windows releases from XP on). I suspect that these bugs are because the TB_MOVEBUTTON message has stopped working, but that's just a guess on my part. Whatever the reason, please fix it so that it is possible to both manually and programmatically move taskbar icons to different positions on the taskbar.

This, of course, stopped some TrayIcon functions from working. Microsoft replied that it was a new taskbar feature, not a bug, but they did, in fact, fix it in the next release, and the TrayIcon functions have been working ever since...until now. An interesting difference between then and now is that drag-and-drop of the icons is working.

TrayIcon function calls that I can confirm work fine in W10 but fail in W11 are TrayIcon_Button, TrayIcon_GetInfo, TrayIcon_Move, TrayIcon_Remove.

I don't know if this is a W11 bug or if they changed the API calls. In any case, I very much hope that you can get the TrayIcon library to work in the current W11 release. It is absolutely crucial for me. Thank you, Joe

Woogachaka
Posts: 2
Joined: 02 Jan 2023, 10:55

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by Woogachaka » 02 Jan 2023, 11:12

So I've been attempting to troubleshoot this modification to the library from this thread, and am admittedly new at this. I fixed a couple of the bugs from on Windows 11, however I'm running into a strange one:

---------------------------
audioDefault.ahk
---------------------------
Error: Call to nonexistent function.

Specifically: WinGetPID(["ahk_class ", sTray,"",""])

Line#
054: dhw := A_DetectHiddenWindows
055: DetectHiddenWindows,1
057: oTrayInfo := []
058: For sTray, in ["Shell_TrayWnd", "NotifyIconOverflowWindow"]
058: {
059: idxTB := TrayIcon_GetTrayBar(sTray)
060: MsgBox,%sTray%
---> 061: pidTaskbar := WinGetPID(["ahk_class ", sTray,"",""])
063: hProc := DllCall("OpenProcess", "UInt",0x38, "Int",0, "UInt",pidTaskbar)
064: pRB := DllCall("VirtualAllocEx", "Ptr",hProc, "Ptr",0, "UPtr",20, "UInt",0x1000, "UInt",0x04)
066: btn := Buffer(A_Is64bitOS ? 32 : 20, 0)
067: nfo := Buffer(A_Is64bitOS ? 32 : 24, 0)
068: tip := Buffer(128 * 2, 0)
070: res := SendMessage(TB_BUTTONCOUNT := 0x0418, 0, 0, "ToolbarWindow32" idxTB, "ahk_class " sTray)
071: Loop,res

The program will exit.
---------------------------
OK
---------------------------

This is the current version of the script:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

+^!F6::
TrayIcon_Button("AudioSwitcher.exe")
return

; ported to AHK v2.0-beta by @krasnovpro
; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=1229

; ----------------------------------------------------------------------------------------------------------------------
; Name ..........: TrayIcon library
; Description ...: Provide some useful functions to deal with Tray icons.
; AHK Version ...: AHK_L 1.1.22.02 x32/64 Unicode
; Code from .....: Sean (http://www.autohotkey.com/forum/viewtopic.php?t=17314)
; Author ........: Cyruz (http://ciroprincipe.info) (http://ahkscript.org/boards/viewtopic.php?f=6&t=1229)
; Mod from ......: Fanatic Guru - Cyruz
; License .......: WTFPL - http://www.wtfpl.net/txt/copying/
; Version Date ..: 2019.03.12
; Upd.20160120 ..: Fanatic Guru - Went through all the data types in the DLL and NumGet and matched them up to MSDN
; ...............:                which fixed idCmd.
; Upd.20160308 ..: Fanatic Guru - Fix for Windows 10 NotifyIconOverflowWindow.
; Upd.20180313 ..: Fanatic Guru - Fix problem with "VirtualFreeEx" pointed out by nnnik.
; Upd.20180313 ..: Fanatic Guru - Additional fix for previous Windows 10 NotifyIconOverflowWindow fix breaking non
; ...............:                hidden icons.
; Upd.20190312 ..: Cyruz        - Added TrayIcon_Set, code merged and refactored.
; ----------------------------------------------------------------------------------------------------------------------

; ----------------------------------------------------------------------------------------------------------------------
; 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 ........: oTrayInfo - An array of objects containing tray icons data. Any entry is structured like this:
; ...............:             oTrayInfo[A_Index].idx     - 0 based tray icon index.
; ...............:             oTrayInfo[A_Index].idcmd   - Command identifier associated with the button.
; ...............:             oTrayInfo[A_Index].pid     - Process ID.
; ...............:             oTrayInfo[A_Index].uid     - Application defined identifier for the icon.
; ...............:             oTrayInfo[A_Index].msgid   - Application defined callback message.
; ...............:             oTrayInfo[A_Index].hicon   - Handle to the tray icon.
; ...............:             oTrayInfo[A_Index].hwnd    - Window handle.
; ...............:             oTrayInfo[A_Index].class   - Window class.
; ...............:             oTrayInfo[A_Index].process - Process executable.
; ...............:             oTrayInfo[A_Index].tray    - Tray Type (Shell_TrayWnd or NotifyIconOverflowWindow).
; ...............:             oTrayInfo[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 := "") {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows 1

  oTrayInfo    := []
  for sTray in ["Shell_TrayWnd", "NotifyIconOverflowWindow"] {
    idxTB      := TrayIcon_GetTrayBar(sTray)
    MsgBox, %sTray%
    pidTaskbar := WinGetPID(["ahk_class ", sTray,"",""])

    hProc      := DllCall("OpenProcess",    "UInt",0x38, "Int",0, "UInt",pidTaskbar)
    pRB        := DllCall("VirtualAllocEx", "Ptr",hProc, "Ptr",0, "UPtr",20, "UInt",0x1000, "UInt",0x04)

    btn        := Buffer(A_Is64bitOS ? 32 : 20, 0)
    nfo        := Buffer(A_Is64bitOS ? 32 : 24, 0)
    tip        := Buffer(128 * 2, 0)

    res := SendMessage(TB_BUTTONCOUNT := 0x0418, 0, 0, "ToolbarWindow32" idxTB, "ahk_class " sTray)
    Loop res {
      SendMessage(TB_GETBUTTON := 0x0417, A_Index - 1, pRB, "ToolbarWindow32" idxTB, "ahk_class " sTray)

      DllCall("ReadProcessMemory", "Ptr",hProc, "Ptr",pRB, "Ptr",btn.Ptr, "UPtr",btn.Size, "UPtr",0)
      iBitmap  := NumGet(btn, 0, "Int")
      idCmd    := NumGet(btn, 4, "Int")
      fsState  := NumGet(btn, 8, "UChar")
      fsStyle  := NumGet(btn, 9, "UChar")
      dwData   := NumGet(btn, A_Is64bitOS ? 16 : 12, "UPtr")
      iString  := NumGet(btn, A_Is64bitOS ? 24 : 16, "Ptr")
      DllCall("ReadProcessMemory", "Ptr",hProc, "Ptr",dwData, "Ptr",nfo.Ptr, "UPtr",nfo.Size, "UPtr",0)

      hWnd     := NumGet(nfo, 0, "Ptr")
      uId      := NumGet(nfo, A_Is64bitOS ?  8 :  4, "UInt")
      msgId    := NumGet(nfo, A_Is64bitOS ? 12 :  8, "UPtr")
      hIcon    := NumGet(nfo, A_Is64bitOS ? 24 : 20, "Ptr")

      nPid     := WinGetPID("ahk_id " hWnd)
      sProcess := WinGetProcessName("ahk_id " hWnd)
      sClass   := WinGetClass("ahk_id " hWnd)

      if not sExeName or sExeName == sProcess or sExeName == nPid {
        DllCall("ReadProcessMemory"   , "Ptr",hProc, "Ptr",iString, "Ptr",tip.Ptr, "UPtr",tip.Size, "UPtr",0)
        oTrayInfo.Push(Map( "idx"     , A_Index - 1
                          , "idcmd"   , idCmd
                          , "pid"     , nPid
                          , "uid"     , uId
                          , "msgid"   , msgId
                          , "hicon"   , hIcon
                          , "hwnd"    , hWnd
                          , "class"   , sClass
                          , "process" , sProcess
                          , "tooltip" , StrGet(tip.Ptr, "UTF-16")
                          , "tray"    , sTray))
      }
    }
    DllCall("VirtualFreeEx", "Ptr",hProc, "Ptr",pRB, "UPtr",0, "UInt",0x8000)
    DllCall("CloseHandle",   "Ptr",hProc)
  }
  if (dhw)
    {
        DetectHiddenWindows 1
    }
  else
    {
        DetectHiddenWindows 0
    }
  return oTrayInfo
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Hide
; Description ..: Hide or unhide a tray icon.
; Parameters ...: idCmd - Command identifier associated with the button.
; ..............: sTray - Place where to find the icon ("Shell_TrayWnd" or "NotifyIconOverflowWindow").
; ..............: bHide - True for hide, False for unhide.
; Info .........: TB_HIDEBUTTON message - http://goo.gl/oelsAa
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Hide(idCmd, sTray := "Shell_TrayWnd", bHide := true) {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows 1
  idxTB := TrayIcon_GetTrayBar()
  SendMessage(TB_HIDEBUTTON := 0x0404, idCmd, bHide, "ToolbarWindow32" idxTB, "ahk_class " sTray)
  SendMessage(0x001A, 0, 0,, "ahk_class " sTray)
  if (dhw)
    {
        DetectHiddenWindows 1
    }
  else
    {
        DetectHiddenWindows 0
    }
  }

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Delete
; Description ..: Delete a tray icon.
; Parameters ...: idx   - 0 based tray icon index.
; ..............: sTray - Place where to find the icon ("Shell_TrayWnd" or "NotifyIconOverflowWindow").
; Info .........: TB_DELETEBUTTON message - http://goo.gl/L0pY4R
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Delete(idx, sTray := "Shell_TrayWnd") {
    dhw := A_DetectHiddenWindows
    DetectHiddenWindows 1
    idxTB := TrayIcon_GetTrayBar()
    SendMessage(TB_DELETEBUTTON := 0x0416, idx, 0, "ToolbarWindow32" idxTB, "ahk_class " sTray)
    SendMessage(0x001A, 0, 0,, "ahk_class " sTray)
    if (dhw)
    {
        DetectHiddenWindows 1
    }
    else
    {
        DetectHiddenWindows 0
    }
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Remove
; Description ..: Remove a Tray icon. It should be more reliable than TrayIcon_Delete.
; Parameters ...: hWnd - Window handle.
; ..............: uId  - Application defined identifier for the icon.
; Info .........: NOTIFYICONDATA structure  - https://goo.gl/1Xuw5r
; ..............: Shell_NotifyIcon function - https://goo.gl/tTSSBM
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Remove(hWnd, uId) {
  NID := Buffer(2 * 384 + A_PtrSize * 5 + 40, 0)
  NumPut("UPtr", NID.Size, NID, 0)
  NumPut("UPtr", hWnd,     NID, A_PtrSize)
  NumPut("UPtr", uId,      NID, A_PtrSize * 2)
  return DllCall("Shell32.dll\Shell_NotifyIcon", "UInt",0x2, "UInt",NID.Ptr)
}

; ----------------------------------------------------------------------------------------------------------------------
; 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  - Place where to find the icon ("Shell_TrayWnd" or "NotifyIconOverflowWindow").
; Info .........: TB_MOVEBUTTON message - http://goo.gl/1F6wPw
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Move(idxOld, idxNew, sTray := "Shell_TrayWnd") {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows 1
  idxTB := TrayIcon_GetTrayBar()
  SendMessage 0x452, idxOld, idxNew, "ToolbarWindow32" idxTB, "ahk_class " sTray ; TB_MOVEBUTTON := 0x452
  if (dhw)
    {
        DetectHiddenWindows 1
    }
  else
    {
        DetectHiddenWindows 0
    }
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Set
; Description ..: Modify icon with the given index for the given window.
; Parameters ...: hWnd       - Window handle.
; ..............: uId        - Application defined identifier for the icon.
; ..............: hIcon      - Handle to the tray icon.
; ..............: hIconSmall - Handle to the small icon, for window menubar. Optional.
; ..............: hIconBig   - Handle to the big icon, for taskbar. Optional.
; Return .......: True on success, false on failure.
; Info .........: NOTIFYICONDATA structure  - https://goo.gl/1Xuw5r
; ..............: Shell_NotifyIcon function - https://goo.gl/tTSSBM
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Set(hWnd, uId, hIcon, hIconSmall := 0, hIconBig := 0) {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows 1
  if hIconSmall
    SendMessage(WM_SETICON := 0x0080, 0, hIconSmall,, "ahk_id " hWnd)
  if hIconBig
    SendMessage(WM_SETICON := 0x0080, 1, hIconBig,, "ahk_id " hWnd)
  if (dhw)
    {
        DetectHiddenWindows 1
    }
  else
    {
        DetectHiddenWindows 0
    }

  NID := Buffer(2 * 384 + A_PtrSize * 5 + 40, 0)
  NumPut("UPtr", NID.Size, NID, 0)
  NumPut("UPtr", hWnd,     NID, (A_PtrSize == 4)? 4  : 8)
  NumPut("UPtr", uId,      NID, (A_PtrSize == 4)? 8  : 16)
  NumPut("UPtr", 2,        NID, (A_PtrSize == 4)? 12 : 20)
  NumPut("UPtr", hIcon,    NID, (A_PtrSize == 4)? 20 : 32)

  return DllCall("Shell32.dll\Shell_NotifyIcon", "UInt",NIM_MODIFY := 0x1, "Ptr",NID.Ptr)
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_GetTrayBar
; Description ..: Get the tray icon handle.
; Parameters ...: sTray - Traybar to retrieve.
; Return .......: Tray icon handle.
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_GetTrayBar(sTray := "Shell_TrayWnd") {
  idxTB := "", nTB := ""
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows 1
  for k in WinGetControls("ahk_class " sTray)
    if RegExMatch(k, "(?<=ToolbarWindow32)\d+(?!.*ToolbarWindow32)", &nTB)
      loop nTB[] {
        hWnd    := ControlGetHwnd("ToolbarWindow32" A_Index, "ahk_class " sTray)
        hParent := DllCall("GetParent", "Ptr",hWnd)
        sClass  := WinGetClass("ahk_id " hParent)
        if not (sClass == "SysPager" or sClass == "NotifyIconOverflowWindow")
          continue
        idxTB := A_Index
        break
      }
  if (dhw)
    {
        DetectHiddenWindows 1
    }
  else
    {
        DetectHiddenWindows 0
    }
  return idxTB
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_GetHotItem
; Description ..: Get the index of tray's hot item.
; Return .......: Index of tray's hot item.
; Info .........: TB_GETHOTITEM message - http://goo.gl/g70qO2
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_GetHotItem() {
  idxTB := TrayIcon_GetTrayBar()
  return SendMessage(TB_GETHOTITEM := 0x0447, 0, 0, "ToolbarWindow32" idxTB, "ahk_class Shell_TrayWnd") << 32 >> 32
}

; ----------------------------------------------------------------------------------------------------------------------
; Function .....: TrayIcon_Button
; Description ..: Simulate mouse button click on a tray icon.
; Parameters ...: sExeName - Executable Process Name of tray icon (case sensitive).
; ..............: sButton  - Mouse button to simulate (L, M, R).
; ..............: bDouble  - True to double click, false to single click.
; ..............: nIdx     - Index of tray icon to click if more than one match.
; ----------------------------------------------------------------------------------------------------------------------
TrayIcon_Button(sExeName, sButton := "L", bDouble := false, nIdx := 1) {
  dhw := A_DetectHiddenWindows
  DetectHiddenWindows 1
  sButton := "WM_" sButton "BUTTON"
  oIcons  := TrayIcon_GetInfo(sExeName)
  if bDouble
    action("DBLCLK")
  else
    action("DOWN"), action("UP")
  if (dhw)
    {
        DetectHiddenWindows 1
    }
  else
    {
        DetectHiddenWindows 0
    }
  /*
  action(arg) {
    static actions := Map(  "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)
    PostMessage(oIcons[nIdx]["msgid"], oIcons[nIdx]["uid"], actions[sButton arg],, "ahk_id " oIcons[nIdx]["hwnd"])
  }
  */
}

action(arg) {
    static actions := Map(  "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)
    PostMessage(oIcons[nIdx]["msgid"], oIcons[nIdx]["uid"], actions[sButton arg],, "ahk_id " oIcons[nIdx]["hwnd"])
  }
Anyone have thoughts on why its failing to find a built in function?

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by swagfag » 02 Jan 2023, 11:54

because uve copypasted a ported script meant for ahk_v2 and are instead trying to run it with ahk_v1

Woogachaka
Posts: 2
Joined: 02 Jan 2023, 10:55

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by Woogachaka » 02 Jan 2023, 16:11

I just installed v2 though. how do I uninstall v1 then?

User avatar
boiler
Posts: 16915
Joined: 21 Dec 2014, 02:44

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by boiler » 02 Jan 2023, 16:20

You don’t have to uninstall v1. In fact, it’s best not to. You can force AHK to select v2 in case it has trouble automatically determining which version to use by putting this at the top of your script:

Code: Select all

#Requires AutoHotkey v2.0

luizrocha
Posts: 3
Joined: 08 Dec 2022, 16:59

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by luizrocha » 04 Jan 2023, 10:29

Is it possible fix the code, so that we can continue using v1? Any tips on how to do it?

User avatar
JoeWinograd
Posts: 2198
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by JoeWinograd » 04 Jan 2023, 10:57

luizrocha wrote:
04 Jan 2023, 10:29
Is it possible fix the code, so that we can continue using v1? Any tips on how to do it?
More discussion on this issue and some work-around tips are at this forum thread:

viewtopic.php?f=23&t=111398

Regards, Joe

luizrocha
Posts: 3
Joined: 08 Dec 2022, 16:59

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by luizrocha » 04 Jan 2023, 12:41

JoeWinograd wrote:
04 Jan 2023, 10:57
luizrocha wrote:
04 Jan 2023, 10:29
Is it possible fix the code, so that we can continue using v1? Any tips on how to do it?
More discussion on this issue and some work-around tips are at this forum thread:

viewtopic.php?f=23&t=111398

Regards, Joe
Thank you

VuYeK
Posts: 1
Joined: 27 Nov 2023, 10:07

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by VuYeK » 27 Nov 2023, 10:10

Hey @JoeWinograd and others. Do you have any working version of this lib for latest windows 11?

User avatar
JoeWinograd
Posts: 2198
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by JoeWinograd » 27 Nov 2023, 14:54

VuYeK wrote:Do you have any working version of this lib for latest windows 11?
Hi VuYeK,
I see that this is your first post here, so let me start with...Welcome Aboard!

Now, to your question. I can't speak for others, but I do not. My W11 Home Windows Insider machine is on Version 23H2, Build 22635.2771, and the V1 TrayIcon Library still does not work on it...and Microsoft still does not acknowledge the problem. Microsoft has not posted anything since the "not a bug" comment 11 months ago. I don't know if any of the AHK TrayIcon Library authors have attempted to get it to work in the recent W11 builds (i.e., Version 22H2 Build 22623.1020 and later)...perhaps one of them will let us know in this thread. Regards, Joe

kashmirLZ
Posts: 41
Joined: 06 Oct 2022, 23:27

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by kashmirLZ » 17 Mar 2024, 09:24

I dont understand what TrayIcon_GetHotItem does.

User avatar
JoeWinograd
Posts: 2198
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by JoeWinograd » 17 Mar 2024, 10:04

Hi @kashmirLZ,

It returns the index (zero-based) of the icon in the system tray over which the mouse is hovering. If the mouse is not hovering over an icon, it returns -1. Microsoft doc is here:

https://learn.microsoft.com/en-us/windows/win32/controls/tb-gethotitem

Very easy to test with this simple V1 script:

Code: Select all

!^h:: ; hotkey is Alt+Ctrl+h - make it whateever you prefer
MsgBox % TrayIcon_GetHotItem()
Return
#Include <location of your V1 TrayIconLibrary.ahk>
Or this V2 script:

Code: Select all

!^h:: ; hotkey is Alt+Ctrl+h - make it whateever you prefer
{
  MsgBox("HotItem Index=" . TrayIcon_GetHotItem(),"HotItem via TrayIcon V2")
  Return
}
#Include <location of your V2 TrayIconLibrary.ahk>

After running the script, move the mouse to different icons in the system tray and hit the hotkey...each time you hit the hotkey, you'll see the icon's tray index in the MsgBox. If you hit the hotkey when you're not hovering over a tray icon, you'll see -1 in the MsgBox. Note my comments above...this function does not work in Windows 11 Build 22623.1020 and later. Regards, Joe

kashmirLZ
Posts: 41
Joined: 06 Oct 2022, 23:27

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by kashmirLZ » 18 Mar 2024, 12:22

Thanks! Next,
TrayIcon_Button("AOL.exe", "R")
I used that function to right-click on my desired tray-icon.
So wherever the mouse is, it makes the context window appear at. (Either above or below cursor depending on where the context menu has room).

So now whats a reliable way to always click the third element in the list?
Or should I ImageSearch at that point to click it?

User avatar
JoeWinograd
Posts: 2198
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: [LIB] TrayIcon - Sean's TrayIcon for Unicode and 64 bit

Post by JoeWinograd » 18 Mar 2024, 12:39

kashmirLZ wrote:So now whats a reliable way to always click the third element in the list?
After the TrayIcon_Button("AOL.exe", "R") call, try this in V1:

Send {Down 3}{Enter}

Or this in V2:

Send "{Down 3}{Enter}"

Both work here. Regards, Joe

Post Reply

Return to “Scripts and Functions (v1)”