Page 1 of 1

script help: minimize to icon in system tray

Posted: 20 Aug 2021, 12:00
by bittersmile
Dear friends,

Can someone help me with a script? I need to assign a hotkey to hide the current active window to system tray as an icon. As for unhide, it would be best if it can be done from the icon (double click or right click and a menu), but if it is difficult, I cal always unhide all windows.

I searched and found some related scripts, but none of them is exactly what I need. The closest one is this one:
https://www.autohotkey.com/boards/viewtopic.php?t=27339

Since I am not good enough to modify these scripts to suit my need, can someone help me?

Thank you very much.

Re: script help: minimize to icon in system tray

Posted: 20 Aug 2021, 15:25
by teadrinker
Try this:

Code: Select all

#NoTrayIcon
DetectHiddenWindows, On
global EVENT_SYSTEM_MINIMIZESTART := 0x0016
     , EVENT_SYSTEM_MINIMIZEEND   := 0x0017
     , EVENT_OBJECT_DESTROY       := 0x8001
     , Windows := []

MinimizeHook := new WinEventHook(EVENT_SYSTEM_MINIMIZESTART, EVENT_SYSTEM_MINIMIZEEND, "HookProc")
DestroyHook  := new WinEventHook(EVENT_OBJECT_DESTROY      , EVENT_OBJECT_DESTROY    , "HookProc")
OnMessage(0x404, "AHK_NOTIFYICON")
OnExit("ShowHidden")
Return

Esc::
Terminate() {
   ExitApp
}

^Space::
AddToListAndMinimize() {
   hWnd := WinExist("A")
   SetTitleMatchMode, RegEx
   if WinActive("ahk_class ^(Progman|WorkerW|Shell_TrayWnd)$")
      Return
   if !Windows.HasKey(hWnd) {
      WinGetTitle, title
      Windows[hwnd] := {title: title, hIcon: GetWindowIcon(hWnd)}
   }
   WinMinimize
}

ShowHidden() {
   for hwnd, v in Windows {
      if !DllCall("IsWindowVisible", "Ptr", hwnd)
         WinShow, ahk_id %hwnd%
      if WinExist("ahk_id" . v.gui)
         RemoveTrayIcon(v.gui)
   }
}

GetWindowIcon(hWnd) {
   static WM_GETICON := 0x007F, ICON_SMALL := 0, GCLP_HICONSM := -34
        , GetClassLong := "GetClassLong" . (A_PtrSize = 4 ? "" : "Ptr")
   SendMessage, WM_GETICON, ICON_SMALL, A_ScreenDPI,, ahk_id %hWnd%
   if !smallIcon := ErrorLevel
      smallIcon := DllCall(GetClassLong, "Ptr", hWnd, "Int", GCLP_HICONSM, "Ptr")
   Return smallIcon
}

AddTrayIcon(hIcon, tip := "")
{
   static NIF_MESSAGE := 1, NIF_ICON := 2, NIF_TIP := 4, NIM_ADD := 0
   flags := NIF_MESSAGE|NIF_ICON|(tip = "" ? 0 : NIF_TIP)
   VarSetCapacity(NOTIFYICONDATA, size := A_PtrSize = 8 ? 848 : A_IsUnicode? 828 : 444, 0)
   Gui, New, +hwndhGui
   NumPut(size         , NOTIFYICONDATA)
   NumPut(hGui         , NOTIFYICONDATA, A_PtrSize)
   NumPut(uID  := 0x404, NOTIFYICONDATA, A_PtrSize*2)
   NumPut(flags        , NOTIFYICONDATA, A_PtrSize*2 + 4)
   NumPut(nMsg := 0x404, NOTIFYICONDATA, A_PtrSize*2 + 8)
   NumPut(hIcon        , NOTIFYICONDATA, A_PtrSize*3 + 8)
   if (tip != "")
      StrPut(tip, &NOTIFYICONDATA + 4*A_PtrSize + 8, "CP0")
   DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_ADD, "Ptr", &NOTIFYICONDATA)
   Return hGui
}

