JockoDundee Posted October 12, 2020 Share Posted October 12, 2020 Ok, not so much of a challenge, just a quick stumper. Why doesn’t this script work to kill all notepads in one shot? While ProcessClose("notepad.exe") WEnd One might reasonably think it would, since it returns 1 when successful, and 0 when not. So it should just keep on going until they are all killed. Right? Hint: this does work in one shot... While ProcessExists("notepad.exe") ProcessClose("notepad.exe") WEnd Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
Developers Jos Posted October 12, 2020 Developers Share Posted October 12, 2020 (edited) I think there will be an error when one of the processes is terminating and you try terminating it again as the function will try to terminate the process with the highest PID.That would stop the loop. The second script will simply continue till none exists. Jos Edited October 12, 2020 by Jos JockoDundee and TheDcoder 1 1 SciTE4AutoIt3 Full installer Download page - Beta files Read before posting How to post scriptsource Forum etiquette Forum Rules Live for the present, Dream of the future, Learn from the past. Link to comment Share on other sites More sharing options...
TheDcoder Posted October 12, 2020 Share Posted October 12, 2020 Good explanation by @Jos, this is a classic race-condition problem. A proper solution would be to get a list of all processes and then using ProcessClose on each one of them, but using ProcessExists in a loop also does the trick, albeit in a less CPU efficient manner JockoDundee 1 EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time) DcodingTheWeb Forum - Follow for updates and Join for discussion Link to comment Share on other sites More sharing options...
TheSaint Posted October 12, 2020 Share Posted October 12, 2020 9 minutes ago, TheDcoder said: A proper solution would be to get a list of all notepad.exe processes and then using ProcessClose on each one of them Make sure brain is in gear before opening mouth! Remember, what is not said, can be just as important as what is said. Spoiler What is the Secret Key? Life is like a Donut If I put effort into communication, I expect you to read properly & fully, or just not comment. Ignoring those who try to divert conversation with irrelevancies. If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it. I'm only big and bad, to those who have an over-active imagination. I may have the Artistic Liesense to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage) Link to comment Share on other sites More sharing options...
JockoDundee Posted October 12, 2020 Author Share Posted October 12, 2020 3 minutes ago, TheDcoder said: A proper solution would be to get a list of all processes and then using ProcessClose on each one of them, Would it really be more “proper” though? It doesn’t actually insure that there exists a moment in time when there is no notepad running, i.e. the process list is stale, and a new notepad may have started in the interim. This is also the reason that the While ProcessClose() method fails: it uses a stale list of processes to close and so tries to close the same one twice and dies. Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
TheDcoder Posted October 12, 2020 Share Posted October 12, 2020 @JockoDundee "Proper" changes with the end goal, in my example the end goal was to close all instance of a processes which were active at that specific point in time. So yes, you are right that it doesn't ensure that there is no notepad.exe process running, for that you'd need to use your method... or use mine in a loop 4 minutes ago, JockoDundee said: This is also the reason that the While ProcessClose() method fails Not the same thing really, I don't know the internals of ProcessClose but I am pretty sure it is not related to the list of processes, perhaps windows doesn't like terminating a process which is already terminating, so it may return an error in that case. @TheSaint It was deliberate, I wasn't specifically targeting notepad.exe, just saying things generally. EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time) DcodingTheWeb Forum - Follow for updates and Join for discussion Link to comment Share on other sites More sharing options...
JockoDundee Posted October 12, 2020 Author Share Posted October 12, 2020 44 minutes ago, Jos said: I think there will be an error when one of the processes is terminating and you try terminating it again as the function will try to terminate the process with the highest PID.That would stop the loop. I agree and (slightly) disagree. I agree that “there will be an error when...you try terminating it again.” IMHO, though, it’s not because one of the “processes is terminating” and you terminate it again. I think each process is fully terminated before it tries another. After all, it looks to be a synchronous call where the return code states the process has been successfully terminated. Rather, as I said above, ProcessClose seems to rely on a stale list of processes updated every 250ms. This causes it to execute twice on a newly non-existent process. Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
JockoDundee Posted October 12, 2020 Author Share Posted October 12, 2020 1 minute ago, TheDcoder said: Not the same thing really, I don't know the internals of ProcessClose but I am pretty sure it is not related to the list of processes I am basing it on the help file for ProcessClose, which states: The process is polled approximately every 250 milliseconds. Perhaps I have misinterpreted this, but I’m not sure how else to interpret it, other than it can act on stale data. Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
Developers Jos Posted October 12, 2020 Developers Share Posted October 12, 2020 (edited) Don't think so and stick to my earlier guess of your issue. process close("notepad.exe") will take a snapshot of all notepad.exe processes available and tries to kill the one with the highest PID, hence my earlier guess.... But I didn't check yet what that 250ms comment is about...so who knows. Jos Edited October 12, 2020 by Jos SciTE4AutoIt3 Full installer Download page - Beta files Read before posting How to post scriptsource Forum etiquette Forum Rules Live for the present, Dream of the future, Learn from the past. Link to comment Share on other sites More sharing options...
seadoggie01 Posted October 12, 2020 Share Posted October 12, 2020 4 minutes ago, JockoDundee said: it looks to be a synchronous call Sheesh, just try it if you want to know test() Func Test() For $i=0 To 4 Run("notepad.exe") Next Sleep(5000) Local $iCount = 0 While ProcessExists("notepad.exe") ProcessClose("notepad.exe") $iCount += 1 WEnd ConsoleWrite("Tried to kill it: " & $iCount & " times" & @CRLF) EndFunc I got 7 tries. ProcessClose doesn't wait. That's what ProcessWaitClose is for. All my code provided is Public Domain... but it may not work. Use it, change it, break it, whatever you want. Spoiler My Humble Contributions:Personal Function Documentation - A personal HelpFile for your functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types Link to comment Share on other sites More sharing options...
JockoDundee Posted October 12, 2020 Author Share Posted October 12, 2020 (edited) 13 minutes ago, seadoggie01 said: That's what ProcessWaitClose is for. ProcessWaitClose doesn’t kill anything. It’s sounds similar but it’s just a blocking check. The reason you got seven tries is because the list that ProcessClose uses is stale, only being updated every 250ms. Therefore, even when ProcessExist determines there is still a process out there, ProcessClose tries to kill the one it just killed 100ms ago. Eventually, it sees the light though Edited October 12, 2020 by JockoDundee Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
seadoggie01 Posted October 12, 2020 Share Posted October 12, 2020 That's my point, it's not synchronous. If it was synchronous, it would wait to see if the process was closed before returning. It sends a kill signal and returns. If you use this instead, it works... test() Func Test() For $i=0 To 4 Run("notepad.exe") Next Local $iCount = 0, $iPid While True $iPid = ProcessExists("notepad.exe") If $iPid = 0 Then ExitLoop ProcessClose($iPid) If @error Then ConsoleWrite("@error: " & @error) ProcessWaitClose($iPid) $iCount += 1 WEnd ConsoleWrite("Tried to kill it: " & $iCount & " times" & @CRLF) EndFunc All my code provided is Public Domain... but it may not work. Use it, change it, break it, whatever you want. Spoiler My Humble Contributions:Personal Function Documentation - A personal HelpFile for your functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types Link to comment Share on other sites More sharing options...
JockoDundee Posted October 12, 2020 Author Share Posted October 12, 2020 3 minutes ago, seadoggie01 said: it would wait to see if the process was closed before returning. It sends a kill signal and returns. It is not message based, the error code says successful, and it clearly is waiting around for the error: 1 = OpenProcess failed2 = AdjustTokenPrivileges Failed3 = TerminateProcess Failed4 = Cannot verify if process exists Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
seadoggie01 Posted October 12, 2020 Share Posted October 12, 2020 https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess Internally, (based on the documentation that you posted) this calls "TerminateProcess" in processthreadsapi.h Which states, Quote TerminateProcess is asynchronous; it initiates termination and returns immediately. If you need to be sure the process has terminated, call the WaitForSingleObject function with a handle to the process. JockoDundee and TheDcoder 1 1 All my code provided is Public Domain... but it may not work. Use it, change it, break it, whatever you want. Spoiler My Humble Contributions:Personal Function Documentation - A personal HelpFile for your functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types Link to comment Share on other sites More sharing options...
JockoDundee Posted October 12, 2020 Author Share Posted October 12, 2020 4 minutes ago, seadoggie01 said: Internally, (based on the documentation that you posted) this calls "TerminateProcess" Thanks, good work. Where does the 250ms polling thing come from then? Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
seadoggie01 Posted October 12, 2020 Share Posted October 12, 2020 The 250ms polling is common to all of the functions like Process*... I'm not 100% sure how it applies here. Maybe it means that it won't check the processes more than once every 1/4 of a second? This is actually really cool though, you can see by the error values how it works internally... first, they open the process handle (OpenProcess), they enable some disabled privileges (AdjustTokenPrivileges), before finally terminating the process (TerminateProcess). TheDcoder 1 All my code provided is Public Domain... but it may not work. Use it, change it, break it, whatever you want. Spoiler My Humble Contributions:Personal Function Documentation - A personal HelpFile for your functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types Link to comment Share on other sites More sharing options...
TheDcoder Posted October 12, 2020 Share Posted October 12, 2020 @JockoDundee Thanks for pointing out the documentation about 250 ms, in that case, it does appear indeed that AutoIt may be caching the process lists as a form of optimization. So anything is possible EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time) DcodingTheWeb Forum - Follow for updates and Join for discussion Link to comment Share on other sites More sharing options...
JockoDundee Posted October 12, 2020 Author Share Posted October 12, 2020 (edited) Well, I’m wrong and basically everyone else, @Jos, @TheDcoder, and @seadoggie01 are right*: There is no “stale” process list. The termination is not complete, even with a successful completion code. But it doesn’t take long; putting just sleep(1) inside the loop is long enough for the original example to work. The 250ms thing is a mirage, I suspect it may be copied over from the ProcessWaitClose() doc where there is an actual polling process. Apologies to all! *Heck, even @TheSaint was right Edited October 12, 2020 by JockoDundee Added praise for the TheSaint Code hard, but don’t hard code... Link to comment Share on other sites More sharing options...
Developers Jos Posted October 12, 2020 Developers Share Posted October 12, 2020 (edited) I don't see anything in the source about caching processes or checking 250ms. It seems to build the process list with each call. I did manage a few times to get an error 1 Extended 87 and not closing all notepad.exe processes with this script: for $x = 1 to 10 run("notepad.exe") Next Sleep(1000) While ProcessClose("notepad.exe") WEnd ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : WEnd =>Error code: ' & @error & @CRLF) ;### Debug Console So we receive a returncode 87 for an OpenProcess() call to PROCESS_TERMINATE an PID that doesn't exists anymore. So to me that means in between the time we did the ProcessList, checked for the first occurrence and doing the actual ProcessOpen() , this PID was gone. 15 minutes ago, JockoDundee said: But it doesn’t take long; putting just sleep(1) inside the loop is long enough for the original example to work. Sleep(1) doesn't really exists and believe 10 is the minimum. 15 minutes ago, JockoDundee said: The 250ms thing is a mirage, Agree, and the only thing I can think of is that the Helpfile doc was copied from ProcessWaitClose and forgotten to remove that line. Jos Edited October 12, 2020 by Jos JockoDundee 1 SciTE4AutoIt3 Full installer Download page - Beta files Read before posting How to post scriptsource Forum etiquette Forum Rules Live for the present, Dream of the future, Learn from the past. Link to comment Share on other sites More sharing options...
seadoggie01 Posted October 12, 2020 Share Posted October 12, 2020 If the Helpfile was copied, ProcessExists might be incorrect as well All my code provided is Public Domain... but it may not work. Use it, change it, break it, whatever you want. Spoiler My Humble Contributions:Personal Function Documentation - A personal HelpFile for your functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types 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