Jump to content

ProcessWait internal functions


wraithdu
 Share

Recommended Posts

I'm trying to implement a ProcessWait function (macro) in NSIS. Currently I'm using a series of psapi calls to enumerate the process pids, then open a handle to each process, get the process base name from the handle, and check that name against my target. It works fine in a ProcessExists sense (only called once), but when calling it in a loop to wait for a process to exist, CPU usage is really ugly high, like 25-30% on my core2duo. I'm pausing for 250ms between loops. Increasing the pause to 500ms lowers CPU slightly to 15-20%. But even when calling it once, there's a CPU spike to 15% or so.

Now ProcessWait in AutoIt polls at 250 ms also, and has no CPU load. So I was wondering what psapi functions ProcessWait is using, and how I can try to implement this in NSIS without the CPU load.

I'll admit, this may just be a problem with NSIS's implementation of its System plugin (same functionality as DllCall() in AutoIt, the ability to call external DLL functions). In that case there's nothing I could do about it but use an external plugin (I'm trying to avoid that though).

Here's the basic sequence of functions I'm using -

loop:

EnumProcesses

loop:

OpenProcess

GetModuleBaseName

CloseHandle

endloop

Sleep 250

endloop

Should I be using something else more efficiently?

Link to comment
Share on other sites

Yes, and that's exactly what I did for my translation of ProcessWaitClose. It works great!

However I'm having problems with ProcessWait (waiting for a process to exist). There's no choice but to continually poll the process list until the target application is started. So I'm looking to optimize it as much as possible.

Here's a link to my actual NSIS code (if it will help) -

http://nascent-project.org/wraithdu/files/NSIS/ProcFunc2.nsh

Edited by wraithdu
Link to comment
Share on other sites

Thanks for the link.

AutoIt uses the same psapi calls I'm doing in NSIS. Actually I'm doing one less call, I'm not enumerating the process modules. According to msdn, it's unnecessary, as the GetModuleBaseName module handle parameter can be null to return the creating process' name (which is what you want anyway).

It must just be that NSIS and the System plugin are inefficient. I might try the toolhelp32 method and see if it is any lighter on resources.

Link to comment
Share on other sites

  • Moderators

Thanks for the link.

AutoIt uses the same psapi calls I'm doing in NSIS. Actually I'm doing one less call, I'm not enumerating the process modules. According to msdn, it's unnecessary, as the GetModuleBaseName module handle parameter can be null to return the creating process' name (which is what you want anyway).

It must just be that NSIS and the System plugin are inefficient. I might try the toolhelp32 method and see if it is any lighter on resources.

Weird about the EnumProcess+Modules...