RemoveTrayIcon(hWnd, uID := 0x404)
{
   VarSetCapacity(NOTIFYICONDATA, size := A_PtrSize = 8 ? 848 : A_IsUnicode? 828 : 444, 0)
   NumPut(size, NOTIFYICONDATA)
   NumPut(hWnd, NOTIFYICONDATA, A_PtrSize)
   NumPut(uID , NOTIFYICONDATA, A_PtrSize*2)
   DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_DELETE := 2, "Ptr", &NOTIFYICONDATA)
   Return
}

AHK_NOTIFYICON(wp, lp, msg, hwnd)   ; wp — uID, lp — Message
{
   static WM_LBUTTONDOWN := 0x201, WM_RBUTTONUP := 0x205, maxLenMenuStr := 40
   if !(lp = WM_LBUTTONDOWN || lp = WM_RBUTTONUP)
      Return
   
   for k, v in Windows
      if (v.gui = hwnd && window := k)
         break
   if !window
      Return
   
   Switch lp {
   Case WM_LBUTTONDOWN:
      RemoveTrayIcon(hwnd)
      Gui, %hwnd%: Destroy
      WinShow, ahk_id %window%
      WinActivate, ahk_id %window%
   Case WM_RBUTTONUP:
      title := Windows[window, "title"]
      b := StrLen(title) > maxLenMenuStr
      menuText := "Restore «" . SubStr(title, 1, maxLenMenuStr) . (b ? "..." : "") . "»"
      fn := Func(A_ThisFunc).Bind(0x404, WM_LBUTTONDOWN, 0x404, hwnd)
      Menu, IconMenu, Add , % menuText, % fn
      Menu, IconMenu, Icon, % menuText, % "HICON:*" . Windows[window, "hIcon"]
      Menu, IconMenu, Add , Terminate script and show all windows, Terminate
      Menu, IconMenu, Show
      Menu, IconMenu, DeleteAll
   }
}

HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) {
   static OBJID_WINDOW := 0
   if !( idObject = OBJID_WINDOW && Windows.HasKey(hwnd) )
      Return
   Switch event {
   Case EVENT_SYSTEM_MINIMIZESTART:
      WinHide, ahk_id %hWnd%
      Windows[hwnd, "gui"] := AddTrayIcon(Windows[hwnd, "hIcon"], Windows[hwnd, "title"])
   Case EVENT_OBJECT_DESTROY, EVENT_SYSTEM_MINIMIZEEND:
      iconGui := Windows[hwnd, "gui"]
      if WinExist("ahk_id" . iconGui)
         RemoveTrayIcon(iconGui)
      try Gui, %iconGui%: Destroy
      ( event = EVENT_OBJECT_DESTROY && Windows.Delete(hwnd) )
   }
}

class WinEventHook
{  ; Event Constants: https://is.gd/tRT5Wr
   __New(eventMin, eventMax, hookProc, eventInfo := 0, idProcess := 0, idThread := 0, dwFlags := 0) {
      this.pCallback := RegisterCallback(hookProc, "F",, eventInfo)
      this.hHook := DllCall("SetWinEventHook", "UInt", eventMin, "UInt", eventMax, "Ptr", 0, "Ptr", this.pCallback
                                             , "UInt", idProcess, "UInt", idThread, "UInt", dwFlags, "Ptr")
   }
   __Delete() {
      DllCall("UnhookWinEvent", "Ptr", this.hHook)
      DllCall("GlobalFree", "Ptr", this.pCallback, "Ptr")
   }
}
Minimize a window to the tray icon with ^Space, afterward this window can be minimized as usual, it will be hidden in the tray icon.
A minimized window can be restored by clicking the tray icon or from the tray icon menu.

Re: script help: minimize to icon in system tray

Posted: 21 Aug 2021, 15:03
by bittersmile
Cool! Thank you very much!

Re: script help: minimize to icon in system tray

