Python Invalid arguments using ctypes when calling SetProcessInformation
Question:
I’m trying to set a process in Eco QOS mode (windows specific, allows power saving for a process, see here) with the SetProcessInformation windows API function. My final goal will be to enable the eco QOS for all the process I launch with subprocess.popen() function that do heavy workloads.
Here is my code (Python 3.11) :
import ctypes
from ctypes import windll, wintypes
from typing import Any
class PROCESS_POWER_THROTTLING_STATE(ctypes.Structure):
_fields_ = [
('Version', wintypes.ULONG),
('ControlMask', wintypes.ULONG),
('StateMask', wintypes.ULONG)
]
def setEcoQOS(pid: int, enable: bool):
PROCESS_SET_INFORMATION = 0x0200
PROCESS_POWER_THROTTLING_CURRENT_VERSION = 1
PROCESS_POWER_THROTTLING_EXECUTION_SPEED = 0x1
ProcessPowerThrottling = 4
GetLastError = windll.kernel32.GetLastError
SetProcessInformation = windll.kernel32.SetProcessInformation
SetProcessInformation.argtypes = (
wintypes.HANDLE, # HANDLE hProcess
wintypes.DWORD, # ProcessInformationClass ProcessInformationClass
wintypes.LPVOID, # LPVOID ProcessInformation
wintypes.DWORD, # DWORD ProcessInformationSize
)
SetProcessInformation.restype = wintypes.BOOL
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = (
wintypes.DWORD, # DWORD dwDesiredAccess
wintypes.BOOL, # BOOL bInheritHandle
wintypes.DWORD, # DWORD dwProcessId
)
OpenProcess.restype = wintypes.HANDLE
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = (
wintypes.HANDLE, # HANDLE hObject
)
CloseHandle.restype = wintypes.BOOL
hwnd = OpenProcess(PROCESS_SET_INFORMATION, False, pid)
if hwnd == 0:
raise Exception(f'could not open process pid {pid} error={GetLastError()}')
PowerThrottling = PROCESS_POWER_THROTTLING_STATE()
PowerThrottling.Version = 1
PowerThrottling.ControlMask = 0
PowerThrottling.StateMask = 0
if enable:
PowerThrottling.StateMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED
ret = SetProcessInformation(hwnd, ProcessPowerThrottling, ctypes.byref(PowerThrottling), ctypes.sizeof(PowerThrottling))
if ret == False:
raise Exception(f'could not set the process information error={GetLastError()}')
ret = CloseHandle(hwnd)
if ret == False:
raise Exception(f'could not close the handle error={GetLastError()}')
pid = 10688 # SET YOUR PID HERE
setEcoQOS(pid, True)
SetProcessInformation documentation here
GetLastError() after SetProcessInformation return 87 which mean my arguments provided to the function are invalid and I cannot manage to fix it. SetProcessInformation expect a LPVOID so I’m probably doing something wrong here.
Any ideas ?
Answers:
I don’t have Windows 11 to test it, but I think ControlMask
should be:
PowerThrottling.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED
It’s a bitmask and that’s the only bit defined to control anything.
StateMask
then would specify on (PROCESS_POWER_THROTTLING_EXECUTION_SPEED
) or off (0
) although that isn’t spelled out well in the documentation.
I’m trying to set a process in Eco QOS mode (windows specific, allows power saving for a process, see here) with the SetProcessInformation windows API function. My final goal will be to enable the eco QOS for all the process I launch with subprocess.popen() function that do heavy workloads.
Here is my code (Python 3.11) :
import ctypes
from ctypes import windll, wintypes
from typing import Any
class PROCESS_POWER_THROTTLING_STATE(ctypes.Structure):
_fields_ = [
('Version', wintypes.ULONG),
('ControlMask', wintypes.ULONG),
('StateMask', wintypes.ULONG)
]
def setEcoQOS(pid: int, enable: bool):
PROCESS_SET_INFORMATION = 0x0200
PROCESS_POWER_THROTTLING_CURRENT_VERSION = 1
PROCESS_POWER_THROTTLING_EXECUTION_SPEED = 0x1
ProcessPowerThrottling = 4
GetLastError = windll.kernel32.GetLastError
SetProcessInformation = windll.kernel32.SetProcessInformation
SetProcessInformation.argtypes = (
wintypes.HANDLE, # HANDLE hProcess
wintypes.DWORD, # ProcessInformationClass ProcessInformationClass
wintypes.LPVOID, # LPVOID ProcessInformation
wintypes.DWORD, # DWORD ProcessInformationSize
)
SetProcessInformation.restype = wintypes.BOOL
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = (
wintypes.DWORD, # DWORD dwDesiredAccess
wintypes.BOOL, # BOOL bInheritHandle
wintypes.DWORD, # DWORD dwProcessId
)
OpenProcess.restype = wintypes.HANDLE
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = (
wintypes.HANDLE, # HANDLE hObject
)
CloseHandle.restype = wintypes.BOOL
hwnd = OpenProcess(PROCESS_SET_INFORMATION, False, pid)
if hwnd == 0:
raise Exception(f'could not open process pid {pid} error={GetLastError()}')
PowerThrottling = PROCESS_POWER_THROTTLING_STATE()
PowerThrottling.Version = 1
PowerThrottling.ControlMask = 0
PowerThrottling.StateMask = 0
if enable:
PowerThrottling.StateMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED
ret = SetProcessInformation(hwnd, ProcessPowerThrottling, ctypes.byref(PowerThrottling), ctypes.sizeof(PowerThrottling))
if ret == False:
raise Exception(f'could not set the process information error={GetLastError()}')
ret = CloseHandle(hwnd)
if ret == False:
raise Exception(f'could not close the handle error={GetLastError()}')
pid = 10688 # SET YOUR PID HERE
setEcoQOS(pid, True)
SetProcessInformation documentation here
GetLastError() after SetProcessInformation return 87 which mean my arguments provided to the function are invalid and I cannot manage to fix it. SetProcessInformation expect a LPVOID so I’m probably doing something wrong here.
Any ideas ?
I don’t have Windows 11 to test it, but I think ControlMask
should be:
PowerThrottling.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED
It’s a bitmask and that’s the only bit defined to control anything.
StateMask
then would specify on (PROCESS_POWER_THROTTLING_EXECUTION_SPEED
) or off (0
) although that isn’t spelled out well in the documentation.