1

我有一个程序(作为后台进程运行),我在其中安装了一个钩子来捕获 EVENT_SYSTEM_FOREGROUND 事件(即 - 当用户在窗口之间切换时)。为钩子注册的回调基本上记录了用户从哪个应用程序(进程 exe 文件名)切换以及他们切换了哪个应用程序。

我想添加一些代码来检查他们切换的应用程序是否仍然处于活动状态(如果不是,我们假设他们已经关闭它,这就是将新窗口带入前台的原因)。我正在通过尝试使用OpenProcess创建上一个 PID 的句柄来测试它的存在

    //Check prev pid still exists - if not, assume the previous app has been closed
    HANDLE hPrevProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,false,g_prevPid);
    if (hPrevProc==NULL){
        prevProcStillRunning=false;
    }
    else{
        CloseHandle(hPrevProc);
    }

上面代码的假设:
g_prevPid 填充了一个 PID - 我已经验证了这个
prevProcStillRunning 已经初始化为 true

上面代码的问题在于,出于某种原因,即使用户退出了应用程序(例如 notepad.exe)。在它们退出后长达 10 秒内,此测试仍然通过(即 - hPrevProc 被初始化)。尽管我可以在任务管理器中看到 Notepad.exe 进程已经消失(是的,我只打开了它的一个实例),但不知何故,OpenProcess 行仍然可以获取该 PID 的句柄。我猜想PID实际上仍然存在,但它可能处于终止状态。我发现如果这段代码被多次调用,最终它会返回 null。

我想找到一种更好的方法来测试 hPrevProc 是否仍然处于活动状态。我尝试使用GetExitCodeProcess函数对此进行测试,但这似乎只是给了我 PID,我什至不确定这是否是正确的方法。

任何帮助表示赞赏。

4

2 回答 2

2

该进程至少在它有一个打开的句柄时终止后仍然存在于系统中。

了解进程是否仍处于活动状态的唯一万无一失的方法是:

  • 确保进程不能以代码 STILL_ACTIVE (259) 退出
  • 尝试打开进程(OpenProcess)-> 如果不能,则终止
  • 读取退出进程代码 ( GetExitCodeProcess) -> 如果不是 STILL_ACTIVE,则终止进程。

你的代码可能变成:

//Check prev pid still exists - if not, assume the previous app has been closed
HANDLE hPrevProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,false,g_prevPid);
if (hPrevProc==NULL){
    prevProcStillRunning=false;
}
else{
    DWORD cr;
    if ((GetExitCodeProcess(hPrevProc, &cr) == 0) || (cr != STILL_ACTIVE)) {
        prevProcStillRunning=false;
    }
    CloseHandle(hPrevProc);
}

无论如何,关闭 GUI 应用程序涉及不同的步骤:

  • GUI 元素被破坏
  • 消息循环结束
  • 最终应用程序可以执行后台操作(将状态保存到文件等)
  • 主过程返回退出代码
  • 系统知道应用程序已终止

该事件将在主窗口关闭后立即发送,这可能在应用程序实际停止之前的某个时间发生。一个很好的例子是 Firefox。如果您关闭窗口并立即尝试启动新进程,则会收到错误消息,因为即使 UI 消失了,该进程仍不会终止。更糟糕的是,当您关闭 UI 时,您可以找到简单地进入后台的应用程序,并允许用户通过对任务栏状态区域中的图标的操作(Shell_NotifyIcon及其回调)再次打开 UI。这对于在后台工作的其他应用程序(网络服务器、防火墙等)的服务很常见。在这种情况下,用户界面消失了,但进程不会终止。

TL/DR:UI 分离和拥有它的进程终止之间的时间是可变的,取决于系统负载和关闭 UI 后进程的后台活动。您可以尝试为此使用延迟,但我不能保证任何事情......

于 2015-09-22T14:23:43.740 回答
1

可能某些进程(也许是你的?)仍然持有该进程的有效句柄。在所有句柄上调用 CloseHandle 之前,系统会维护允许访问其进程数据的内部记录。这很重要,因为正如您所说,必须可以在关闭的进程上调用 GetExitCodeProcess,也有人可能希望等待它通过 WaitForSingleObject 停止。

还要小心 PID,它们可以重复使用 - 所以理论上你可以在其他一些新打开的进程上调用 OpenProcess。

至于检查给定进程是否不是僵尸,您可以尝试使用 EnumWindows 枚举顶级窗口,并检查它们中的任何一个是否与给定 PID 相关联(获取窗口的 PID 使用 GetWindowThreadProcessID)。

于 2015-09-22T14:15:47.267 回答