Posted: 21 Aug 2021, 16:05
by Xtra
teadrinker wrote:
20 Aug 2021, 15:25
Try this:
:thumbup:

Re: script help: minimize to icon in system tray

Posted: 21 Aug 2021, 16:05
by teadrinker
Edited a bit.

Re: script help: minimize to icon in system tray

Posted: 26 Oct 2021, 06:45
by ufunk
Thank you very much, I also searched for such a script for a long time :)

Re: script help: minimize to icon in system tray

Posted: 31 Dec 2021, 12:14
by ufunk
Hi,
minimize a window to tray and unhide it from tray works great. But after the first unhide from tray, I just have the wish that the window minimizes as usual when I press minimize in title bar of the window. The current behaviour is that the window minimizes to tray when I press minimize in title bar. My wish is that the window should only minimize to tray when I press the hotkey for it (^Space).
Could someone help me with that? Implementing this change is beyond my ahk skills.

For clarity, here is a picture of the minimize button in the title bar I am referring to:
Image

Re: script help: minimize to icon in system tray

Posted: 01 Jan 2022, 13:29
by teadrinker
@ufunk

Code: Select all

#NoTrayIcon
DetectHiddenWindows, On
global EVENT_SYSTEM_MINIMIZEEND := 0x0017
     , EVENT_OBJECT_DESTROY     := 0x8001
     , Windows := []

MinimizeHook := new WinEventHook(EVENT_SYSTEM_MINIMIZEEND, EVENT_SYSTEM_MINIMIZEEND, "HookProc")
DestroyHook  := new WinEventHook(EVENT_OBJECT_DESTROY    , EVENT_OBJECT_DESTROY    , "HookProc")
OnMessage(0x404, "AHK_NOTIFYICON")
OnExit("ShowHidden")
Return

^Space::
AddToListAndMinimize() {
   hWnd := WinExist("A")
   SetTitleMatchMode, RegEx
   if WinActive("ahk_class ^(Progman|WorkerW|Shell_TrayWnd)$")
      Return
   if !Windows.HasKey(hWnd) {
      WinGetTitle, title
      Windows[hwnd] := {title: title, hIcon: GetWindowIcon(hWnd)}
   }
   WinMinimize
   WinHide
   Windows[hwnd, "gui"] := AddTrayIcon(Windows[hwnd, "hIcon"], Windows[hwnd, "title"])
}

ShowHidden() {
   for hwnd, v in Windows {
      if !DllCall("IsWindowVisible", "Ptr", hwnd)
         WinShow, ahk_id %hwnd%
      if WinExist("ahk_id" . v.gui)
         RemoveTrayIcon(v.gui)
   }
}

GetWindowIcon(hWnd) {
   static WM_GETICON := 0x007F, ICON_SMALL := 0, GCLP_HICONSM := -34
        , GetClassLong := "GetClassLong" . (A_PtrSize = 4 ? "" : "Ptr")
   SendMessage, WM_GETICON, ICON_SMALL, A_ScreenDPI,, ahk_id %hWnd%
   if !smallIcon := ErrorLevel
      smallIcon := DllCall(GetClassLong, "Ptr", hWnd, "Int", GCLP_HICONSM, "Ptr")
   Return smallIcon
}

AddTrayIcon(hIcon, tip := "")
{
   static NIF_MESSAGE := 1, NIF_ICON := 2, NIF_TIP := 4, NIM_ADD := 0
   flags := NIF_MESSAGE|NIF_ICON|(tip = "" ? 0 : NIF_TIP)
   VarSetCapacity(NOTIFYICONDATA, size := 396, 0)
   Gui, New, +hwndhGui
   NumPut(size         , NOTIFYICONDATA)
   NumPut(hGui         , NOTIFYICONDATA, A_PtrSize)
   NumPut(uID  := 0x404, NOTIFYICONDATA, A_PtrSize*2)
   NumPut(flags        , NOTIFYICONDATA, A_PtrSize*2 + 4)
   NumPut(nMsg := 0x404, NOTIFYICONDATA, A_PtrSize*2 + 8)
   NumPut(hIcon        , NOTIFYICONDATA, A_PtrSize*3 + 8)
   if (tip != "")
      StrPut(tip, &NOTIFYICONDATA + 4*A_PtrSize + 8, "CP0")
   DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_ADD, "Ptr", &NOTIFYICONDATA)
   Return hGui
}

