Обнаружить «приостановленный» процесс Windows 8/10 - PullRequest
0 голосов
/ 04 мая 2018

Приложения UWP (или «Метро») в Windows 8/10 могут быть приостановлены, если они не находятся на переднем плане. Приложения в этом состоянии продолжают существовать, но больше не потребляют процессорное время. Похоже, что это изменение было введено для повышения производительности на устройствах с низким энергопотреблением / запоминающих устройствах, таких как планшеты и телефоны.

Какой самый элегантный и простой способ обнаружить процесс в этом состоянии?

На данный момент я вижу 2 возможных решения:

  1. Вызовите NtQuerySystemInformation () и перечислите каждый процесс и каждый поток. Процесс "приостановлен", если все потоки находятся в состоянии ожидания. Этот подход потребует большого количества кода, и критически NtQuerySystemInformation () является лишь частично документированным и может быть удален в будущей ОС. NtQueryInformationProcess () также может предложить аналогичное решение с той же проблемой.

  2. Вызовите GetProcessTimes () и запишите счетчики для каждого процесса. Подождите некоторое время (минуты) и проверьте снова. Если счетчики процесса не изменились, то предположим, что процесс приостановлен. Я признаю, что это взлом, но, возможно, может сработать, если период времени достаточно длинный.

Есть ли более элегантный способ?

1 Ответ

0 голосов
/ 04 мая 2018

для этого существует PROCESS_EXTENDED_BASIC_INFORMATION - значение флагов, описанных в этом ответе . Вам нужен флаг IsFrozen. поэтому вам нужен открытый процесс с доступом PROCESS_QUERY_LIMITED_INFORMATION (для этого нужно включить все процессы SE_DEBUG_PRIVILEGE в токене). и вызовите NtQuerySystemInformation с ProcessBasicInformation и PROCESS_EXTENDED_BASIC_INFORMATION в качестве ввода. для перечисления всех процессов мы можем использовать NtQuerySystemInformation с SystemProcessInformation. конечно можно и использовать CreateToolhelp32Snapshot + Process32First + Process32Next но этот API очень неэффективен, сравните прямой вызов с NtQuerySystemInformation

также возможно перечислить все потоки в процессе и проверить его состояние, а если состояние ждать - причина ожидания. это очень просто, потому что вся эта информация уже возвращена за один вызов NtQuerySystemInformation с SystemProcessInformation. При этом нам не нужны открытые процессы. обычно оба эти способа дают одинаковый результат (для приостановленных / замороженных) процессов, но, тем не менее, использование IsFrozen является наиболее правильным решением.

void PrintSuspended()
{
    BOOLEAN b;
    RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &b);

    ULONG cb = 0x1000;
    NTSTATUS status;
    do 
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        if (PBYTE buf = new BYTE[cb])
        {
            if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                union {
                    PBYTE pb;
                    SYSTEM_PROCESS_INFORMATION* spi;
                };

                pb = buf;

                ULONG NextEntryOffset = 0;
                do 
                {
                    pb += NextEntryOffset;

                    if (!spi->UniqueProcessId)
                    {
                        continue;
                    }

                    if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, 
                        (ULONG)(ULONG_PTR)spi->UniqueProcessId))
                    {
                        PROCESS_EXTENDED_BASIC_INFORMATION pebi;
                        if (0 <= NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pebi, sizeof(pebi), 0) &&
                            pebi.Size >= sizeof(pebi))
                        {
                            if (pebi.IsFrozen)
                            {
                                DbgPrint("f:%x %wZ\n", spi->UniqueProcessId, spi->ImageName);
                            }
                        }
                        CloseHandle(hProcess);
                    }

                    if (ULONG NumberOfThreads = spi->NumberOfThreads)
                    {
                        SYSTEM_THREAD_INFORMATION* TH = spi->TH;
                        do 
                        {
                            if (TH->ThreadState != StateWait || TH->WaitReason != Suspended)
                            {
                                break;
                            }
                        } while (TH++, --NumberOfThreads);

                        if (!NumberOfThreads)
                        {
                            DbgPrint("s:%x %wZ\n", spi->UniqueProcessId, spi->ImageName);
                        }
                    }

                } while (NextEntryOffset = spi->NextEntryOffset);
            }
            delete [] buf;
        }
    } while (status == STATUS_INFO_LENGTH_MISMATCH);
}
...