4
\$\begingroup\$

(See the next iteration .)

I have this program (call it, for example, prioset.exe), that asks for two command line arguments: (1) the target process PID, (2) a priority class selector in the range 0...5 (as specified in the WinAPI docs, where 0 stands for idle priority class, and 5 for real-time priority class.

My main concern is whether we can pass to dwDesiredAccess of OpenProcess more restricted flags than PROCESS_ALL_ACCESS.

Here's the code:

#include <Windows.h>
#include <iostream>
#include <sstream>
#include <string>

using std::wcout;
using std::wcerr;
using std::wstringstream;

static void MyPrintHelp();
static void MyAttemptToChangeProcessPriorityClass(DWORD pid, 
                                                  DWORD priorityClass);

static bool MyCheckPriorityClassSelection(DWORD priorityClassSelection);
static DWORD MyConvertPriorityClassSelectionToFlag(DWORD priorityClassSelection);

int wmain(int argc, const wchar_t** args) {
    if (argc != 3) {
        MyPrintHelp();
        return 0;
    }

    DWORD pid;
    DWORD priorityClassSelection;

    wstringstream wss;
    wss << args[1] << L" " << args[2];
    wss >> pid;

    if (wss.fail() || wss.eof()) {
        wcerr << L"Error: bad PID (" << args[1] << L"\n";
        return EXIT_FAILURE;
    }

    wss >> priorityClassSelection;

    if (wss.fail()) {
        wcerr << L"Error: bad priority class selector (" << args[2] << L")\n";
        return EXIT_FAILURE;
    }

    if (!MyCheckPriorityClassSelection(priorityClassSelection)) {
        wcerr << L"Error: bad priority class selection: " 
              << priorityClassSelection
              << L". Must be between 0 and 5, inclusively.\n";

        return EXIT_FAILURE;
    }

    MyAttemptToChangeProcessPriorityClass(
        pid,
        MyConvertPriorityClassSelectionToFlag(
            priorityClassSelection));
}

static void MyPrintHelp() {
    wcout << L"prioset.exe PID PRIORITY\n";
}

static std::wstring GetLastErrorAsString(DWORD errorMessageId)
{
    LPWSTR messageBuffer = nullptr;

    size_t size = FormatMessage(
                        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
                        FORMAT_MESSAGE_FROM_SYSTEM | 
                        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, 
        errorMessageId, 
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
        (LPWSTR) &messageBuffer, 0, NULL);

    //Copy the error message into a std::string.
    std::wstring message(messageBuffer, size);

    //Free the Win32's string's buffer.
    LocalFree(messageBuffer);
    return message;
}

static void MyAttemptToChangeProcessPriorityClass(DWORD pid, DWORD priorityClass) {
    HANDLE processHandle = 
        ::OpenProcess(PROCESS_ALL_ACCESS, 
            false, 
            pid);

    if (processHandle == NULL) {
        wcout << L"Error: could not open a process with PID " << pid << "\n";
        return;
    }

    DWORD oldPriorityClass = GetPriorityClass(processHandle);

    if (oldPriorityClass == 0) {
        DWORD errorCode = ::GetLastError();
        wcerr << L"Error: " 
              << GetLastErrorAsString(errorCode) 
              << L"Error code: " 
              << errorCode 
              << L"\n";

        ::CloseHandle(processHandle);
        return;
    }

    if (!::SetPriorityClass(processHandle, priorityClass)) {
        DWORD errorCode = GetLastError();
        wcerr << L"Error: "
            << GetLastErrorAsString(errorCode)
            << L"Error code: "
            << errorCode
            << L"\n";

        ::CloseHandle(processHandle);
        return;
    }

    DWORD newPriorityClass = GetPriorityClass(processHandle);

    if (newPriorityClass == 0) {
        DWORD errorCode = ::GetLastError();
        wcerr << L"Error: "
            << GetLastErrorAsString(errorCode)
            << L"Error code: "
            << errorCode
            << L"\n";

        ::CloseHandle(processHandle);
        return;
    }

    ::CloseHandle(processHandle);

    if (newPriorityClass == priorityClass) {
        wcout << L"Successfully changed the priority class from "
              << oldPriorityClass 
              << L" to "
              << newPriorityClass
              << L"\n";
    } else {
        wcout << L"Could not change the priority class from "
              << oldPriorityClass 
              << L" to "
              << priorityClass
              << L" Instead, "
              << newPriorityClass
              << L" is used as a new priority class.\n";
    }
}

static bool MyCheckPriorityClassSelection(DWORD priorityClassSelection) {
    return priorityClassSelection >= 0 || priorityClassSelection < 6;
}

static DWORD MyConvertPriorityClassSelectionToFlag(
    DWORD priorityClassSelection) {
    switch (priorityClassSelection) {
        case 0:
            return IDLE_PRIORITY_CLASS;

         case 1:
            return BELOW_NORMAL_PRIORITY_CLASS;

         case 2:
            return NORMAL_PRIORITY_CLASS;

         case 3:
            return ABOVE_NORMAL_PRIORITY_CLASS;

         case 4:
            return HIGH_PRIORITY_CLASS;

         case 5:
            return REALTIME_PRIORITY_CLASS;

         default:
            throw L"Bad priority class selection.";
    }
}

```
\$\endgroup\$
2
  • 1
    \$\begingroup\$ I cannot test it currently, but from the documentation I would assume that PROCESS_QUERY_INFORMATION (or PROCESS_QUERY_LIMITED_INFORMATION) PROCESS_SET_INFORMATION is sufficient. \$\endgroup\$
    – Martin R
    Jul 12, 2022 at 11:04
  • \$\begingroup\$ @MartinR Just checked it. Yes, you are correct. \$\endgroup\$
    – coderodde
    Jul 12, 2022 at 11:12

1 Answer 1

4
\$\begingroup\$

Some remarks.

The error reporting is not consistent:

  • Most error messages go to standard error (which is good), but some go to standard output.

  • Most invalid input causes the program to exit with EXIT_FAILURE (which is good), but failures in MyAttemptToChangeProcessPriorityClass() make the program terminate normally.

  • An error in the integer to priority conversion throws a C++ exception.

According to the documentation it suffices to call OpenProcess() with

PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_INFORMATION

You have two separate functions MyCheckPriorityClassSelection() which checks the range of the (integer) input, and MyConvertPriorityClassSelectionToFlag() which converts that integer to a priority value. That bears a risk if additional priorities are introduced.

I would combine that into a single function which returns a priority value or indicates an error.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.