WinGetListAlt() : Returns list of Alt+Tab windows

Post your working scripts, libraries and tools.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

WinGetListAlt() : Returns list of Alt+Tab windows

21 Jan 2022, 14:09

WinGetListAlt( WinTitle, WinText, ExcludeTitle, ExcludeText, ExcludeMinimized )
 
This is a wrapper function for WinGetList().
Results will be refined and contain only Alt+Tab qualified windows.
If the extra 5th parameter is True, then minimized windows will be excluded from results.

Usage example: Use Alt+MouseWheel instead of Alt-tab to switch between windows.
 
 
How task-switcher (Alt-tab) works?
(Note: The following is based on user-observation. This is a work-in-progress)
 
When Alt-tab is pressed.
 
Shows an UI with a list of top-level windows that meets following criteria:
The window
 
  • 1) Should be visible (WS_VISIBLE) and not cloaked (DWMWA_CLOAKED).
  • 2) Should allow activation (Not WS_EX_NOACTIVATE).
  • 3) Should not be a floating toolbar (WS_EX_TOOLWINDOW) unless it is also an App window (WS_EX_APPWINDOW).
  • 4) Should be an unowned window or an owned window with WS_EX_APPWINDOW style.
     
    or, if an owned window without WS_EX_APPWINDOW style, then
     
    • 1) Should be the first visible window of the owner-chain, and
    • 2) None of the preceding invisible owner windows should be a *floating toolbar. (WS_EX_TOOLWINDOW)
      *floating toolbar which is also an app window (WS_EX_APPWINDOW) is exempted.
 When Alt key is released.
 
  • 1) Finds the RootOwner handle of selected window by calling GetAncestor()
  • 2) Passes the RootOwner handle to *GetLastActivePopup() and
  • 3) Activates the window returned by it.
*GetLastActivePopup() will return
  • 1) The last active window in an owner chain. (or the first window if the chain never had an active one)
  • 2) The same window for an unowned window. (irrespective of whether it was active or not)
  
 
The function:. Tested on Window 7, 10, and 11
 

Code: Select all

WinGetListAlt(params*) ;                       v0.21 by SKAN for ah2 on D51K/D51O @ autohotkey.com/r?t=99157
{
    Static S_OK      :=  0

    Local  hModule   :=  DllCall("Kernel32\LoadLibrary", "str","dwmapi", "ptr")
        ,  List      :=  []
        ,  ExMin     :=  0
        ,  Style     :=  0
        ,  ExStyle   :=  0
        ,  hwnd      :=  0

    While params.Length > 4
          ExMin := params.pop()

    For ,  hwnd in WinGetList(params*)
       If  IsVisible(hwnd)
      and  StyledRight(hwnd)
      and  NotMinimized(hwnd)
      and  IsAltTabWindow(hwnd)
           List.Push(hwnd)

    DllCall("Kernel32\FreeLibrary", "ptr",hModule)
    Return List

    ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

                    IsVisible(hwnd, Cloaked:=0)
                    {
                        If S_OK  =   0
                           S_OK  :=  DllCall( "dwmapi\DwmGetWindowAttribute", "ptr",hwnd
                                            , "int",   14                   ; DWMWA_CLOAKED
                                            , "uintp", &Cloaked
                                            , "int",   4                    ; sizeof uint
                                            )

                        Style  :=  WinGetStyle(hwnd)
                        Return (Style & 0x10000000) and not Cloaked         ; WS_VISIBLE
                    }


                    StyledRight(hwnd)   
                    {
                        ExStyle := WinGetExStyle(hwnd)

                        Return (ExStyle & 0x8000000) ? False                ; WS_EX_NOACTIVATE
                             : (ExStyle & 0x40000)   ? True                 ; WS_EX_APPWINDOW
                             : (ExStyle & 0x80)      ? False                ; WS_EX_TOOLWINDOW
                                                     : True
                    }


                    NotMinimized(hwnd)
                    {
                        Return ExMin ? WinGetMinMax(hwnd) != -1 : True
                    }


                    IsAltTabWindow(Hwnd)
                    {

                        ExStyle  :=  WinGetExStyle(hwnd)
                        If  ( ExStyle  &  0x40000 )                         ; WS_EX_APPWINDOW
                              Return True

                        While  hwnd := DllCall("GetParent", "ptr",hwnd, "ptr")
                        {
                           If IsVisible(Hwnd)
                              Return False

                           ExStyle  :=  WinGetExStyle(hwnd)

                                If ( ExStyle  &  0x80 )                     ; WS_EX_TOOLWINDOW
                           and not ( ExStyle  &  0x40000 )                  ; WS_EX_APPWINDOW
                               Return False
                        }

                        Return !Hwnd
                    }
} ; ________________________________________________________________________________________________________
My Scripts and Functions: V1  V2
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Use Alt+MouseWheel instead of Alt-tab to switch between windows.