RemoveTrayIcon(hWnd, uID := 0x404)
{
   VarSetCapacity(NOTIFYICONDATA, size := 24, 0)
   NumPut(size, NOTIFYICONDATA)
   NumPut(hWnd, NOTIFYICONDATA, A_PtrSize)
   NumPut(uID , NOTIFYICONDATA, A_PtrSize*2)
   DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_DELETE := 2, "Ptr", &NOTIFYICONDATA)
   Return
}

AHK_NOTIFYICON(wp, lp, msg, hwnd)   ; wp — uID, lp — Message
{
   static WM_LBUTTONDOWN := 0x201, WM_RBUTTONUP := 0x205, maxLenMenuStr := 40
   if !(lp = WM_LBUTTONDOWN || lp = WM_RBUTTONUP)
      Return
   
   for k, v in Windows
      if (v.gui = hwnd && window := k)
         break
   if !window
      Return
   
   Switch lp {
   Case WM_LBUTTONDOWN:
      RemoveTrayIcon(hwnd)
      Gui, %hwnd%: Destroy
      WinShow, ahk_id %window%
      WinActivate, ahk_id %window%
   Case WM_RBUTTONUP:
      title := Windows[window, "title"]
      b := StrLen(title) > maxLenMenuStr
      menuText := "Restore «" . SubStr(title, 1, maxLenMenuStr) . (b ? "..." : "") . "»"
      fn := Func(A_ThisFunc).Bind(0x404, WM_LBUTTONDOWN, 0x404, hwnd)
      Menu, IconMenu, Add , % menuText, % fn
      Menu, IconMenu, Icon, % menuText, % "HICON:*" . Windows[window, "hIcon"]
      Menu, IconMenu, Add , Show all windows, ShowHidden
      Menu, IconMenu, Show
      Menu, IconMenu, DeleteAll
   }
}

HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) {
   static OBJID_WINDOW := 0
   if !( idObject = OBJID_WINDOW && Windows.HasKey(hwnd) )
      Return
   iconGui := Windows[hwnd, "gui"]
   if WinExist("ahk_id" . iconGui) {
      RemoveTrayIcon(iconGui)
      Gui, %iconGui%: Destroy
   }
   ( event = EVENT_OBJECT_DESTROY && Windows.Delete(hwnd) )
}

class WinEventHook
{  ; Event Constants: https://is.gd/tRT5Wr
   __New(eventMin, eventMax, hookProc, eventInfo := 0, idProcess := 0, idThread := 0, dwFlags := 0) {
      this.pCallback := RegisterCallback(hookProc, "F",, eventInfo)
      this.hHook := DllCall("SetWinEventHook", "UInt", eventMin, "UInt", eventMax, "Ptr", 0, "Ptr", this.pCallback
                                             , "UInt", idProcess, "UInt", idThread, "UInt", dwFlags, "Ptr")
   }
   __Delete() {
      DllCall("UnhookWinEvent", "Ptr", this.hHook)
      DllCall("GlobalFree", "Ptr", this.pCallback, "Ptr")
   }
}

Re: script help: minimize to icon in system tray

Posted: 01 Jan 2022, 16:31
by ufunk
This is breathtaking and works like a charm. Thank you a lot for your inputs in such a fruitful way. I appreciate that.

Re: script help: minimize to icon in system tray

Posted: 21 Mar 2024, 09:08
by poopux
I like the script, yet, how do I restore the window that has been hidden? I only see the option to restore via the tray. Is there a possible to use a hotkey for restore, too?

Re: script help: minimize to icon in system tray

