Return list of Alt-Tab windows win10+

Post your working scripts, libraries and tools for AHK v1.1 and older
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Return list of Alt-Tab windows win10+

16 Oct 2023, 20:26

Thanks to @teadrinker who found method in non-documented api We can get alt-tab windows more reliable.

Code: Select all

DetectHiddenWindows On
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 ImmersiveShell, IApplicationViewCollection, MONITOR_DEFAULTTONULL := 0, VirtualDesktopAltTabFilter := "null", PropEnumProcEx := RegisterCallback("PropEnumProcEx", "Fast", 4)
   if (VirtualDesktopAltTabFilter = "null")
   {
      RegRead, VirtualDesktopAltTabFilter, HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced, VirtualDesktopAltTabFilter
      OSbuildNumber := StrSplit(A_OSVersion, ".")[3]
      if (OSbuildNumber < 14393)
      {
         msgbox Your %A_OSVersion% can not handle virtual desktops
         exitapp
      }
      else if (OSbuildNumber <= 17134)   ; Windows 10 1607 to 1803 and Windows Server 2016
         IID_IApplicationViewCollection := "{2C08ADF0-A386-4B35-9250-0FE183476FCC}"
      else
         IID_IApplicationViewCollection := "{1841C6D7-4F9D-42C0-AF41-8747538F10E5}"
      if !(ImmersiveShell := ComObjCreate(CLSID_ImmersiveShell := "{C2F03A33-21F5-47FA-B4BB-156362A2F239}", IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"))
      {
         MsgBox ImmersiveShell not supported.
         ExitApp
      }
      if !(IApplicationViewCollection := ComObjQuery(ImmersiveShell, IID_IApplicationViewCollection, IID_IApplicationViewCollection))
      {
         MsgBox IApplicationViewCollection interface not supported.
         ExitApp	
      }
   }
   WinGetClass, winClass, ahk_id %hWnd%
   if (winClass = "Windows.UI.Core.CoreWindow")
      return
   if (winClass = "ApplicationFrameWindow")
   {
      varsetcapacity(ApplicationViewCloakType, 4, 0)
      DllCall("EnumPropsEx", "uptr", hWnd, "ptr", PropEnumProcEx, "ptr", &ApplicationViewCloakType)
      if (numget(ApplicationViewCloakType, 0, "int") = 1)   ; https://github.com/kvakulo/Switcheroo/commit/fa526606d52d5ba066ba0b2b5aa83ed04741390f
         return
   }
   ; if !DllCall("MonitorFromWindow", "uptr", hwnd, "uint", MONITOR_DEFAULTTONULL, "ptr")   ; test if window is shown on any monitor. alt-tab shows any window even if window is out of monitor.
   ;   return
   DllCall(NumGet(NumGet(IApplicationViewCollection+0)+6*A_PtrSize), "ptr", IApplicationViewCollection, "uptr", hwnd, "ptr*", pView)   ; GetViewForHwnd
   if pView
   {
      DllCall(NumGet(NumGet(pView+0)+27*A_PtrSize), "ptr", pView, "int*", ShowInSwitchers)   ; GetShowInSwitchers
      ObjRelease(pView)
   }
   if ShowInSwitchers and ((VirtualDesktopAltTabFilter = 0) or IsWindowOnCurrentVirtualDesktop(hwnd))
      return true
   return
}

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

IsWindowOnCurrentVirtualDesktop(hwnd)
{
   static IVirtualDesktopManager
   if !IVirtualDesktopManager
      IVirtualDesktopManager := ComObjCreate(CLSID_VirtualDesktopManager := "{AA509086-5CA9-4C25-8F95-589D3C07B48A}", IID_IVirtualDesktopManager := "{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}")
   DllCall(NumGet(NumGet(IVirtualDesktopManager+0), 3*A_PtrSize), "ptr", IVirtualDesktopManager, "uptr", hwnd, "int*", onCurrentDesktop)   ; IsWindowOnCurrentVirtualDesktop
   return onCurrentDesktop
}

PropEnumProcEx(hWnd, lpszString, hData, dwData)
{
   if (strget(lpszString, "UTF-16") = "ApplicationViewCloakType")
   {
      numput(hData, dwData+0, 0, "int")
      return false
   }
   return true
}
The next code shows alt-tab windows without not-documented api (it can show windows only on active virtual display):

Code: Select all

DetectHiddenWindows On
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, DWMWA_CLOAKED := 14, DWM_CLOAKED_SHELL := 2, WS_EX_NOACTIVATE := 0x8000000, GA_PARENT := 1, GW_OWNER := 4, MONITOR_DEFAULTTONULL := 0, VirtualDesktopExist, PropEnumProcEx := RegisterCallback("PropEnumProcEx", "Fast", 4)
   if (VirtualDesktopExist = "")
   {
      OSbuildNumber := StrSplit(A_OSVersion, ".")[3]
      if (OSbuildNumber < 14393)
         VirtualDesktopExist := 0
      else
         VirtualDesktopExist := 1
   }
   if !DllCall("IsWindowVisible", "uptr", hWnd)
      return
   DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hWnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
   if (cloaked = DWM_CLOAKED_SHELL)
      return
   if (realHwnd(DllCall("GetAncestor", "uptr", hwnd, "uint", GA_PARENT, "ptr")) != realHwnd(DllCall("GetDesktopWindow", "ptr")))
      return
   WinGetClass, winClass, ahk_id %hWnd%
   if (winClass = "Windows.UI.Core.CoreWindow")
      return
   if (winClass = "ApplicationFrameWindow")
   {
      varsetcapacity(ApplicationViewCloakType, 4, 0)
      DllCall("EnumPropsEx", "uptr", hWnd, "ptr", PropEnumProcEx, "ptr", &ApplicationViewCloakType)
      if (numget(ApplicationViewCloakType, 0, "int") = 1)   ; https://github.com/kvakulo/Switcheroo/commit/fa526606d52d5ba066ba0b2b5aa83ed04741390f
         return
   }
   ; if !DllCall("MonitorFromWindow", "uptr", hwnd, "uint", MONITOR_DEFAULTTONULL, "ptr")   ; test if window is shown on any monitor. alt-tab shows any window even if window is out of monitor.
   ;   return
   WinGet, exStyles, ExStyle, ahk_id %hWnd%
   if (exStyles & WS_EX_APPWINDOW)
   {
      if DllCall("GetProp", "uptr", hWnd, "str", "ITaskList_Deleted", "ptr")
         return
      if (VirtualDesktopExist = 0) or IsWindowOnCurrentVirtualDesktop(hwnd)
         return true
      else
         return
   }
   if (exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)
      return
   loop
   {
      hwndPrev := hwnd
      hwnd := DllCall("GetWindow", "uptr", hwnd, "uint", GW_OWNER, "ptr")
      if !hwnd
      {
         if DllCall("GetProp", "uptr", hwndPrev, "str", "ITaskList_Deleted", "ptr")
            return
         if (VirtualDesktopExist = 0) or IsWindowOnCurrentVirtualDesktop(hwndPrev)
            return true
         else
            return
      }
      if DllCall("IsWindowVisible", "uptr", hwnd)
         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
}

IsWindowOnCurrentVirtualDesktop(hwnd)
{
   static IVirtualDesktopManager
   if !IVirtualDesktopManager
      IVirtualDesktopManager := ComObjCreate(CLSID_VirtualDesktopManager := "{AA509086-5CA9-4C25-8F95-589D3C07B48A}", IID_IVirtualDesktopManager := "{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}")
   DllCall(NumGet(NumGet(IVirtualDesktopManager+0), 3*A_PtrSize), "ptr", IVirtualDesktopManager, "uptr", hwnd, "int*", onCurrentDesktop)   ; IsWindowOnCurrentVirtualDesktop
   return onCurrentDesktop
}

PropEnumProcEx(hWnd, lpszString, hData, dwData)
{
   if (strget(lpszString, "UTF-16") = "ApplicationViewCloakType")
   {
      numput(hData, dwData+0, 0, "int")
      return false
   }
   return true
}

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

Re: Return list of Alt-Tab windows win10+

16 Oct 2023, 20:47

Thank you. My old version was unreliable, I will test with this one and see if there are any bugs.
iseahound
Posts: 1456
Joined: 13 Aug 2016, 21:04
Contact:

Re: Return list of Alt-Tab windows win10+

16 Oct 2023, 22:18

I can't get a v2 translation working... any ideas why? It always fails at this line:

Code: Select all

; int GetViewForHwnd(IntPtr hwnd, out IntPtr view);
ComCall(6, IApplicationViewCollection, "ptr", hwnd, "ptr*", pView:=0)   ; GetViewForHwnd
The return value is 0 on success, and throws on failure. Adding try to the beginning isolates the successful cases where the return value is 0. However, even when successful, the value of pView is never set. Since pView is always zero, the second comcall to GetShowInSwitchers fails.
Spoiler
ntepa
Posts: 434
Joined: 19 Oct 2022, 20:52

Re: Return list of Alt-Tab windows win10+

16 Oct 2023, 23:26

try ComCall(6, IApplicationViewCollection, "ptr", hwnd, "ptr*", &pView:=0)
teadrinker
Posts: 4370
Joined: 29 Mar 2015, 09:41
Contact:

Re: Return list of Alt-Tab windows win10+

17 Oct 2023, 05:32

Better: ComCall(GetViewForHwnd := 6, IApplicationViewCollection, 'Ptr', hwnd, 'PtrP', IApplicationView := ComValue(VT_UNKNOWN := 13, 0))
iseahound
Posts: 1456
Joined: 13 Aug 2016, 21:04
Contact:

Re: Return list of Alt-Tab windows win10+

17 Oct 2023, 11:24

@ntepa
I can't believe I forgot that!

@teadrinker
What does casting to VT_UNKNOWN do? Does it call query interface on the pointer (and incrementing ref count) before wrapping it into a com object?
teadrinker
Posts: 4370
Joined: 29 Mar 2015, 09:41
Contact:

Re: Return list of Alt-Tab windows win10+

17 Oct 2023, 11:53

This is not casting, it is wrapping. The GetViewForHwnd method returns a raw pointer to an instance of a class that implements the IApplicationView interface, which inherits the IUnknown interface. And the

Code: Select all

ComValue(VT_UNKNOWN := 13, 0)
code just immediately wraps this raw pointer into a ComValue object, after which you don't need to worry about reference counting.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: whoops and 118 guests