22 Jan 2022, 08:20

Requires WinGetListAlt()

Code: Select all

#Requires AutoHotkey v2.0-
#Warn
#SingleInstance


;-----------------------------------------------------------------------------------------------------------
; Taken from docs : https://www.autohotkey.com/docs/commands/Run.htm#RunAs

full_command_line := DllCall("GetCommandLine", "str")

if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
{
    try
    {
        if A_IsCompiled
            Run '*RunAs "' A_ScriptFullPath '" /restart'
        else
            Run '*RunAs "' A_AhkPath '" /restart "' A_ScriptFullPath '"'
    }
    ExitApp
}
;-----------------------------------------------------------------------------------------------------------

; Use Alt+MouseWheel instead on Alt-tab to switch between windows.
; The following would switch only between unminimized windows. Use WinGetListAlt() to get full control.

Hotkey("!WheelDown", (*) => AltMousewheel("Dn"))
Hotkey("!WheelUp",   (*) => AltMousewheel("Up"))

AltMousewheel(Option)
{
    Static Count    :=  0
         , Counter  :=  0
         , List     :=  []

    Local Hwnd

    If ( Option  =   "Clear" )
         Return ( List     :=  []
                , Count    :=  0
                , Counter  :=  0
                , Tooltip()
                )

    If ( Count = 0 )
         List     :=  WinGetListAlt(,,,,1)
       , Count    :=  List.Length
       , Counter  :=  0
 
    If not Count
       Return

    Counter  :=  Counter + ( Option = "Up" ? 1 :  -1 )
    Counter  :=  Counter > Count ? 1 : Counter < 1 ? Count : Counter


  , Hwnd  :=  DllCall("User32\GetAncestor", "ptr",List[Counter], "int",3, "ptr") ; GA_ROOTOWNER 3
  , Hwnd  :=  DllCall("User32\GetLastActivePopup", "ptr",Hwnd, "ptr")

    Try WinActivate(Hwnd)
    SetTimer((*) => AltMonitor(), 250)

                    AltMonitor(*)
                    {
                        Tooltip Counter "/" Count Chr(10) WinGetTitle(List[Counter])
                        If GetKeyState("Alt", "P") = False
                           SetTimer(, 0)
                         , SetTimer((*) => AltMousewheel("Clear"), -1)
                    }
}

;= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
User avatar
Delta Pythagorean
Posts: 627
Joined: 13 Feb 2017, 13:44
Location: Somewhere in the US
Contact:

Re: WinGetListAlt() : Returns list of Alt+Tab windows

23 Jan 2022, 00:19

Could there possibly be a way to cache the windows and do a check for new values or removed ones to save on memory?
...
What I mean is, on the first run of the function, it get's the full list of windows. The second run of the function, it only gets changes, such as if there's new windows or if previous windows are no longer there. The idea is to keep the amount of calls for finding all of the alt-tab windows to a minimum and possibly save on memory.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: WinGetListAlt() : Returns list of Alt+Tab windows

23 Jan 2022, 04:30

Delta Pythagorean wrote:
23 Jan 2022, 00:19
What I mean is, on the first run of the function, it get's the full list of windows. The second run of the function, it only gets changes, such as if there's new windows or if previous windows are no longer there. The idea is to keep the amount of calls for finding all of the alt-tab windows to a minimum and possibly save on memory.
:thumbup:

If you want maintain a window-list of task-bar , then "SHELLHOOK" exists specifically for that purpose.
Attempting the same with window-list of task-switcher would be error prone and also not worth of the effort put... IMO.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

WinGetListAlt() v0.19

23 Jan 2022, 07:08

WinGetListAlt() updated : v0.19
Example updated : Use Alt+MouseWheel instead of Alt-tab to switch between windows.
Added explanation to original post: How task-switcher (Alt-tab) works?
  
Notes:
 
Previous WinGetListAlt() was using an altered version of IsAltTabWindow() by Raymond Chen,
posted @ Which windows appear in the Alt+Tab list?
 

Code: Select all

