Jump to content

PiD “challenge”


Recommended Posts

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

  • Developers

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 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

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 ;)

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

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

:P

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)

userbar.png

Link to comment
Share on other sites

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

@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 :lol:

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

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

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

  • Developers

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 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

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 functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

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 by JockoDundee

Code hard, but don’t hard code...

Link to comment
Share on other sites

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 functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

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 failed
2 = AdjustTokenPrivileges Failed
3 = TerminateProcess Failed
4 = Cannot verify if process exists

Code hard, but don’t hard code...

Link to comment
Share on other sites

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.

 

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 functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

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).

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 functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

@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

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 by JockoDundee
Added praise for the TheSaint

Code hard, but don’t hard code...

Link to comment
Share on other sites

  • Developers

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 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

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 functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...