Windows 8/10中的UWP(或#34; Metro")应用程序在不在前台时可以暂停。处于此状态的应用程序继续存在,但不再消耗CPU时间。看起来这种改变是为了提高平板电脑和手机等低功耗/存储设备的性能。
在这种状态下检测进程的最优雅,最简单的方法是什么?
目前我可以看到两种可能的解决方案:
调用NtQuerySystemInformation()并枚举每个进程和每个线程。一个过程被暂停"如果所有线程都处于挂起状态。这种方法需要大量代码,而且关键是NtQuerySystemInformation()只是半文档化的,可以在未来的操作系统中删除。 NtQueryInformationProcess()也可以提供具有相同问题的类似解决方案。
调用GetProcessTimes()并记录每个进程的计数器。等待一段很长的时间(分钟)并再次检查。如果流程计数器没有改变,则假设该流程被暂停。我承认这是一个黑客,但如果时间段足够长,也许可以工作。
有更优雅的方式吗?
答案 0 :(得分:2)
这个存在PROCESS_EXTENDED_BASIC_INFORMATION
- 这个answer中描述的标志的含义。你需要IsFrozen
标志。因此,您需要具有PROCESS_QUERY_LIMITED_INFORMATION
访问权限的开放流程(为了对所有流程执行此操作,您需要在令牌中启用SE_DEBUG_PRIVILEGE
)。并以NtQuerySystemInformation
和PROCESS_EXTENDED_BASIC_INFORMATION
作为输入来致电ProcessBasicInformation
。为枚举所有流程,我们可以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);
}