BOOL IsAltTabWindow(HWND hwnd)
{
 // Start at the root owner
 HWND hwndWalk = GetAncestor(hwnd, GA_ROOTOWNER);
 // See if we are the last active visible popup
 HWND hwndTry;
 while ((hwndTry = GetLastActivePopup(hwndWalk)) != hwndTry) {
  if (IsWindowVisible(hwndTry)) break;
  hwndWalk = hwndTry;
 }
 return hwndWalk == hwnd;
}
 
The approach in above code is flawed.
 
GetLastActivePopup() will pick the active visible window in a owner-chain of windows
whereas, task-switcher will always pick the first visible window in such a chain.
 
Example 1 :
 
In following owner-chained windows, task-switcher will select the first visible window: "Window One"
whereas IsAltTabWindow() will pick the active window, which is "Window three"
 
Image
 

Code: Select all

#Requires AutoHotkey v2.0-
#Warn
#SingleInstance

MyGui1 := Gui("-MinimizeBox","Window One")
MyGui2 := Gui("-MinimizeBox Owner" MyGui1.Hwnd, "Window Two")
MyGui3 := Gui("-MinimizeBox Owner" MyGui2.Hwnd, "Window Three")
MyGui4 := Gui("-MinimizeBox Owner" MyGui3.Hwnd, "Window Four")
MyGui5 := Gui("-MinimizeBox Owner" MyGui3.Hwnd, "Window Five")

MyGui1.Show("x100 y100 w200 h100 NA")
MyGui2.Show("x140 y140 w200 h100 NA")
MyGui3.Show("x180 y180 w200 h100   ")
MyGui4.Show("x220 y220 w200 h100 NA")
MyGui5.Show("x260 y260 w200 h100 NA")
 
 
Example 2 :
 
IsAltTabWindow() wouldn't consider the following window as a task-switcher item, as the function presumes owner will always be visible.
... and again: task-switcher will always pick the first visible window in an owner-chain.
 
Image
 

Code: Select all

MyGui := Gui("-MinimizeBox +Owner", "Owned by a hidden window") ; Owner will be A_ScriptHwnd
MyGui.Show("w200 h200")
 
Conclusion :
 
GetLastActivePopup() should not be used to enumerate Task-switcher items.
GetLastActivePopup() could/should be used while activating a task-switcher qualified window.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

WinGetListAlt() : v0.21

24 Jan 2022, 12:06

WinGetListAlt() updated : v0.21
Example updated : Use Alt+MouseWheel instead of Alt-tab to switch between windows.
Updated explanation in original post: How task-switcher (Alt-tab) works?
 
User avatar
Delta Pythagorean
Posts: 627
Joined: 13 Feb 2017, 13:44
Location: Somewhere in the US
Contact:

Re: WinGetListAlt() : Returns list of Alt+Tab windows

24 Jan 2022, 13:43

SKAN wrote:
23 Jan 2022, 04:30
If you want maintain a window-list of task-bar , then "SHELLHOOK" exists specifically for that purpose.
Attempting the same with window-list of task-switcher would be error prone and also not worth of the effort put... IMO.
Touché.
Also I completely forgot about SHELLHOOK lol.

[AHK]......: v2.0.12 | 64-bit
[OS].......: Windows 11 | 23H2 (OS Build: 22621.3296)
[GITHUB]...: github.com/DelPyth
[PAYPAL]...: paypal.me/DelPyth
[DISCORD]..: tophatcat

iPhilip
Posts: 814
Joined: 02 Oct 2013, 12:21

Re: WinGetListAlt() : Returns list of Alt+Tab windows

17 Sep 2022, 02:58

Hi @SKAN,

I did some testing of WinGetListAlt() on my computer and would like to report some results with Microsoft Teams windows:

  • If the "On close, keep the application running" settings option is checked under Microsoft Teams Settings:General, a handle to the window is included in the list returned by WinGetListAlt(), regardless of whether the window is visible or not. In contrast, the Alt+Tab menu only shows the window when it's visible.
  • If the settings option is unckeched, WinGetListAlt() only includes a handle to the window when it's visible (same as the Alt+Tab menu).
I hope this helps you further debug the function.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: WinGetListAlt() : Returns list of Alt+Tab windows

14 Oct 2023, 07:59

SKAN, I think Your algorithm is not right.
From my test it should be like this.
Code for ahk v1, I am too lazy to convert it to ahk v2.

Code: Select all

arr := EnumerateAltTabWindows()
for k, v in arr {
   WinGetClass, winClass, ahk_id %v%
   WinGetTitle, title, ahk_id %v%
   altTabWindows .= A_Index "`nTitle: " title "`nClass: " winClass "`nhWnd: " v "`nLastActivePopup: " format("0x{:x}", GetLastActivePopup(v)) "`n`n"
}
MsgBox, % altTabWindows