I've found that in FreeBasic and BCX that I also had permission issues (assuming that's why GetModuleBaseName would fail) with EnumProcesses/Modules + GetModuleBaseName (an issue obviously that AutoIt doesn't have).

When using that method, I had no CPU issue with 250 ms delay between polls (Just didn't get all the names), when switching to CreateToolHelp32Snapshot, it definately fixed the name issue, but that is when it shot up to a rediculous cpu issue.

Still haven't quite worked out how to get those two languages to work properly as AutoIt does because AdjustTokenPrivileges always fails (Can't even use ExitWindowsEx properly in those), maybe some inside security setting I haven't found yet.

Anyway... I play with it (the Process* functions) often in the others, so if it there isn't a fix for you soon, and I find one, I'll post my method on those here.

Edited by SmOke_N

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

Well I got the toolhelp32 method working in NSIS....no better. With a loop wait of 250ms, CPU is still pegged at 20-25%. 500ms brings it down to 12-17%. I'm gonna have to say that it's a problem with NSIS and its System plugin, because this is exactly the correct way to do it. Here's the toolhelp32 method if anyone wants to play in NSIS. This loops until notepad.exe exists.

SilentInstall silent
OutFile test.exe

!include LogicLib.nsh

!define TH32CS_SNAPPROCESS 0x00000002

Section
; initialize loop
    StrCpy $R0 1
    System::Call /NOUNLOAD '*(&l1060, i, i, i, i, i, i, i, i, &t1024)i .r1'
; create PROCESSENTRY32 struct, $1 = handle
    
    ${DoUntil} $R0 == 0
        System::Call /NOUNLOAD 'kernel32::CreateToolhelp32Snapshot(i ${TH32CS_SNAPPROCESS}, i 0)i .r0'
; $0 = handle to snapshot
        System::Call /NOUNLOAD 'kernel32::Process32First(i $0, i $1)i .r3'
; first process
        
        ${Do}
            System::Call /NOUNLOAD 'kernel32::Process32Next(i $0, i $1)i .r3'
            ${IfThen} $3 == 0 ${|} ${Break} ${|}
            System::Call /NOUNLOAD '*$1(i, i, i, i, i, i, i, i, i, &t1024 .r4)'
    ; read process name
            ${If} $4 == "notepad.exe"
                StrCpy $R0 0
                ${Break}
            ${EndIf}
        ${Loop}
        System::Call /NOUNLOAD 'kernel32::CloseHandle(i $0)'
; destroy snapshot
        ${IfThen} $R0 == 1 ${|} Sleep 250 ${|}
; pause before looping again
    ${Loop}
    System::Free $1
; free struct
Sectionend

EDIT: Fixed looping code, CPU usage should be on par with the script below. Also fixed comments. Board code omits space before the ; and causes errors on compile.

Edited by wraithdu
Link to comment
Share on other sites

Well I got the toolhelp32 method working in NSIS....no better. With a loop wait of 250ms, CPU is still pegged at 20-25%. 500ms brings it down to 12-17%. I'm gonna have to say that it's a problem with NSIS and its System plugin, because this is exactly the correct way to do it. Here's the toolhelp32 method if anyone wants to play in NSIS. This loops until notepad.exe exists.

System plugin has the least to do with this. It's the horrendous overhead of logic lib.

SilentInstall silent
OutFile test.exe

;;!include LogicLib.nsh

!define TH32CS_SNAPPROCESS 0x00000002



Section
   ;;StrCpy $R0 1; initialize loop
    System::Call /NOUNLOAD '*(&l1060, i, i, i, i, i, i, i, i, &t1024)i .r1'; create PROCESSENTRY32 struct, $1 = handle
loop1:
    System::Call /NOUNLOAD 'kernel32::CreateToolhelp32Snapshot(i ${TH32CS_SNAPPROCESS}, i 0)i .r0'; $0 = handle to snapshot
    System::Call /NOUNLOAD 'kernel32::Process32First(i $0, i $1)i .r3'; first process
;;loop through snapshot
    loop2:
        System::Call /NOUNLOAD 'kernel32::Process32Next(i $0, i $1)i .r3'
        IntCmp $3 0 break
        System::Call /NOUNLOAD '*$1(i, i, i, i, i, i, i, i, i, &t1024 .r4)'; read process name
        StrCmp $4 "notepad.exe" finish
        Goto loop2
    break:
    System::Call /NOUNLOAD 'kernel32::CloseHandle(i $0)'; destroy snapshot
    Sleep 250; pause before looping again
    Goto loop1
finish:
    System::Free $1; free struct
Sectionend

"be smart, drink your wine"

Link to comment
Share on other sites

Ron, you don't need to muck about with privileges to enumerate processes. You just need to open them with ONLY the access rights you need (in your case, PROCESS_QUERY_INFORMATION). One thing working on AutoIt has taught me is that when working with any API that takes access rights, always figure out what the least rights you need are and use only those. Those blanket "ALL_ACCESS" constants are nice but inevitably they will cause you to get access denied errors as you've requested access rights your token doesn't have privileges for.

Link to comment
Share on other sites

System plugin has the least to do with this. It's the horrendous overhead of logic lib.

Thanks for the suggestion, but cutting out LogicLib only saves about 1-2% CPU in my tests. Not worth it IMO.

Edited by wraithdu
Link to comment
Share on other sites

  • Moderators

Ron, you don't need to muck about with privileges to enumerate processes. You just need to open them with ONLY the access rights you need (in your case, PROCESS_QUERY_INFORMATION). One thing working on AutoIt has taught me is that when working with any API that takes access rights, always figure out what the least rights you need are and use only those. Those blanket "ALL_ACCESS" constants are nice but inevitably they will cause you to get access denied errors as you've requested access rights your token doesn't have privileges for.

I'm not going to hijack this poor souls thread (as I have a similar issue with my ProcessWait() in other languages :D :D )

But... I "always" do the min required for that very issue (My own trial and error :) ).

For the OpenProcess():

PROCESS_QUERY_INFORMATION | PROCESS_VM_READ

And

PROCESS_QUERY_INFORMATION

Will not allow me to use GetModuleFileNameEx() to get the full path or even GetModuleBaseName() for the exe name only of any higher level process running (such as csrss.exe/some svchost.exe's/and a few other highers)... I can of course always get my PIDs however with those query methods.

Edit:

You know... you had me second guess myself after all this time... a quick glance at msdn again... as well as a quick glance at my code in both languages showed me the error I did.

I really could just puke at the moment (HANDLE hMod :D ) (more like HMODULE hMod :D ) ... I haven't even tested it yet... but I'd bet that's it.

Edit2:

Nah, didn't fix it... but at least it was an error I found.

Edited by SmOke_N

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

Thanks for the suggestion, but cutting out LogicLib only saves about 1-2% CPU in my tests. Not worth it IMO.

Not sure what tests you ran, but the script I posted takes 3-5% of my CPU, the "logic" one above it takes the max, 90+, using NSIS 2.36.

"be smart, drink your wine"

Link to comment
Share on other sites

Not sure what's going on then...I tried your script again verbatim, and still get 20-25%, Core2Duo. I'll have to try on another system when I get a chance.

I wrote up another looping routine that uses the FindProcDLL plugin (which kinda defeats the purpose since I was trying to get everything working with just the default NSIS build and no extra plugins), it loops every 250ms and produces 0 CPU load. It uses LogicLib as well. Go figure.

Edited by wraithdu
Link to comment
Share on other sites

Ha, more fun :D

Finally got a chance to try this on my older work computer, XP SP2. Things aren't nearly as bad as on Vista. With the tighter 250ms loop CPU usage is at around 9-12%, and with the 500ms loop is around 6-9%. Why the higher resource demands on Vista? I wonder if the System plugin needs some Vista optimizations.

Incidentally, I didn't see any real difference in CPU usage when running with or without LogicLib, maybe 1%.

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