【问题标题】:Win32 C++ OpenProceess should return null if user has exited application but doesnt?如果用户已退出应用程序,Win32 C++ OpenProceess 应该返回 null 但没有?
【发布时间】:2015-12-19 13:06:04
【问题描述】:

我有一个程序(作为后台进程运行),我在其中安装了一个挂钩来捕获 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,我什至不确定这是否是正确的方法。

任何帮助表示赞赏。

【问题讨论】:

  • 内核对象的最后一个句柄关闭并因此导致 OpenProcess() 失败的确切时间是完全不可预测的。程序员自愿在他们的机器上安装的典型的收缩包装恶意软件很可能使其存活的时间比预期的要长。
  • 僵尸进程有一个 ID,即使它们已经终止。使用WaitForSingleObject,超时时间为0,查看进程是否已终止。

标签: c++ pid openprocess


【解决方案1】:

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

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

  • 确保进程无法以代码 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 后进程的后台活动。您可以尝试为此使用延迟,但我不能保证任何事情......

【讨论】:

  • 感谢 Serge 的这个建议,我尝试了您在上面发布的代码 - 但同样,如果在新窗口成为焦点时运行“if”语句中的测试,它将获得“STILL_ACTIVE” - 1 秒后再次测试,它将返回另一个值,指示该进程不再存在。
  • @Tommy:恐怕我不能在这里做得更好。请参阅我的编辑以获取解释-不是解决方案:-(
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 2019-03-29
    • 2011-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-27
    • 1970-01-01
    相关资源
    最近更新 更多