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 ?

Asked By: notsoeasy

||

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.

Answered By: Mark Tolonen
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.