【问题标题】:Detect "suspended" Windows 8/10 process检测“暂停”的 Windows 8/10 进程
【发布时间】:2018-10-14 19:14:44
【问题描述】:

Windows 8/10 中的 UWP(或“Metro”)应用可以在它们不在前台时暂停。处于此状态的应用程序继续存在,但不再消耗 CPU 时间。引入此更改似乎是为了提高平板电脑和手机等低功耗/存储设备的性能。

在这种状态下检测进程最优雅、最简单的方法是什么?

目前我可以看到两种可能的解决方案:

  1. 调用 NtQuerySystemInformation() 并枚举每个进程和每个线程。如果所有线程都处于挂起状态,则进程被“挂起”。这种方法需要大量代码,关键是 NtQuerySystemInformation() 只是半文档化的,可以在未来的操作系统中删除。 NtQueryInformationProcess() 也可以针对相同的问题提供类似的解决方案。

  2. 调用 GetProcessTimes() 并记录每个进程的计数器。等待较长的时间(分钟)并再次检查。如果进程计数器未更改,则假定进程已暂停。我承认这是一个 hack,但如果时间段足够长,也许可以工作。

有没有更优雅的方式?

【问题讨论】:

  • 假设您可以确定此状态,您将如何处理该信息?
  • 我维护了一个执行系统维护任务的企业应用程序。当其他“重要”程序正在运行时,可以推迟任务。 Windows 8/10 改变了这一点,因为 UWP 程序现在进入挂起状态并在那里停留很长时间。因此,现在有必要将长期挂起的程序与实时运行的进程区分开来。

标签: windows api winapi uwp power-management


【解决方案1】:

为此存在PROCESS_EXTENDED_BASIC_INFORMATION - 在此answer 中描述的其中标志的含义。你需要IsFrozen 标志。因此,您需要具有PROCESS_QUERY_LIMITED_INFORMATION 访问权限的打开进程(要对所有进程执行此操作,您需要在令牌中启用SE_DEBUG_PRIVILEGE)。并使用ProcessBasicInformationPROCESS_EXTENDED_BASIC_INFORMATION 作为输入调用NtQuerySystemInformation。对于枚举所有进程,我们可以使用NtQuerySystemInformationSystemProcessInformation。当然可以使用CreateToolhelp32Snapshot + Process32First + Process32Next 但是这个api效率很低,比较直接调用NtQuerySystemInformation

还可以枚举进程中的所有线程并检查它的状态以及如果状态等待 - 等待原因。这很容易,因为所有这些信息已经通过使用SystemProcessInformation 一次调用NtQuerySystemInformation 返回。有了这个,我们不需要开放的流程。通常这两种方式都会给出相同的结果(对于挂起/冻结的)进程,但是使用 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);
}

【讨论】:

  • 谢谢。我会尝试这两种方法。很遗憾,两者都需要 SE_DEBUG_PRIVILEGE 但对我的应用程序来说不是问题。
  • @jimc - SE_DEBUG_PRIVILEGE 仅在您想要打开所有进程(包括受保护的系统进程)时才启用。但是它是可选的 - 当您尝试打开某些进程时,您只是被拒绝访问。
  • 我只想指出 IsFrozen 不适用于正常进程,仅适用于 UWP(至少根据我的测试)。
  • @MiroslavPolicki 在调试器下暂停的任何进程也可以IsFrozen
  • @RbMm 有趣。不过我用了NtSuspendProcess
猜你喜欢
  • 1970-01-01
  • 2020-02-06
  • 1970-01-01
  • 2011-04-13
  • 2010-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-17
相关资源
最近更新 更多