EnumerateAltTabWindows() {
   AltTabList := []
   WinGet, list, List
   Loop % list
      if IsAltTabWindow(list%A_Index%)
         AltTabList.Push(list%A_Index%)
   Return AltTabList
}

IsAltTabWindow(hWnd)
{
   static WS_EX_APPWINDOW := 0x40000, WS_EX_TOOLWINDOW := 0x80, WS_EX_NOACTIVATE := 0x8000000, DWMWA_CLOAKED := 14, GA_PARENT := 1, GW_OWNER := 4
   if !DllCall("IsWindowVisible", "uptr", hWnd)
      return
   DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hWnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
   if cloaked
      return
   if (realHwnd(DllCall("GetAncestor", "uptr", hwnd, "uint", GA_PARENT, "ptr")) != realHwnd(DllCall("GetDesktopWindow", "ptr")))
      return
   WinGet, exStyles, ExStyle, ahk_id %hWnd%
   if (exStyles & WS_EX_APPWINDOW)
      return true
   if (exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)
      return
   loop
   {
      hwnd := DllCall("GetWindow", "uptr", hwnd, "uint", GW_OWNER, "ptr")
      if !hwnd
         return true
      if DllCall("IsWindowVisible", "uptr", hwnd)
      {
         DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hwnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
         if !cloaked
            return
      }
      WinGet, exStyles, ExStyle, ahk_id %hwnd%
      if ((exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)) and !(exStyles & WS_EX_APPWINDOW)
         return
   }
}

GetLastActivePopup(hwnd)
{
   static GA_ROOTOWNER := 3
   hwnd := DllCall("GetAncestor", "uptr", hwnd, "uint", GA_ROOTOWNER, "ptr")
   hwnd := DllCall("GetLastActivePopup", "uptr", hwnd, "ptr")
   return hwnd
}

realHwnd(hwnd)
{
   varsetcapacity(var, 8, 0)
   numput(hwnd, var, 0, "uint64")
   return numget(var, 0, "uint")
}
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: WinGetListAlt() : Returns list of Alt+Tab windows

14 Oct 2023, 23:34

@malcev this is the one I use: maybe the logic can be combined, but it's hard to say what "works"

Code: Select all

AltTabWindows() { ; modernized, original by ophthalmos https://www.autohotkey.com/boards/viewtopic.php?t=13288
   static WS_EX_APPWINDOW :=      0x40000 ; has a taskbar button
   static WS_EX_TOOLWINDOW :=        0x80 ; does not appear on the Alt-Tab list
   static GW_OWNER :=                   4 ; identifies as the owner window

   ; Get the current monitor the mouse cusor is in.
   DllCall("GetCursorPos", "uint64*", &point:=0)
   hMonitor := DllCall("MonitorFromPoint", "uint64", point, "uint", 0x2, "ptr")

   AltTabList := []

   DetectHiddenWindows False     ; makes IsWindowVisible and DWMWA_CLOAKED unnecessary in subsequent call to WinGetList()
   for hwnd in WinGetList() {    ; gather a list of running programs

      ; Check if the window is on the same monitor.
      if hMonitor == DllCall("MonitorFromWindow", "ptr", hwnd, "uint", 0x2, "ptr") {

         ; Find the top-most owner of the child window.
         owner := DllCall("GetAncestor", "ptr", hwnd, "uint", GA_ROOTOWNER := 3, "ptr") 
         owner := owner || hwnd ; Above call could be zero.

         ; Check to make sure that the active window is also the owner window.
         if (DllCall("GetLastActivePopup", "ptr", owner) = hwnd) {

            ; Get window extended style.
            es := WinGetExStyle(hwnd)

            ; Must appear on the Alt+Tab list, have a taskbar button, and not be a Windows 10 background app.
            if (!(es & WS_EX_TOOLWINDOW) || (es & WS_EX_APPWINDOW))
               AltTabList.push(hwnd)
         }
      }
   }

   return AltTabList
}
100% the monitor the window is on must be taken into account.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: WinGetListAlt() : Returns list of Alt+Tab windows

16 Oct 2023, 07:55

Why? At-tab does not differ monitors.
Also at-tab not always hides cloaked windows.
I am 100% sure that Your code is not right.
Also I thought about virtual displays and some other events and created topic in ahk v1.
viewtopic.php?f=6&t=122399

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: No registered users and 27 guests