DllCall: adjust memory priority of script Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

DllCall: adjust memory priority of script

15 Jul 2017, 22:49

When launching a script at windows startup via task scheduler, there is a problem where Windows treats it as a background task and the script's process receives lower CPU and memory priorities, which affects performance of the script significantly. Any other processes run by it also receive these lower priorities and will run slow as well. The values for CPU and memory can be exposed in Process Explorer:

Image

The default priorities appear to be 8 for CPU and 5 for memory. Task scheduler gives them lower values like 6 and 3.

Process, Priority, ,N restores the CPU priority to 8, but memory priority is still 3 and performance of the script and any programs launched by the script is noticeably slower (eg. Chrome launch via URL link in my script GUI takes approximately 5 seconds; seems Chrome doesn't like the low memory priority of 3).

I found the priorities can be increased to 8 and 4 by creating the task with XML file <Priority>4</Priority> , however memory priority is still lower (4 instead of 5). Performance seems ok, but still I want the exact same priorities as if the user was just run the program normally as my script does some memory intensive things occasionally.

According to this it should be possible to do with a dllcall. Here is my unsuccessful attempt:

Code: Select all

Process, Exist  ;get pid of script
h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel, "Ptr")  ;get handle to the script
DllCall("SetPriorityClass", "Ptr", h, "UInt", 0x002)  ;set priority class of script to PROCESS_BACKGROUND_MODE_END
DllCall("CloseHandle", "Ptr", h)   ;close handle to script
According to the SetPriorityClass function I need to have PROCESS_SET_INFORMATION privileges, which I can't seem to find a way of doing. Can anyone help?

Also I tried the command line processPriority.exe by user Charlie in that thread, but it returns error "unable to open process".

I've tried Reload aswell but it doesn't change the priorities. SingleInstance Off and then launch a new parallel instance should work, but then the UAC nag for admin privileges will pop up which defeats the whole purpose of using task scheduler in the first place, as it's the only way to silently run as admin at windows startup.

Thanks

edit:
<Priority>2</Priority> in the task scheduler XML is now giving me the default 8 and 5 values. However that's not how the documentation says it's supposed to work, which leads me to believe it's going to result in above normal values on other systems. Perhaps <Priority>2</Priority> in combination with Process, Priority, ,N will yield a safer result.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: DllCall: adjust memory priority of script

16 Jul 2017, 05:35

I tried getting the SeDebugPrivilege per the dll example in the Process documentation, but it's not working. The whole access rights API is way over my head and I would need to take at least a 3 day seminar to understand it :lol:

In the meantime here is how I am scheduling the script as a task with silent admin privileges on windows start, which may be useful if you can be bothered trying to replicate the issue.

Code: Select all

XML_out := ""
XML_out .= "<?xml version=""1.0"" encoding=""UTF-16""?>" . "`n"
XML_out .= "<Task version=""1.2"" xmlns=""http://schemas.microsoft.com/windows/2004/02/mit/task"">" . "`n"
XML_out .= "  <RegistrationInfo />" . "`n"
XML_out .= "  <Triggers>" . "`n"
XML_out .= "    <LogonTrigger>" . "`n"
XML_out .= "      <Enabled>true</Enabled>" . "`n"
XML_out .= "    </LogonTrigger>" . "`n"
XML_out .= "  </Triggers>" . "`n"
XML_out .= "  <Principals>" . "`n"
XML_out .= "    <Principal id=""Author"">" . "`n"
XML_out .= "      <RunLevel>HighestAvailable</RunLevel>" . "`n"
XML_out .= "      <UserId>" . A_ComputerName . "\" . A_UserName . "</UserId>" . "`n"
XML_out .= "      <LogonType>InteractiveToken</LogonType>" . "`n"
XML_out .= "    </Principal>" . "`n"
XML_out .= "  </Principals>" . "`n"
XML_out .= "  <Settings>" . "`n"
XML_out .= "    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>" . "`n"
XML_out .= "    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>" . "`n"
XML_out .= "    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>" . "`n"
XML_out .= "    <AllowHardTerminate>true</AllowHardTerminate>" . "`n"
XML_out .= "    <StartWhenAvailable>true</StartWhenAvailable>" . "`n"
XML_out .= "    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>" . "`n"
XML_out .= "   <IdleSettings>" . "`n"
XML_out .= "      <Duration>PT10M</Duration>" . "`n"
XML_out .= "      <WaitTimeout>PT1H</WaitTimeout>" . "`n"
XML_out .= "      <StopOnIdleEnd>false</StopOnIdleEnd>" . "`n"
XML_out .= "      <RestartOnIdle>false</RestartOnIdle>" . "`n"
XML_out .= "    </IdleSettings>" . "`n"
XML_out .= "    <AllowStartOnDemand>true</AllowStartOnDemand>" . "`n"
XML_out .= "    <Enabled>true</Enabled>" . "`n"
XML_out .= "   <Hidden>false</Hidden>" . "`n"
XML_out .= "    <RunOnlyIfIdle>false</RunOnlyIfIdle>" . "`n"
XML_out .= "    <WakeToRun>false</WakeToRun>" . "`n"
XML_out .= "    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>" . "`n"
XML_out .= "    <Priority>2</Priority>" . "`n"    
XML_out .= "  </Settings>" . "`n"
XML_out .= "  <Actions Context=""Author"">" . "`n"
XML_out .= "    <Exec>" . "`n"
XML_out .= "      <Command>" . A_ScriptDir . "\AutoHotkeyExecutable.exe" . "</Command>" . "`n"
XML_out .= "      <Arguments>YourScriptFile.ahk</Arguments>" . "`n"
XML_out .= "      <WorkingDirectory>" . A_ScriptDir . "</WorkingDirectory>" . "`n"
XML_out .= "    </Exec>" . "`n"
XML_out .= "  </Actions>" . "`n"
XML_out .= "</Task>"

