Jump to content

FIXED: ProcessWaitClose - 15-20% processor utilization -> skip to WMI ?


aef03
 Share

Recommended Posts

Anyone else experience this?

Anyone compare it to using WMI events against Win32_process?

I am running this as a shell to another process that stays running for along time, so it's significant.

Running the latest AutoIt on Windows 7.

Thanks.

Edited by aef03
Link to comment
Share on other sites

Yes, I have.

Here's a solution in pseudo code:

1. PID = Run()  
2. HANDLE = OpenProcess(PID, PROCESS_ALL_ACCESS) 
3. WaitForSingleObject(HANDLE, -1)
4. CloseHandle(HANDLE)

I'm just starting to port my skills from advanced VBScript to AutoIt - any chance you (or anyone) has working sample code?

Also a sentence or two about how it is accomplishing the waiting for the other process would be very helpful. I understand what a handle is, but not the WaitForSingleObject.

I would be very grateful!

One other quick question, is there a similar technique that I could use to know when the program I have launched is ready for user input? I would love to have a please wait splash that knew when the launched program was ready.

Thanks much for any additional help.

Link to comment
Share on other sites

I apologize, I didn't do my homework here.

When I jumped into the code, it is actually ProcessWaitClose doing the monitoring.

ShellExecuteWait($OurScriptDir & "starter.exe","runas")

ProcessWait("program.exe")

Sleep("1000")

RemoveSplash()

ProcessWaitClose("program.exe")

So it is "ProcessWaitClose" that was creating this level of CPU burden.

starter.exe starts up program.exe - so I have no way to get a handle back to the actual program I need to watch for.

With this new information has anyone resorted to WMI and gotten better performance than ProcessWaitClose?

(I can write the code, just don't want to do the work if someone already knows it's not any better or that ProcessWaitClose is already implemented using WMI).

D.

Edited by aef03
Link to comment
Share on other sites

This is what I'd do

$iPid = Run()
While ProcessExists($iPid)
    Sleep(100)
WEnd

Or if your waiting for that Program to end then

While ProcessExists("program.exe")
    Sleep(100)
WEnd
Edited by ChrisL
Link to comment
Share on other sites

I put in some hacking time and the following code works and has no perceptible CPU overhead in my EXE, but also I do not see a spike in any service process (where WMI is actually doing the "polling" - though it is extremely light duty polling used internally by Windows for all kinds of things).

Please note that: 1) The process I am monitoring only allows one instance, so I did not need to build it into my WMI code to check if other instances are still alive. The VBScript code for that is below if you need to do that.

ShellExecuteWait($OurScriptDir & "starter.exe","runas")

ProcessWait("program.exe")

Sleep("1000")

RemoveSplash()

Dim $objWMIService,$colMonitoredEvents,$objEventObject

$objWMIService = ObjGet("winmgmts:\\.\root\cimv2")

$colMonitoredEvents = $objWMIService.ExecNotificationQuery _

("SELECT * FROM __InstanceDeletionEvent WITHIN 5 WHERE " _

& "Targetinstance ISA 'Win32_Process' and " _

& "TargetInstance.Name = 'program.exe' ")

$objEventObject = $colMonitoredEvents.NextEvent()

***********

VBScript (that you would need to convert to AutoIt) to monitor if additional instances are running (replaces the last line above):

Do Until i = 9999999

Set objProcess = colProcesses.NextEvent

Set wmi = GetObject("winmgmts://./root/cimv2")

Set col = wmi.ExecQuery("Select * from Win32_Process where name = 'program.exe'")

If col.count = 0 Then Exit Do

Loop

Edited by aef03
Link to comment
Share on other sites

That changes nothing. In that case it's PID = ProcessExists("program.exe").

But since you are the one choosing the (wrong) way, good luck with it.

Trancexx, as I mentioned I'm still getting used to AutoIt. I'm not a coding or research wimp - so by the time I ask here I've taken a pretty good look and/or don't have time for rabbit holes of learning when I can tap into the wisdom of the group here - that's why working code examples help me immensely. I wanted to benchmark your method against WMI, so I got as far as the below code - it compiles and runs, but does not wait for execution.

Not sure what I have done wrong.

ShellExecuteWait($OurScriptDir & "starter.exe","runas")

ProcessWait("program.exe")

Sleep("1000")

Dim $HANDLE

$HANDLE = ProcessExists("program.exe")

RemoveSplash()

_WinAPI_WaitForSingleObject($HANDLE, -1)

_WinAPI_CloseHandle($HANDLE)

Link to comment
Share on other sites

Well that's a complicated way to go about it

3 variables and 3 lines of code is complicated? If I cared much about the style, I know I can hack it down to 1 variable and 2 lines of code - but I'm looking for lower CPU utilization, not frame-worthy code.

I think the real critique needs to be focused on why the implementation of ProcessWaitClose generates up to 20% CPU utilization in some cases (at least the one I am experiencing).

Edited by aef03
Link to comment
Share on other sites

ShellExecuteWait($OurScriptDir & "starter.exe","runas")
ProcessWait("program.exe")
Sleep("1000")
RemoveSplash()

While ProcessExists("program.exe")
    Sleep(100)
WEnd
Msgbox(0,"","Done")

Or am I missing something?

Link to comment
Share on other sites

I think the real critique needs to be focused on why the implementation of ProcessWaitClose generates up to 20% CPU utilization in some cases (at least the one I am experiencing).

Because the way it works. And that is probably very similar to what ChrisL's code do.

Try this (you missed step 2):

