Mbee Posted October 25, 2020 Share Posted October 25, 2020 Hello, wise and my ever-so-kind advisers! I have two monitors, and one of them is my HDTV. Sometimes one of them will be dedicated to something I don't want interrupted, such as when I'm watching TV and working on the computer at the same time (for one example). So I want to "block" one of the monitors from new windows. So I'm developing a script that will detect when a new application -or- popup window has been opened on the blocked monitor, and automatically move it to the non-blocked one. I've spent weeks trying to figure out how to distinguish a main application window from every other type of window, and after reading on sites like Stack Overflow I've learned that this is nearly impossible, and it would take an expert in Windows 10 internals to even attempt it. This is fortunate, because I've thought of a new and much simpler approach: Simply assume that all the windows and pop-ups currently displayed on the blocked monitor are deliberately left there, and then watch for any new windows or pop-ups that open there. And instead of trying to determine if the new windows are main application windows, I'll look to see if it's ancestors include explorer.exe and if so, assume it's an app window. In the case where it's not a descendant of explorer, I'll traverse all the windows associated with the same PID and look for the window with the largest area and assume that's an application window. In comparison, pop-ups are much easier. All I have to do is to first build a record of all the current windows and pop-ups on the blocked monitor, then keep looping and checking to see if there are any windows in subsequent loops that were not in the previous list. I was initially going to use arrays, but then the issue of fast comparisons came up, so I looked at all the various threads on the subject and came away convinced that the fastest and simplest was to use scripting dictionary objects. The only problem is that the documentation of them that I've been able to find are, shall we say, inadequate -- and quite confusing (at least to dummies like me). So I thought I'd post the main part of the code that uses them and ask for corrections and suggested alternatives. Note that I didn't feel it at all necessary to post the entire thing with all the functions and declarations, therefore it won't run as is. But since I'm not asking for troubleshooting and instead just advice, I don't believe a runnable script was necessary. So here we go! expandcollapse popup$G_MoveTblCount = 0 $G_NumPrevWins = 0 $G_NumPrevPopups = 0 While True Global $G_CurWinAra = _WinAPI_EnumWindows( True, Default ) ; Get list of ALL visible windows $G_NumCurWins = $G_CurWinAra[0][0] Global $G_CurPopupAra = _WinAPI_EnumWindowsPopup() ; Get list of all pop-up windows $G_NumCurPopups = $G_CurPopupAra[0][0] $G_NumCurPopups = UBound( $G_CurPopupAra ) If $G_NumPrevWins > 0 Then ; If we've been through this loop more than once, we'll have something to compare against If $L_CurWinDictExists Then $L_CurWinDict.RemoveAll $L_CurWinDict.Init ; ? Does RemoveAll and Imit have the same effect? Else $L_CurWinDict = ObjCreate("Scripting.Dictionary") $L_CurWinDictExists = True EndIf If $L_PrevWinDictExists Then $L_PrevWinDict.RemoveAll $L_PrevWinDict.Init Else $L_PrevWinDict = ObjCreate("Scripting.Dictionary") $L_PrevWinDictExists = True EndIf For $i = 1 To $G_NumCurWins -1 $L_CurWinDict.ADD(Hex($G_CurWinAra[$i][0]), $G_CurWinAra[$i][1]) ; I'm unclear on the second parameter If @error <> 0 Then Local $pig = 11 ; These are just something I can set the debugger to break upon EndIf Next For $i = 1 To $G_NumPrevwins -1 $L_PrevWinDict.ADD(Hex($G_PrevWinAra[$i][0]), $G_CurWinAra[$i][1]) If @error <> 0 Then Local $cat = 11 EndIf Next For $i = 1 To $G_NumCurWins -1 $L_CurWinHdl = $G_CurWinAra[$i][0] $L_CurHexWinHdl = Hex( $L_CurWinHdl ) If Not $L_PrevWinDict.Exists( $L_CurHexWinHdl ) Then If __IsExplorerWindow( $G_CurWinAra[$G_MainWinIdx][0], $G_CurWinAra[$G_MainWinIdx][1] ) Then $G_CurWinIsExplorer = True Else $G_CurWinIsExplorer = False EndIf $L_Stat = __MoveNewWin( $L_CurWinHdl, $G_CurWinAra[$G_MainWinIdx][1], $G_CurWinIsExplorer ) EndIf Next EndIf If $G_NumPrevWins = 0 Then Global $G_PrevWinAra = $G_CurWinAra $G_NumPrevWins = $G_NumCurWins EndIf If $G_NumPrevPopups > 0 Then If $L_CurPopDictExists Then $L_CurPopDict.RemoveAll $L_CurPopDict.Init Else $L_CurPopDict = ObjCreate("Scripting.Dictionary") $L_CurPopDictExists = True EndIf If $L_PrevPopDictExists Then $L_PrevPopDict.RemoveAll $L_PrevPopDict.Init Else $L_PrevPopDict = ObjCreate("Scripting.Dictionary") $L_PrevPopDictExists = True EndIf For $i = 1 To $G_NumPrevPopups -1 $L_PrevPopupWinHdl = $G_PrevPopupAra[$i][0] $L_PrevHexPopupWinHdl = Hex( $L_PrevPopupWinHdl ) $L_PrevPopDict.ADD($L_PrevHexPopupWinHdl, $G_PrevPopupAra[$i][1]) If @error <> 0 Then Local $catpop = 11 EndIf Next For $i = 1 To $G_NumCurPopups -1 $L_CurPopupWinHdl = $G_CurPopupAra[$i][0] $L_CurHexPopupWinHdl = Hex($L_CurPopupWinHdl) $L_CurPopDict.ADD($L_CurHexPopupWinHdl, $G_CurPopupAra[$i][1]) If @error <> 0 Then Local $pigpop = 11 EndIf Next For $i = 1 To $G_NumCurPopups -1 $L_CurPopupWinHdl = $G_CurPopupAra[$i][0] $L_CurHexPopupWinHdl = Hex($L_CurPopupWinHdl) If Not $L_PrevWinDict.Exists( $L_CurHexPopupWinHdl ) Then $L_Stat =__MoveNewPopup( $L_CurPopupWinHdl ) EndIf Next EndIf If $G_NumPrevPopups = 0 Then Global $G_PrevPopupAra = $G_CurPopupAra $G_NumPrevPopups = $G_NumCurPopups EndIf Sleep( 1000 ) WEnd Exit Thank you very much for your time! Link to comment Share on other sites More sharing options...
water Posted October 25, 2020 Share Posted October 25, 2020 My 2 cents worth: There doesn't seem to be an Init method according to https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/dictionary-object Create the Dictionary object before you start the loop. Once created just fill and empty dictionary as needed. $L_CurWinDict = ObjCreate("Scripting.Dictionary") $L_PrevWinDict = ObjCreate("Scripting.Dictionary") While True Global $G_CurWinAra = _WinAPI_EnumWindows( True, Default ) ; Get list of ALL visible windows $G_NumCurWins = $G_CurWinAra[0][0] Global $G_CurPopupAra = _WinAPI_EnumWindowsPopup() ; Get list of all pop-up windows $G_NumCurPopups = $G_CurPopupAra[0][0] $G_NumCurPopups = UBound( $G_CurPopupAra ) If $G_NumPrevWins > 0 Then ; If we've been through this loop more than once, we'll have something to compare against $L_CurWinDict.RemoveAll $L_PrevWinDict.RemoveAll ... My UDFs and Tutorials: Spoiler UDFs: Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs: Excel - Example Scripts - Wiki Word - Wiki Tutorials: ADO - Wiki WebDriver - Wiki Link to comment Share on other sites More sharing options...
jchd Posted October 25, 2020 Share Posted October 25, 2020 You can also use the new AutoIt Map datatype supported by the beta. All quirks have been fixed and Maps work better than scripting dictionaries: #include <Array.au3> Local $m[] ; $m is a Map $m[0x12345678] = "This 32-bit key works" $m[0x1234567812345678] = "But does this 64-bit key work?" ; yes! _ArrayDisplay(MapKeys($m)) Local $d = ObjCreate("Scripting.Dictionary") $d.Add(0x12345678, "This 32-bit key works") $d.Add(0x1234567812345678, "But does this 64-bit key work?") ; no! Mbee 1 This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
Mbee Posted November 2, 2020 Author Share Posted November 2, 2020 Thank you most kindly, @jchd ! I apologize for the delay in responding... I certainly wish to stay up to date, so Maps it is -- I hope! And I wouldn't have known to travel that route without your always valuable contributions. But I'm unsure on a few points. First, the data structure I need to use will contain some entries that have the same key but different values (i.e., different key/value combinations). But since the MapExists() function doesn't allow one to test for the existence of such combos (or at least it doesn't look like it to me), am I going to be able to use Maps after all? I cannot conceive of a way to combine both entities into a single key, since I'll have no idea what possible data values might already exist without looking up the key first! (There's a hole in that bucket...) Is there a solution to this dilemma using Maps that you know of? But for now, let's assume there is a solution. Yet because one mustn't duplicate a key if it already exists, do I assume correctly that one has to test using MapExists() first, and if it doesn't exist, what then? Do I simply perform an assignment, or should I use MapAppend()? I can see that MapAppend() is equivalent to ReDim-ming an array and adding a new element, but since these aren't arrays, doesn't an assignment add another element anyway, just like append? In other words, I fail to grasp when to append and when to simply assign. Also, do you know if there's a simple way to delete all elements in a Map without looping through them all? If not, that's not a problem, but I'd just like to know. Two more questions: (1)L Can I simply copy one Map to another with a single assignment statement like we can with an array? (never mind, that's easy to test). (2): The keys I will be using are window handles. Should I leave them as integers, or convert them into hex strings first? If there's someone or somewhere else I should ask these questions (technical subforum?), please let me know. Thank you enormously once again, sir! Link to comment Share on other sites More sharing options...
Mbee Posted November 2, 2020 Author Share Posted November 2, 2020 I should have thought longer about my first question, since there's an obvious solution! I don't need to have duplicate keys with different values (which probably aren't allowed anyway). All I have to do is to create a compound, delimited string with however many values I need to associate with a specific key (separated by @LFs, for example). Problem solved! I'd still like your take on the rest, though... Link to comment Share on other sites More sharing options...
jchd Posted November 3, 2020 Share Posted November 3, 2020 "Associative arrays" types like Maps, Scripting-dictionaries and whatever they're called in other languages don't cope with dup keys. If you have to store distinct pairs having the same key, say {keyX, valueA} and {keyX, valueB} I'd propose storing {keyX, arrayV} with arrayV being and array of values for keyA, [valueA, valueB]. Alternatively you can also store multiple values for a given key in a nested Map, with keys 1, 2, 3, ... This way you can still store any datatype value verbatim without fear it could be destroyed by converting it to string. E.g. you can store objects this way, something you can't by using conversion to string. This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
Mbee Posted November 3, 2020 Author Share Posted November 3, 2020 Thanks again! I had described in my post just above yours that I figured out that I absolutely do not need duplicate keys, since I can store as much info in the value as I need. Since all value data will be strings anyway, I was planning on building compound strings separated by life feeds and modify them as needed. But now that I've read your final sentence, I'll simply store them as arrays to obviate the need for using StringSplit(). And to change these arrays' sizes to hold more or less data, I'll just Redim them as needed. But I'm still wondering if there are faster/easier ways to empty a Map rather than just looping through and using MapRemove() on every element. Maybe I can declare an empty Map and simply assign it to the other Map I want to erase? If you have any thoughts on such issues, I'd be eager to read them. But it's about time I start experimenting anyway... Link to comment Share on other sites More sharing options...
jchd Posted November 4, 2020 Share Posted November 4, 2020 Here's an example (based on nested Maps) you can play with. The function vd() produces a variable dump as the output shows. Local $m[] ; $m is a Map $m["abc"] = "This" $m["def"] = "That" $m["key"] = 111 vd($m) ; till now, everything was added explicitely ; now use a function to insert new value(s) to possibly already existing keys _Add2Map($m, "ghi", "One more value") ; key not existing vd($m) ; several values to existing key _Add2Map($m, "key", 123) _Add2Map($m, "key", 456) _Add2Map($m, "key", 789) vd($m) ; same value to existing key _Add2Map($m, "key", 456) vd($m) Func _Add2Map(ByRef $map, $key, $value) If MapExists($map, $key) Then ; same as If $m[$key] = Null Then If IsMap($map[$key]) Then MapAppend($map[$key], $value) Else Local $m2[] ; an empty map $m2[1] = $map[$key] $m2[2] = $value $map[$key] = $m2 EndIf Else $map[$key] = $value EndIf EndFunc Map[3] ['abc'] => String (4) 'This' ['def'] => String (4) 'That' ['key'] => Int32 111 Map[4] ['abc'] => String (4) 'This' ['def'] => String (4) 'That' ['key'] => Int32 111 ['ghi'] => String (14) 'One more value' Map[4] ['abc'] => String (4) 'This' ['def'] => String (4) 'That' ['key'] => Map[4] [1] => Int32 111 [2] => Int32 123 [3] => Int32 456 [4] => Int32 789 ['ghi'] => String (14) 'One more value' Map[4] ['abc'] => String (4) 'This' ['def'] => String (4) 'That' ['key'] => Map[5] [1] => Int32 111 [2] => Int32 123 [3] => Int32 456 [4] => Int32 789 [5] => Int32 456 ['ghi'] => String (14) 'One more value' Now I don't really get what/when you'll need to remove elements and on which criterion. Anyway, the keys of nested maps in this example are positive increasing integers in insert order. Removing any entry in a Map is faster than removing a row in an array. Mbee 1 This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
Mbee Posted November 17, 2020 Author Share Posted November 17, 2020 Well, I've invested considerable effort in getting Maps to work for me, and regrettably I've failed. All the Maps work in my rather basic code for everything other than simply retrieving the data value associated with a key, which always seems to return zero. I'll post the code that reproduces this behavior if asked, but I'm kinda fed up for the moment... My question now is: Given the problem I'm having with Maps, what alternative should I use? Scripting dictionaries as in my OP, or perhaps Nutster's Associative Array UDF? Thanks. Link to comment Share on other sites More sharing options...
Nine Posted November 17, 2020 Share Posted November 17, 2020 Scripting dictionary never deceived me before. Very fast and simple to use. No need for additional UDF. “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) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
Mbee Posted November 17, 2020 Author Share Posted November 17, 2020 Just now, Nine said: Scripting dictionary never deceived me before. Very fast and simple to use. No need for additional UDF. Thank you, @Nine You're most definitely a voice to trust! Link to comment Share on other sites More sharing options...
jchd Posted November 18, 2020 Share Posted November 18, 2020 Post your Map "failing code" for us to fix. This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
Mbee Posted November 18, 2020 Author Share Posted November 18, 2020 Sure thing. Happy to! Hang on a bit, though... Link to comment Share on other sites More sharing options...
Mbee Posted November 18, 2020 Author Share Posted November 18, 2020 (edited) Boy, I had just gotten through apologizing because I was unable to duplicate the problem all of a sudden, and I was extremely embarrassed!! But just now I realized that the working version was a scripting dictionary version rather than the maps version, so I have to get my excrement in order again. Sorry. I might not have anything to post for a while... Edited November 18, 2020 by Mbee Major screwup Link to comment Share on other sites More sharing options...
Mbee Posted November 18, 2020 Author Share Posted November 18, 2020 Hi, @jchd Fortunately, it turns out that Map data retrievals are working perfectly! Woo hoo! 😊 I think I may have discovered why I mistakenly thought that Map data value retrievals were returning zeros: I was using the graphical debugger in Release mode. Happily, that debugger also has a Beta mode, and when I engaged that, the problem disappeared. Thanks again for your patience and assistance. Link to comment Share on other sites More sharing options...
jchd Posted November 18, 2020 Share Posted November 18, 2020 Good. This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
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