Posted: 21 Mar 2024, 11:50
by teadrinker
poopux wrote: how do I restore the window that has been hidden?
The script can hide many windows. Which one are you talking about?

Re: script help: minimize to icon in system tray

Posted: 21 Mar 2024, 14:19
by poopux
any window. Once a window is hidden in the tray by pressing CTRL + Space it can only be recovered by clicking on the icon in the tray. I would also like to recover it with a hotkey.
Hide a window by hotkey and restore it with a hotkey.

Thanks
poopux

Re: script help: minimize to icon in system tray

Posted: 21 Mar 2024, 14:53
by teadrinker
Imagine you already have 10 hidden windows. Which of them should appear after pressing a hotkey? Any of them?

Re: script help: minimize to icon in system tray

Posted: 22 Mar 2024, 05:31
by poopux
One by one. Last in, first out.

Re: script help: minimize to icon in system tray

Posted: 22 Mar 2024, 11:14
by teadrinker
Ctrl + R to restore:

Code: Select all

#Requires AutoHotkey v1

#NoTrayIcon
DetectHiddenWindows, On
global EVENT_SYSTEM_MINIMIZEEND := 0x0017
     , EVENT_OBJECT_DESTROY     := 0x8001
     , Windows := [], orderList := []

MinimizeHook := new WinEventHook(EVENT_SYSTEM_MINIMIZEEND, EVENT_SYSTEM_MINIMIZEEND, "HookProc")
DestroyHook  := new WinEventHook(EVENT_OBJECT_DESTROY    , EVENT_OBJECT_DESTROY    , "HookProc")
OnMessage(0x404, "AHK_NOTIFYICON")
OnExit("ShowHidden")
Return

^Space::
AddToListAndMinimize() {
   hWnd := WinExist("A")
   SetTitleMatchMode, RegEx
   if WinActive("ahk_class ^(Progman|WorkerW|Shell_TrayWnd)$")
      Return
   if !Windows.HasKey(hWnd) {
      WinGetTitle, title
      Windows[hwnd] := {title: title, hIcon: GetWindowIcon(hWnd)}
   }
   WinMinimize
   WinHide
   Windows[hwnd, "gui"] := AddTrayIcon(Windows[hwnd, "hIcon"], Windows[hwnd, "title"])
   orderList.Push(hwnd)
}

^r::
RestoreLast() {
   AHK_NOTIFYICON(0x404, WM_LBUTTONDOWN := 0x201, 0x404, Windows[orderList.Pop()].gui)
}

ShowHidden() {
   for hwnd, v in Windows {
      if !DllCall("IsWindowVisible", "Ptr", hwnd)
         WinShow, ahk_id %hwnd%
      if WinExist("ahk_id" . v.gui)
         RemoveTrayIcon(v.gui)
   }
   orderList := []
}

GetWindowIcon(hWnd) {
   static WM_GETICON := 0x007F, ICON_SMALL := 0, GCLP_HICONSM := -34
        , GetClassLong := "GetClassLong" . (A_PtrSize = 4 ? "" : "Ptr")
   SendMessage, WM_GETICON, ICON_SMALL, A_ScreenDPI,, ahk_id %hWnd%
   if !smallIcon := ErrorLevel
      smallIcon := DllCall(GetClassLong, "Ptr", hWnd, "Int", GCLP_HICONSM, "Ptr")
   Return smallIcon
}

AddTrayIcon(hIcon, tip := "")
{
   static NIF_MESSAGE := 1, NIF_ICON := 2, NIF_TIP := 4, NIM_ADD := 0
   flags := NIF_MESSAGE|NIF_ICON|(tip = "" ? 0 : NIF_TIP)
   VarSetCapacity(NOTIFYICONDATA, size := 396, 0)
   Gui, New, +hwndhGui
   NumPut(size         , NOTIFYICONDATA)
   NumPut(hGui         , NOTIFYICONDATA, A_PtrSize)
   NumPut(uID  := 0x404, NOTIFYICONDATA, A_PtrSize*2)
   NumPut(flags        , NOTIFYICONDATA, A_PtrSize*2 + 4)
   NumPut(nMsg := 0x404, NOTIFYICONDATA, A_PtrSize*2 + 8)
   NumPut(hIcon        , NOTIFYICONDATA, A_PtrSize*3 + 8)
   if (tip != "")
      StrPut(tip, &NOTIFYICONDATA + 4*A_PtrSize + 8, "CP0")
   DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_ADD, "Ptr", &NOTIFYICONDATA)
   Return hGui
}