$iPId = ProcessExists("program.exe")
$hProcess = _WinAPI_OpenProcess(0x00100000, False, $iPId) ; SYNCHRONIZE (better than PROCESS_ALL_ACCESS for this purpose)
_WinAPI_WaitForSingleObject($hProcess, -1)
_WinAPI_CloseHandle($hProcess)

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

ShellExecuteWait($OurScriptDir & "starter.exe","runas")
ProcessWait("program.exe")
Sleep("1000")
RemoveSplash()

While ProcessExists("program.exe")
    Sleep(100)
WEnd
Msgbox(0,"","Done")

Or am I missing something?

About 5% of your CPU time :x Does surprisingly better than ProcessWaitClose - but I also am not doing controlled tests - so it's possible that it would mimic ProcessWaitClose's CPU utlization under the same conditions if it is also doing this type of direct polling.

With WMI I have 0% utilization by my EXE and when I isolate the instance of svchost.exe that contains WMI it has a 0.75% blip occasionally, but it also has 18 other services running under it.

I'm gonna stick with WMI for now.

Here's the prettiest version of the WMI portion of the code:

Dim $objWMIService

$objWMIService = ObjGet("winmgmts:\\.\root\cimv2")

$objWMIService.ExecNotificationQuery _

("SELECT * FROM __InstanceDeletionEvent WITHIN 5 WHERE " _

& "Targetinstance ISA 'Win32_Process' and " _

& "TargetInstance.Name = 'program.exe' ").NextEvent()

Link to comment
Share on other sites

Because the way it works. And that is probably very similar to what ChrisL's code do.

Try this (you missed step 2):

$iPId = ProcessExists("program.exe")
$hProcess = _WinAPI_OpenProcess(0x00100000, False, $iPId) ; SYNCHRONIZE (better than PROCESS_ALL_ACCESS for this purpose)
_WinAPI_WaitForSingleObject($hProcess, -1)
_WinAPI_CloseHandle($hProcess)

That code is perfect - 0% utilization.

I re-checked with ProcessWaitClose under roughly the same system conditions and it is again 23%.

This should definitely be the implementation of ProcessWaitClose - not sure if there are other things it does that require the polling - but this is much more efficient.

I will be using this code.

D.

P.S. Running on Windows 7 when I get these results.

Link to comment
Share on other sites

trancexx, do you have any other super-cool code that could tell me when "program.exe" is *most likely* ready for user input (settled)?

I want to put together code that allows me launch an EXE and then reasonably accurately (not 100%) display a "please wait" message until the launched EXE is ready. I want to avoid watching for Windows as that would require that the code be customized for every different EXE that is launched.

This is a longer term need where I can spend some time hammering out the details - so just pseudo code and/or design ideas that come to mind would be great.

Thanks,

D.

Edited by aef03
Link to comment
Share on other sites

  • 4 weeks later...

@ trancexx

@ ChrisL

Guys, this is exactly what I was working with tonight.

Had a spot of difficulty, maybe someone can point me in the right direction. I was trying to modify this to work in order to watch 2 processes. If #1 is closed, #2 is also closed. If #2 is closed, it just deletes a group of files. At any rate, when either of the above events happens this app will kill itself as well.

I'm trying as follows (My AutoIt compiled exe's are prefaced with "AI_" in this explanation outside of the code)

AI_App1.exe launches MyProgram.exe

Once MyProgram.exe is successfully launched, AI_App1.exe launches AI_App2.exe (AI_App2.exe watches to see if AI_App1.exe is closed and closes MyProgram.exe if this happens; if MyProgram.exe is closed it only deletes the files listed at the end, if MyProgram.exe or AI_App1.exe are closed, AI_App2.exe will kill itself)

I tried to work the code, but it doesn't want to be worked. Here's what I came up with for AI_App2.exe.

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_UseX64=n
#AutoIt3Wrapper_Res_Fileversion=1.25.0.2
#AutoIt3Wrapper_Res_Fileversion_AutoIncrement=p
#AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

Opt("TrayIconHide", 1)
#include <WinApi.au3>
If $Cmdline[0] <> 5 Then
    Exit (10)
Else
    $path = $Cmdline[1] ;external app (MyProgram.exe) was run from here
    $path2 = $Cmdline[2] ;folder #2
    $path3 = $Cmdline[3] ; folder #3
    $PID = $Cmdline[4] ;PID for external app (MyProgram.exe)
    $tempfile = $Cmdline[5] ; name of temp file
EndIf

$iPID=$PID ; $PID is from MyProgram.exe
$hProcess = _WinAPI_OpenProcess(0x00100000, False, $iPId) ; SYNCHRONIZE 
_WinAPI_WaitForSingleObject($hProcess, -1); - from MyProgram.exe


$iPId2 = ProcessExists("App1.exe")
$hProcess2 = _WinAPI_OpenProcess(0x00100000, False, $iPId2) ; SYNCHRONIZE 
_WinAPI_WaitForSingleObject($hProcess2, -1); - from App1.exe
_WinAPI_CloseHandle($hProcess)





Sleep(250)
$temp=String($tempfile&".abc")
;--if App1.exe is closed, kill MyProgram.exe and delete the files below
;--if MyProgram.exe is closed delete the files below
Sleep(250)
;FileDelete($path&"\*.*")
;ConsoleWrite("Ready to delete folders" & @CRLF)
FileSetAttrib($path2, "-RASHNOT", 1)
FileSetAttrib($path3, "-RASHNOT", 1)
FileSetAttrib($path, "-RASHNOT", 1)
DirRemove($path2, 1)
DirRemove($path3, 1)
DirRemove($path, 1)

sleep(5000)

FileDelete(@TempDir & $temp)

Sleep(250)
Exit

I know I'm missing a step there somewhere. Any thoughts??

Thanks,

Dean

Edited by MadDogDean
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...