Bagel Posted October 7, 2018 Posted October 7, 2018 Okay, I am trying to create a script that will allow me to toggle minimize/restore on a select group of open windows as shown in my taskbar. In poking around I was able to locate a script that showed ALL open windows in the taskbar and I've tried modifying it by paring that list down to only those windows whose parent process ID matches that of the program that spawned them. However, I haven't been able to get it to work. I'm quite new so I'm probably making an obvious mistake but I want to understand what's going on and haven't been able to get any further. Here is what I have so far: expandcollapse popup#Include <Array.au3> #Include <WinAPI.au3> #Include <WindowsConstants.au3> #include <MsgBoxConstants.au3> Local $aWinInTaskBar[1][2] Local $bMTparentWindows[1][2] $iCount = 0 $nCount = 0 $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]), 2 ) Then ; And only visible windows $iCount += 1 Redim $aWinInTaskBar[ $iCount + 1][2] $aWinInTaskBar[ $iCount][0] = $aList[$i][0] $aWinInTaskBar[ $iCount][1] = $aList[$i][1] ; MsgBox($MB_OK, "VALUE", "This is the value:", $aWinInTaskBar[$iCount][0]) EndIf Next For $n = 1 To $aWinInTaskBar[0][0] $nParentWindow = _WinAPI_GetParent($aWinInTaskBar[$n][1]) ; get the parent of each Window MsgBox($MB_OK, "VALUE", "This is the value:", & $aWinInTaskBar[$n][1]) If (WinGetProcess ($nParentWindow) == 9464) Then ; Search for windows whose parent ID equals main process ID $nCount += 1 Redim $bMTparentWindows[ $nCount + 1][2] $bMTparentWindows[ $nCount][0] = $aWinInTaskBar[$n][0] $bMTparentWindows[ $nCount][1] = $aWinInTaskBar[$n][1] EndIf Next $aWinInTaskBar[0][0] = UBound($aWinInTaskBar) - 1 $bMTparentWindows[0][0] = UBound($bMTparentWindows) - 1 _ArrayDisplay($aWinInTaskBar) _ArrayDisplay($bMTparentWindows) Now the first FOR loop correctly shows the list of opened windows in my task bar but my progress is complicated by the fact that I can't even get MsgBox to display items from the arrays for testing. The second FOR loop is my adaptation. It produces a box showing the @bMTparentWindows array but it's entirely devoid of any values. My thinking was that this would be a good first step to reaching my ultimate goal and I was planning on expanding it to include the toggle function and to combine the FOR loops and eliminate the usage of Redim. Is there is a simpler way? Why is this not working? Any help would be much appreciated!
mikell Posted October 8, 2018 Posted October 8, 2018 The last comma in both Msgbox is unwanted The 2nd For loop doesn't run because you define the $aWinInTaskBar[0][0] value after this loop I don't know exactly what you mean with "parent process" , WinGetProcess should be enough
Bagel Posted October 8, 2018 Author Posted October 8, 2018 12 hours ago, mikell said: The last comma in both Msgbox is unwanted The 2nd For loop doesn't run because you define the $aWinInTaskBar[0][0] value after this loop I don't know exactly what you mean with "parent process" , WinGetProcess should be enough Okay, I removed the extra comma there, thanks. I'm not sure what you mean though about $aWinInTaskBar[0][0] being defined after the second loop. My thinking was that $aWinInTaskBar[0][0] was defined on this line in the first FOR loop: 23 hours ago, Bagel said: $aWinInTaskBar[ $iCount][0] = $aList[$i][0] I had thought that $aWinInTaskBar was the array created by the first loop and my idea was to cycle through the elements in that array with the second loop as $n iterates from 1 to the end of the array. I then wanted to determine the process ID of the parent window of each item in the array so that I can compare it against the process ID of a particular program. The first loop produces a list of all the open windows on my taskbar but I need a subset of that list that returns only the windows associated with a particular program so that I can control just those windows. I don't know of any way to tell WinGetProcess to do that. Each window associated with the program I want to filter for has a different name, the names can change and some of the windows will have the same titles as other windows. So my idea was that every window associated with that program would have the same "parent" window so I went with _WinAPI_GetParent. I can confirm using Msgbox that indeed the second FOR loop never executes, I just don't understand how After the first FOR loop $aWinInTaskBar is definitely a populated array that can be displayed and so shouldn't it's value $aWinInTaskBar[0][0] already be defined? Thanks a lot for the feedback!
mikell Posted October 9, 2018 Posted October 9, 2018 Well what I meant is written in the comments in the code below BTW a process id can be different each time you launch it, so it's better to get this ID in the script expandcollapse popup#Include <Array.au3> #Include <WinAPI.au3> #Include <WindowsConstants.au3> #include <MsgBoxConstants.au3> $aList = WinList() Local $aWinInTaskBar[$aList[0][0]+1][2] ; <<<<<<<give an initial size to the array $iCount = 0 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]), 2 ) Then ; And only visible windows $iCount += 1 $aWinInTaskBar[ $iCount][0] = $aList[$i][0] $aWinInTaskBar[ $iCount][1] = $aList[$i][1] ; MsgBox($MB_OK, "VALUE", "This is the value: " & $aWinInTaskBar[$iCount][0]) EndIf Next Redim $aWinInTaskBar[ $iCount + 1][2] ;<<<<<<<<<<<<<<Redim only once $aWinInTaskBar[0][0] = UBound($aWinInTaskBar) - 1 ;<<<<<write the value at index 0 _ArrayDisplay($aWinInTaskBar) ; Then you can safely go for the 2nd For loop Local $bMTparentWindows[$aWinInTaskBar[0][0]+1][2] $nCount = 0 For $n = 1 To $aWinInTaskBar[0][0] ; MsgBox($MB_OK, "VALUE", "This is the value: " & $aWinInTaskBar[$n][1]) $PID = ProcessExists("firefox.exe") ;<<<<<<<<<<<<<<<get the PID of the process If $PID <> 0 Then If (WinGetProcess ($aWinInTaskBar[$n][1]) = $PID) Then $nCount += 1 $bMTparentWindows[ $nCount][0] = $aWinInTaskBar[$n][0] $bMTparentWindows[ $nCount][1] = $aWinInTaskBar[$n][1] EndIf EndIf Next Redim $bMTparentWindows[ $nCount + 1][2] $bMTparentWindows[0][0] = UBound($bMTparentWindows) - 1 _ArrayDisplay($bMTparentWindows)
Deye Posted October 9, 2018 Posted October 9, 2018 (edited) Hi Bagel, To refresh : You can control a group of windows by their class group * for example, list them into a comboBox with other additional action buttons as in close, restore , minimize .etc the rest is too easy .. #include <Array.au3> #include <WinAPISysWin.au3> #include <WindowsConstants.au3> Local $aWin = _WinAPI_EnumWindowsTop() $aWinU = _ArrayExtract($aWin, 2, _ArraySearch($aWin, "") - 2) For $i = UBound($aWinU) - 1 To 0 Step -1 If Not WinGetTitle($aWinU[$i][0], "") Then _ArrayDelete($aWinU, $i) Next If UBound($aWinU) - 1 < 3 Then Exit _ArrayDisplay($aWinU, "Desktop windows") $aGroups = _ArrayUnique($aWinU, 1) _ArrayDisplay($aGroups, "Choose a group") Deye Edited October 9, 2018 by Deye Typo
Bagel Posted October 14, 2018 Author Posted October 14, 2018 On 10/9/2018 at 5:47 AM, Deye said: Hi Bagel, To refresh : You can control a group of windows by their class group * for example, list them into a comboBox with other additional action buttons as in close, restore , minimize .etc the rest is too easy .. #include <Array.au3> #include <WinAPISysWin.au3> #include <WindowsConstants.au3> Local $aWin = _WinAPI_EnumWindowsTop() $aWinU = _ArrayExtract($aWin, 2, _ArraySearch($aWin, "") - 2) For $i = UBound($aWinU) - 1 To 0 Step -1 If Not WinGetTitle($aWinU[$i][0], "") Then _ArrayDelete($aWinU, $i) Next If UBound($aWinU) - 1 < 3 Then Exit _ArrayDisplay($aWinU, "Desktop windows") $aGroups = _ArrayUnique($aWinU, 1) _ArrayDisplay($aGroups, "Choose a group") Deye Okay, thanks! I really like this approach, it's very close to what I'm looking for and much faster than my other approach but now I need to extract one window from $aWinU whose title always contains two words. Here is my attempt but I haven't been able to get it to work and I don't know why. I also can't even get MsgBox to work here to troubleshoot and I can't figure that out either: #include <Array.au3> #include <WinAPISysWin.au3> #include <WindowsConstants.au3> #include <MsgBoxConstants.au3> Local $aWin = _WinAPI_EnumWindowsTop() ;WindowsForms10.Window.8.app.0.1ca0192_r6_ad1 $aWinU = _ArrayExtract($aWin, 2, _ArraySearch($aWin, "") - 2) For $i = UBound($aWinU) - 1 To 0 Step -1 If Not WinGetTitle($aWinU[$i][0], "") And WinGetTitle($aWinU[$i][0], "two words") Then _ArrayDelete($aWinU, $i) MsgBox($MB_OK, & $aWinU([$i][0]) Next If UBound($aWinU) - 1 < 3 Then Exit _ArrayDisplay($aWinU, "Desktop windows") $aGroups = _ArrayUnique($aWinU, 1) ;_ArrayDisplay($aGroups, "Choose a group")
Deye Posted October 14, 2018 Posted October 14, 2018 Here is an example where you can use StringRegExp to match any of words (two words|two|words|.etc) Local $aWin = _WinAPI_EnumWindowsTop() $aWinU = _ArrayExtract($aWin, 2, _ArraySearch($aWin, "") - 2) For $i = UBound($aWinU) - 1 To 0 Step -1 If Not WinGetTitle($aWinU[$i][0], "") Then _ArrayDelete($aWinU, $i) ElseIf StringRegExp(WinGetTitle($aWinU[$i][0], ""), "(?i)(Scite|AutoIt General Help and Support|.etc)", 0) Then MsgBox(0, "Deleting - " & $aWinU[$i][1], WinGetTitle($aWinU[$i][0], ""), 3) _ArrayDelete($aWinU, $i) EndIf Next If UBound($aWinU) < 3 Then Exit _ArrayDisplay($aWinU, "Desktop windows") Deye
Bagel Posted October 14, 2018 Author Posted October 14, 2018 Okay, it has occurred to me that there might be a way to make this faster. I thought it might be easier if we could extract all of the elements from $aWin that have a class that matches a specific string, then use the StringRegExp function to eliminate the one undesirable element. Here is my idea: #include <Array.au3> #include <WinAPISysWin.au3> #include <WindowsConstants.au3> #include <MsgBoxConstants.au3> Local $aWin = _WinAPI_EnumWindowsTop() Local $mtClass = "WindowsForms10.Window.8.app.0.1ca0192_r6_ad1" $aWinU = _ArrayExtract($aWin, 2, _ArraySearch($aWin, $mtClass) - 2) For $i = UBound($aWinU) - 1 To 0 Step -1 If Not WinGetTitle($aWinU[$i][0], "") Then _ArrayDelete($aWinU, $i) ElseIf StringRegExp(WinGetTitle($aWinU[$i][0], ""), "MT", 0) Then MsgBox(0, "Deleting - " & $aWinU[$i][1], WinGetTitle($aWinU[$i][0], ""), 3) _ArrayDelete($aWinU, $i) EndIf Next If UBound($aWinU) < 3 Then Exit _ArrayDisplay($aWinU, "Desktop windows") From what I understand about _ArraySearch I expect it to enter the string $mtClass as the matching criteria to test against every entry in $aWin creating an array in $aWinU that only has those class values. But it's not working and I can't see why. Thanks a lot for your help.
Deye Posted October 15, 2018 Posted October 15, 2018 In this example: Using _ArraySort will glue together all rows containing $mtClass Create a range that will be used with _ArrayDelete : With _ArraySearch find the first instance of $mtClass searching from the beginning of the array (from) , And (to) find the first instance reading from the end of the array #include <Array.au3> #include <WinAPISysWin.au3> Local $aWin = _WinAPI_EnumWindowsTop() Local $mtClass = "WindowsForms10.Window.8.app.0.1ca0192_r6_ad1" $aWinU = _ArrayExtract($aWin, 2, _ArraySearch($aWin, "") - 2) _ArraySort($aWinU, 0, 0, 0, 1) ; $iSubItem 1 Local $i = _ArraySearch($aWinU, $mtClass, Default, Default, Default, 2, Default, 1) ; searches the array from beginning to end If Not @error Then Local $byRange = $i & "-" & _ArraySearch($aWinU, $mtClass, Default, Default, Default, 2, 0, 1) ;searches the array from end to beginning _ArrayDisplay($aWinU, "Rows From\To " & $byRange & " will be gone") _ArrayDelete($aWinU, $byRange) EndIf _ArrayDisplay($aWinU, "Desktop windows")
Bagel Posted October 15, 2018 Author Posted October 15, 2018 Okay, thank you very much! Putting some of this together, this is what I came up with: Quote Local $aWin = _WinAPI_EnumWindowsTop() Local $mtClass = "WindowsForms10.Window.8.app.0.1ca0192_r6_ad1" $aWinU = _ArrayExtract($aWin, _ArraySearch($aWin, $mtClass, Default, Default, Default, 2, Default, Default), _ArraySearch($aWin, $mtClass, Default, Default, Default, 2, 0, Default)) ;_ArrayDisplay($aWinU, "Desktop windows") For $i = UBound($aWinU) - 1 To 0 Step -1 If StringRegExp(WinGetTitle($aWinU[$i][0], ""), "MT", 0) Then _ArrayDelete($aWinU, $i) EndIf Next ;_ArrayDisplay($aWinU, "Desktop windows") It seems on my system that I don't need to do any sorting as the windows I want are all grouped together already. Now for the next step, I need to loop through the final array and toggle between minimizing and restoring each window, or all of them simultaneously if possible. But I need to be able to do this quickly. The above code executes in about 3 tenths of a second but is there a way I can store and reuse this final array so that every time I trigger this toggling operation the above code doesn't re-execute?
Deye Posted October 16, 2018 Posted October 16, 2018 So in long short form this is all you are after ? #include <Array.au3> $aWin = WinList("[CLASS:WindowsForms10.Window.8.app.0.1ca0192_r6_ad1]") _ArrayDisplay($aWin) 1 hour ago, Bagel said: toggle between minimizing and restoring each window, or all of them simultaneously At this point I cannot tell because I never tried that maybe a better approach for you is to find out how to automate a taskbar group where you could toggle between minimizing .. all at once try looking here Deye
Bagel Posted October 16, 2018 Author Posted October 16, 2018 12 minutes ago, Deye said: So in long short form this is all you are after ? #include <Array.au3> $aWin = WinList("[CLASS:WindowsForms10.Window.8.app.0.1ca0192_r6_ad1]") _ArrayDisplay($aWin) At this point I cannot tell because I never tried that maybe a better approach for you is to find out how to automate a taskbar group where you could toggle between minimizing .. all at once try looking here Deye Unfortunately, that's all that I think I needed for this stage. But in the beginning because of my earlier attempt and reading about parent and child ID's ( I was trying to find a way to do it in Powershell and C# ) I thought I had to work with them and didn't know I could obtain fine-grained control by using the window class. At least I'm learning quite a bit more about AutoIT scripting. Thanks for the suggestion but what that link leads too is a bit too much overhead for my needs at this point. I'll start another topic about the rest. Thank you very much for your help! And thanks to mikell as well!
Deye Posted October 17, 2018 Posted October 17, 2018 (edited) @Bagel Had a spare moment to see what I can come up with .. #include <WinAPISysWin.au3> #include <Array.au3> $aWin = WinList("[CLASS:WindowsForms10.Window.8.app.0.1ca0192_r6_ad1]") $tRET = _WinAPI_GetWindowPlacement($aWin[1][1]) $retore = (DllStructGetData($tRET, "showCmd") = 2 ? 1 : 2) ; Toggle accordingly to the state of the top last active window of the group For $i = UBound($aWin) - 1 To 0 Step -1 $tRET = _WinAPI_GetWindowPlacement($aWin[$i][1]) DllStructSetData($tRET, "showCmd", $retore) _WinAPI_SetWindowPlacement($aWin[$i][1], $tRET) Next Quick enough for you, I hope Deye Edited October 18, 2018 by Deye changed to maintain windows Z order
Bagel Posted October 18, 2018 Author Posted October 18, 2018 Amazing! Not only is it very fast but it's working more consistently than what I had. I was using WinSetState but found that it wasn't grabbing the right windows, sometimes it would work and sometimes it wouldn't... Driving me nuts. Then found _WinAPI_EnumProcessWindows which was working slowly but more consistently and grabbing the proper windows. SetWindowPlacement seems to be both consistent and very fast. Only now I'm running into a problem where it will grab too many of the windows with that specified class so I'm looking into how I can pare the list down to the proper windows more efficiently. I'm thinking that a lot of functions in my previous codes that aren't WinAPI specific are causing the slowdowns but not sure how to refine the filter process for the windows using the WinAPI. I can locate some documentation on specific WinAPI functions (and haven't figured out quite yet how your code here works) but it's hard to get a broader picture of what kind of functions are out there that pertain to window elements that might allow me to make the selection part of the code more accurate. I continue working on it as I can. Thanks for another big piece of the puzzle!
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