RemoveTrayIcon(hWnd, uID := 0x404)
{
   VarSetCapacity(NOTIFYICONDATA, size := 24, 0)
   NumPut(size, NOTIFYICONDATA)
   NumPut(hWnd, NOTIFYICONDATA, A_PtrSize)
   NumPut(uID , NOTIFYICONDATA, A_PtrSize*2)
   DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_DELETE := 2, "Ptr", &NOTIFYICONDATA)
}

AHK_NOTIFYICON(wp, lp, msg, hwnd)   ; wp — uID, lp — Message
{
   static WM_LBUTTONDOWN := 0x201, WM_RBUTTONUP := 0x205, maxLenMenuStr := 40
   if !(lp = WM_LBUTTONDOWN || lp = WM_RBUTTONUP)
      Return
   
   for k, v in Windows
      if (v.gui = hwnd && window := k)
         break
   if !window
      Return
   
   Switch lp {
   Case WM_LBUTTONDOWN:
      RemoveTrayIcon(hwnd)
      Gui, %hwnd%: Destroy
      WinShow, ahk_id %window%
      WinActivate, ahk_id %window%
   Case WM_RBUTTONUP:
      title := Windows[window, "title"]
      b := StrLen(title) > maxLenMenuStr
      menuText := "Restore «" . SubStr(title, 1, maxLenMenuStr) . (b ? "..." : "") . "»"
      fn := Func(A_ThisFunc).Bind(0x404, WM_LBUTTONDOWN, 0x404, hwnd)
      Menu, IconMenu, Add , % menuText, % fn
      Menu, IconMenu, Icon, % menuText, % "HICON:*" . Windows[window, "hIcon"]
      Menu, IconMenu, Add , Show all windows, ShowHidden
      Menu, IconMenu, Show
      Menu, IconMenu, DeleteAll
   }
}

HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) {
   static OBJID_WINDOW := 0
   if !( idObject = OBJID_WINDOW && Windows.HasKey(hwnd) )
      Return
   iconGui := Windows[hwnd, "gui"]
   if WinExist("ahk_id" . iconGui) {
      RemoveTrayIcon(iconGui)
      Gui, %iconGui%: Destroy
   }
   for k, v in orderList {
      if (v = hwnd) {
         orderList.RemoveAt(k)
         break
      }
   }
   ( event = EVENT_OBJECT_DESTROY && Windows.Delete(hwnd) )
}

class WinEventHook
{  ; Event Constants: https://is.gd/tRT5Wr
   __New(eventMin, eventMax, hookProc, eventInfo := 0, idProcess := 0, idThread := 0, dwFlags := 0) {
      this.pCallback := RegisterCallback(hookProc, "F",, eventInfo)
      this.hHook := DllCall("SetWinEventHook", "UInt", eventMin, "UInt", eventMax, "Ptr", 0, "Ptr", this.pCallback
                                             , "UInt", idProcess, "UInt", idThread, "UInt", dwFlags, "Ptr")
   }
   __Delete() {
      DllCall("UnhookWinEvent", "Ptr", this.hHook)
      DllCall("GlobalFree", "Ptr", this.pCallback, "Ptr")
   }
}

Re: script help: minimize to icon in system tray

Posted: 23 Mar 2024, 04:08
by poopux
Thank you very much! I will give it a try and revert.

Re: script help: minimize to icon in system tray

Posted: 24 Mar 2024, 10:50
by poopux
works like a charm, thank you!!