sylremo Posted April 25, 2023 Posted April 25, 2023 (edited) I'm creating an alternative to Windows' alt-tab menu, and I'm encountering issues with WinList as it DOES NOT provide the correct order of recently opened or activated windows. While WinList normally respects the z-order, this is not the case when topmost windows are present (mentioned here). Quote The system maintains the z-order in a single list. It adds windows to the z-order based on whether they are topmost windows, top-level windows, or child windows. A topmost window overlaps all other non-topmost windows, regardless of whether it is the active or foreground window. A topmost window has the WS_EX_TOPMOST style. All topmost windows appear in the z-order before any non-topmost windows. A child window is grouped with its parent in z-order. Here is an example showing what I mean: #include <WinAPI.au3> #include <WindowsConstants.au3> Run('notepad.exe') Run('calc.exe') Local $sNotepadTitle = 'Untitled - Notepad' Local $sCalculatorTitle = 'Calculator' WinWait($sNotepadTitle) WinWait($sCalculatorTitle) WinSetOnTop($sCalculatorTitle, '', $WINDOWS_ONTOP) WinActivate($sNotepadTitle) ConsoleWrite('Active window: ' & WinGetTitle('[ACTIVE]') & @CRLF) ConsoleWrite('First visible window from WinList: ' & getTopWindow() & @CRLF) Func getTopWindow() Local $aList = WinList() For $i = 1 To $aList[0][0] $iExStyle = _WinAPI_GetWindowLong($aList[$i][1], $GWL_EXSTYLE) ; get the extended style of each Window If Not BitAND($iExStyle, $WS_EX_TOOLWINDOW) And _ ; Search for windows without $WS_EX_TOOLWINDOW extended style BitAND(WinGetState($aList[$i][1]), $WIN_STATE_VISIBLE) Then ; And only visible windows Return $aList[$i][0] EndIf Next EndFunc Explain: Activating Notepad should precede Calculator in WinList. However, due to $WS_EX_TOPMOST, Calculator takes precedence over any non-$WS_EX_TOPMOST windows in the WinList array. I've tried _WinAPI_EnumWindows, _WinAPI_EnumWindowsTop, both yield the same result as WinList. Are there any other APIs or methods to retrieve a list of recently activated windows, regardless of topmost windows (similar to Windows' alt-tab menu)? Edited April 26, 2023 by sylremo clarify
pixelsearch Posted April 25, 2023 Posted April 25, 2023 I got a script named "378 - List of Alt-tabbable windows.au3" based on code found in the commented links at the end of the script.expandcollapse popup #include <ComboConstants.au3> #include <GuiConstants.au3> #include <WinAPISysWin.au3> Opt('MustDeclareVars', 1) GUICreate("List of Alt-tabbable windows", 390, 250) Local $idCombo = GUICtrlCreateCombo("", 20, 60, 350, 100, $CBS_DROPDOWNLIST) Local $idButton = GUICtrlCreateButton("Create list", 150, 150, 85, 20) GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop Case $idButton Local $aWin = WinList() Local $bVisible, $bToolWindow, $sComboStrings = '' GUICtrlSetData($idCombo, '') For $i = 1 To $aWin[0][0] $bVisible = BitAND(WinGetState($aWin[$i][1]), 2) ; $WIN_STATE_VISIBLE (2) = Window is visible $bToolWindow = BitAND(_WinAPI_GetWindowLong($aWin[$i][1], $GWL_EXSTYLE), $WS_EX_TOOLWINDOW) If $bVisible And Not $bToolWindow Then $sComboStrings &= $aWin[$i][0] & '|' EndIf Next GUICtrlSetData($idCombo, $sComboStrings) ; or StringTrimRight($sComboStrings, 1)) to remove last "|" (same visual effect) GUICtrlSendMsg($idCombo, $CB_SHOWDROPDOWN, True, 0) ; wparam : True to show, False to hide GUICtrlSendMsg($idCombo, $CB_SETCURSEL, 0, 0) ; wparam : zero-based index of the string to select EndSwitch WEnd ; https://www.autoitscript.com/forum/topic/90149-solved-get-all-windows/?do=findComment&comment=649609 ; https://www.autoitscript.com/forum/topic/90149-solved-get-all-windows/?do=findComment&comment=650710 ; Personal (reworked a bit the variable names, get rid of Func GetWindowLong etc...) ; https://www.autoitscript.com/forum/topic/90149-solved-get-all-windows/?do=findComment&comment=648698 ; Ascend4nt : "Definitely a neat technique to getting the 'alt-tabbable' windows! Kudos" Hope it helps "I think you are searching a bug where there is no bug... don't listen to bad advice."
sylremo Posted April 25, 2023 Author Posted April 25, 2023 (edited) @pixelsearch, thank you for your time. The thing that I'm trying to achieve is the last activation order when topmost window(s) exist. The code that you provided is actually the same as mine getTopWindow() without returning only the first one. As you can see, if I clicked on "Create list" button on the GUI, the autoit window should be the current activated window, but in this case, Calculator, which is topmost window, still on top of the list instead of the autoit window. I still need to get the recently activated window list. Edited April 25, 2023 by sylremo
Nine Posted April 25, 2023 Posted April 25, 2023 I do not believe that there is an API that can give you what you want "on the spot". My guess is that Windows keeps track of a list of last active windows dynamically. But you could mimic Windows with _WinAPI_RegisterShellHookWindow. On activation you put the window on top of your list, on closure you remove the window from the list. I quickly tested it with your script and it seems to work fine. But you would only get a list of windows after you have started your script. Of course you could start it at logon and create an IPC to provide list to other programs. Some work has to be done, but all depends how important it is for you. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
pixelsearch Posted April 25, 2023 Posted April 25, 2023 The script below should do it. It will ignore the Scite Window and the Gui window (just to display a clearer list) Now the 1st line displayed in the combo box will be the last active window, even if it's not a Topmost one.expandcollapse popup #include <ComboConstants.au3> #include <GuiConstants.au3> #include <WinAPISysWin.au3> Opt('MustDeclareVars', 1) Local $hWin, $hWin_old, $hWin_temp, $hScite = WinGetHandle(@ScriptFullPath) ; ConsoleWrite($hScite & @crlf) Local $hGUI = GUICreate("List of Alt-tabbable windows (with active 1st)", 390, 250) Local $idCombo = GUICtrlCreateCombo("", 20, 60, 350, 100, $CBS_DROPDOWNLIST) Local $idButton = GUICtrlCreateButton("Create list", 150, 150, 85, 20) GUISetState() While 1 $hWin_temp = WinGetHandle("[active]") If $hWin_temp <> $hGUI And $hWin_temp <> $hScite Then $hWin = $hWin_temp If $hWin <> $hWin_old Then ConsoleWrite($hWin & " " & _WinAPI_GetWindowText($hWin) & @crlf) $hWin_old = $hWin EndIf EndIf Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop Case $idButton Local $aWin = WinList() Local $bVisible, $bToolWindow, $sComboStrings = '' GUICtrlSetData($idCombo, '') For $i = 1 To $aWin[0][0] $bVisible = BitAND(WinGetState($aWin[$i][1]), 2) ; $WIN_STATE_VISIBLE (2) = Window is visible $bToolWindow = BitAND(_WinAPI_GetWindowLong($aWin[$i][1], $GWL_EXSTYLE), $WS_EX_TOOLWINDOW) If $bVisible And Not $bToolWindow Then If $aWin[$i][1] <> $hGUI And $aWin[$i][1] <> $hScite Then If $aWin[$i][1] <> $hWin Then $sComboStrings &= $aWin[$i][0] & '|' Else ; Active window at 1st place (even if not topmost) $sComboStrings = $aWin[$i][0] & '|' & $sComboStrings EndIf EndIf EndIf Next GUICtrlSetData($idCombo, $sComboStrings) ; or StringTrimRight($sComboStrings, 1)) to remove last "|" (same visual effect) GUICtrlSendMsg($idCombo, $CB_SHOWDROPDOWN, True, 0) ; wparam : True to show, False to hide GUICtrlSendMsg($idCombo, $CB_SETCURSEL, 0, 0) ; wparam : zero-based index of the string to select EndSwitch WEnd "I think you are searching a bug where there is no bug... don't listen to bad advice."
Nine Posted April 25, 2023 Posted April 25, 2023 No you dont understand -- WinList is useless. OP does not want z-order as other APIs do. OP wants the last activated windows, which no API will give. Plz take a moment to run the script he gave us and then do Alt-tab, you will see it is different than the z-order of WinList. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
sylremo Posted April 26, 2023 Author Posted April 26, 2023 6 hours ago, Nine said: But you could mimic Windows with _WinAPI_RegisterShellHookWindow Hi @Nine, Thank you for sharing your thoughts on how to keep track of active windows dynamically in Windows. Your suggestion to use _WinAPI_RegisterShellHookWindow seems like a viable option. Would you be able to provide some more details on how to implement it? It would be greatly appreciated. Thank you!
sylremo Posted April 26, 2023 Author Posted April 26, 2023 I reviewed _WinAPI_RegisterShellHookWindow in the help file and was able to detect window activations. However, I could not find a method to detect window closures.
Nine Posted April 26, 2023 Posted April 26, 2023 (edited) As per MSDN : Quote HSHELL_WINDOWDESTROYED A handle to the top-level window being destroyed. See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registershellhookwindow Here my simple way to detect windows : #include <GUIConstants.au3> #include <APISysConstants.au3> #include <WinAPISysWin.au3> #include <Constants.au3> Global $hForm = GUICreate('') GUIRegisterMsg(_WinAPI_RegisterWindowMessage('SHELLHOOK'), WM_SHELLHOOK) _WinAPI_RegisterShellHookWindow($hForm) OnAutoItExitRegister(OnAutoItExit) Run('notepad.exe') Run('calc.exe') Local $sNotepadTitle = '[CLASS:Notepad]' Local $sCalculatorTitle = 'Calculatrice' WinWait($sNotepadTitle) WinWait($sCalculatorTitle) WinSetOnTop($sCalculatorTitle, '', $WINDOWS_ONTOP) WinActivate($sNotepadTitle) Sleep(1000) Func WM_SHELLHOOK($hWnd, $iMsg, $wParam, $lParam) Switch $wParam Case $HSHELL_WINDOWACTIVATED, $HSHELL_WINDOWCREATED, $HSHELL_RUDEAPPACTIVATED ConsoleWrite($lParam & "/" & WinGetTitle($lParam) & " has been activated" & @CRLF) Case $HSHELL_WINDOWDESTROYED ConsoleWrite($lParam & "/" & WinGetTitle($lParam) & " has been closed" & @CRLF) EndSwitch EndFunc ;==>WM_SHELLHOOK Func OnAutoItExit() _WinAPI_DeregisterShellHookWindow($hForm) EndFunc ;==>OnAutoItExit Edited April 26, 2023 by Nine sylremo and ioa747 2 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now