wraithdu Posted April 11, 2008 Share Posted April 11, 2008 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 More sharing options...
Valik Posted April 11, 2008 Share Posted April 11, 2008 Once you have a handle to a process, you can use WaitForSingleObject() on it which will be signaled when the process is closed. Link to comment Share on other sites More sharing options...
wraithdu Posted April 11, 2008 Author Share Posted April 11, 2008 (edited) 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 April 11, 2008 by wraithdu Link to comment Share on other sites More sharing options...
Valik Posted April 11, 2008 Share Posted April 11, 2008 Sorry, for some reason I thought you said "close" each time and not "wait". Anyway, have you looked at the AutoIt source code? The source is available for an old version. Link to comment Share on other sites More sharing options...
wraithdu Posted April 11, 2008 Author Share Posted April 11, 2008 No, I'll see if I can find it. You have a link available somewhere? Link to comment Share on other sites More sharing options...
LarryDalooza Posted April 11, 2008 Share Posted April 11, 2008 http://www.autoitscript.com/autoit3/files/...-v3.1.0-src.exe AutoIt has helped make me wealthy Link to comment Share on other sites More sharing options...
wraithdu Posted April 11, 2008 Author Share Posted April 11, 2008 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 More sharing options...
Moderators SmOke_N Posted April 12, 2008 Moderators Share Posted April 12, 2008 (edited) 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 April 12, 2008 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 More sharing options...
wraithdu Posted April 12, 2008 Author Share Posted April 12, 2008 (edited) 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.expandcollapse popupSilentInstall 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 SectionendEDIT: 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 April 16, 2008 by wraithdu Link to comment Share on other sites More sharing options...
Siao Posted April 12, 2008 Share Posted April 12, 2008 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 More sharing options...
Valik Posted April 12, 2008 Share Posted April 12, 2008 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 More sharing options...
wraithdu Posted April 12, 2008 Author Share Posted April 12, 2008 (edited) 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 April 12, 2008 by wraithdu Link to comment Share on other sites More sharing options...
Moderators SmOke_N Posted April 12, 2008 Moderators Share Posted April 12, 2008 (edited) 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 )But... I "always" do the min required for that very issue (My own trial and error ).For the OpenProcess():PROCESS_QUERY_INFORMATION | PROCESS_VM_READAndPROCESS_QUERY_INFORMATIONWill 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 ) (more like HMODULE hMod ) ... 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 April 12, 2008 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 More sharing options...
Siao Posted April 12, 2008 Share Posted April 12, 2008 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 More sharing options...
wraithdu Posted April 13, 2008 Author Share Posted April 13, 2008 (edited) 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 April 13, 2008 by wraithdu Link to comment Share on other sites More sharing options...
wraithdu Posted April 14, 2008 Author Share Posted April 14, 2008 Ha, more fun 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 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