FileAppend , %XML_Out% , %A_ScriptDir%\XML_Out.xml  
RunWait, %A_WinDir%\System32\schtasks.exe /Create /TN YourAutoHotkeyTask /XML %A_ScriptDir%\XML_Out.xml /F, ,Hide
FileDelete, %A_ScriptDir%\XML_Out.xml

To delete the scheduled task

Code: Select all

 
 RunWait, %A_WinDir%\System32\schtasks.exe /Delete /TN YourAutoHotkeyTask /F, ,Hide
edit: forgot to mention, if I have UAC turned on, the ahk tray icon doesn't appear at startup. The process is running but no tray icon, some kind of bug. Putting in a Reload command in the auto-execute section solved that for me (luckily reload won't trigger the UAC prompt, nor will it re-send script parameters so you can still control what happens in the script specifically at windows startup) . Another way is to add a small delay to the startup (the task scheduler only allows 30 second minimum delay, which is too long!). I think there is a special place in hell for the person(s) who created the Task Scheduler given how badly it hobbles startup items :mrgreen:

Code: Select all

<LogonTrigger>
      <Enabled>true</Enabled>
      <Delay>PT3S</Delay>  ;3 second delay, try longer if the tray icon refuses to appear with UAC enabled
</LogonTrigger>
edit2: seems like the <Priority>2</Priority> is solving the trayicon-not-appearing problem as well.
edit3: now the tray icon is showing even with the default <Priority>7</Priority>. The only thing I can think of is I ran CCleaner in the last 24 hours, maybe that cleaned out whatever was causing the tray icon bug.
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: DllCall: adjust memory priority of script  Topic is solved

16 Jul 2017, 12:28

Using <Priority>4</Priority> in the XMLs for the two AutoHotkey scheduled tasks I have here on my Windows 10 system has always worked fine for me (they start with normal process priority, normal I/O priority and normal page priority). Still, if you want to have a go at setting them yourself for the current process, then call SetPriorities():

Code: Select all

SetPriorities(priority := "N", ioPriority := 2, pagePriority := 5)
{
	static hCurrProc := DllCall("GetCurrentProcess", "Ptr"), ProcessIoPriority := 33, ProcessPagePriority := 39
	Process Priority,, %priority%
	; Thanks to Process Hacker: 
	DllCall("ntdll\NtSetInformationProcess", "Ptr", hCurrProc, "UInt", ProcessIoPriority, "UInt*", ioPriority, "UInt", 4)
	,DllCall("ntdll\NtSetInformationProcess", "Ptr", hCurrProc, "UInt", ProcessPagePriority, "UInt*", pagePriority, "UInt", 4)
}
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: DllCall: adjust memory priority of script

16 Jul 2017, 23:27

qwerty12 wrote:Using <Priority>4</Priority> in the XMLs for the two AutoHotkey scheduled tasks I have here on my Windows 10 system has always worked fine for me (they start with normal process priority, normal I/O priority and normal page priority). Still, if you want to have a go at setting them yourself for the current process, then call SetPriorities():

Code: Select all

SetPriorities(priority := "N", ioPriority := 2, pagePriority := 5)
{
	static hCurrProc := DllCall("GetCurrentProcess", "Ptr"), ProcessIoPriority := 33, ProcessPagePriority := 39
	Process Priority,, %priority%
	; Thanks to Process Hacker: 
	DllCall("ntdll\NtSetInformationProcess", "Ptr", hCurrProc, "UInt", ProcessIoPriority, "UInt*", ioPriority, "UInt", 4)
	,DllCall("ntdll\NtSetInformationProcess", "Ptr", hCurrProc, "UInt", ProcessPagePriority, "UInt*", pagePriority, "UInt", 4)
}
You bloody beauty it works! :bravo: :dance: :clap: :thumbup:

I'm still paranoid it won't work on some systems, so I'm still going to set <Priority>2</Priority> in the XML just to be sure :)

Actually would you mind telling me what priorities you get on your Win10 machine with <Priority>2</Priority> in the XML and NOT calling SetPriorities()?
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: DllCall: adjust memory priority of script

17 Jul 2017, 01:47

I should also mention that calling Reload is equivalent to calling Process, Priority, ,N , which doesn't affect I/O priority or Memory Priority (or pagefile priority as you have called it).

So the way I am doing it:

Code: Select all

XML_out .= "<Priority>2</Priority>"   ;On my Win7x64 this results in I/O Priority = Normal, Priority = 10 ("Above Normal"), Memory Priority = 5 (the default for Normal processes).  I am deliberately setting it slightly high here in case SetPriorities() fails; better too high than too low and have a sluggish script.

Reload  ;to prevent tray icon disappearing bug, this resets it back to I/O Priority = Normal, Priority = 8 ("Normal") , Memory Priority = 5 
Process, Priority, ,%usersDesiredValue%   ; only alters Priority and leaves I/O Priority and Memory Priority at their current values.

;SetPriorities()
ioPriority := 2 ;0=VeryLow , 1=Low , 2=Normal , 3=High+
pagePriority := 5 ;"Memory Priority" in Process Explorer
ProcessIoPriority := 33 
ProcessPagePriority := 39
hCurrProc := DllCall("GetCurrentProcess", "Ptr") 
DllCall("ntdll\NtSetInformationProcess", "Ptr", hCurrProc, "UInt", ProcessIoPriority, "UInt*", ioPriority, "UInt", 4)
DllCall("ntdll\NtSetInformationProcess", "Ptr", hCurrProc, "UInt", ProcessPagePriority, "UInt*", pagePriority, "UInt", 4)
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: DllCall: adjust memory priority of script

17 Jul 2017, 07:59

pneumatic wrote:Actually would you mind telling me what priorities you get on your Win10 machine with <Priority>2</Priority> in the XML and NOT calling SetPriorities()?
With a simple task that's "configured for Windows 10": above normal process priority (10), normal I/O priority (both Process Hacker and Process Explorer say "Normal" here) and normal page priority (5)
pneumatic wrote:(or pagefile priority as you have called it).
I'm just going by whatever Process Hacker (which I've always considered as the best task manager around) terms the thing :)
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: DllCall: adjust memory priority of script

17 Jul 2017, 08:30

qwerty12 wrote:
With a simple task that's "configured for Windows 10": above normal process priority (10), normal I/O priority (both Process Hacker and Process Explorer say "Normal" here) and normal page priority (5)
Thanks, I'm getting the same values here on Win7.

But, when I set <Priority>4</Priority> I'm getting a different value to you for the page priority.

On my system it's like this:

XML priority: IO priority, CPU priority, page priority
7: low,6,3
6: normal,8,3
5: normal,8,4
4: normal,8,4
3: normal,10,4
2: normal,10,5

Remember the default for normal tasks is "normal,8,5" so the only way I can get normal page priority is by using <Priority>2</Priority> in XML, and then reducing CPU priority back down to 8 with Process, Priority,, N (or reload which also does the same thing).

Sorry to be a pain but are you able to confirm that <Priority>4</Priority> on your machine does in fact result in page priority of 5 and not 4?
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: DllCall: adjust memory priority of script

17 Jul 2017, 08:58

pneumatic wrote:Sorry to be a pain but are you able to confirm that <Priority>4</Priority> on your machine does in fact result in page priority of 5 and not 4?
For this task
Spoiler
set to run this amazing script
Spoiler
I get the following:

Image
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: DllCall: adjust memory priority of script

17 Jul 2017, 22:34

Thanks. Looks like Windows 10 sets the priorities right. Must be a bug in Windows 7 or something specific to my machine causing it. The only thing I notice is the "task version" tag is 1.4 vs 1.2, maybe they changed it in between.
Testertime
Posts: 7
Joined: 22 Aug 2017, 16:44

Re: DllCall: adjust memory priority of script

03 Jul 2020, 01:00

Can anyone please tell how to set these priorities on other processes? I know how to elevate a script, but I'm stuck how to get this "hCurrProc" for another process:
pneumatic wrote:
17 Jul 2017, 01:47
hCurrProc := DllCall("GetCurrentProcess", "Ptr")
Edit: Got it, thanks to this awesome post: https://www.autohotkey.com/boards/viewtopic.php?p=190156#p190156

The trick is to change the line above into this:

Code: Select all

hCurrProc := DllCall("OpenProcess", "UInt", PROCESS_SET_INFORMATION := 0x0200, "Int", 0, "UInt", DesiredPID, "Ptr")
"DesiredPID" needs to be defined as a variable before, or just replace it with a process ID (for example 1234). Getting process IDs automatically is another topic though :P At the end of the code, do not forget to use this:

Code: Select all

DllCall("CloseHandle", "Ptr", hCurrProc) 

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: catquas, jpsala and 172 guests