【问题标题】:Working with a shut-down state of a user session on Windows?在 Windows 上使用用户会话的关闭状态?
【发布时间】:2012-11-10 07:49:10
【问题描述】:

假设,如果我打开记事本,在其中输入一些内容但不保存,然后从同一个用户会话中调用以下 API:

ExitWindowsEx(EWX_LOGOFF, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED);

该用户会话将进入“关闭状态”,其中操作系统将显示一个覆盖窗口,其中显示记事本阻止系统注销用户的消息。在用户单击“取消”或“强制退出”按钮之前,此叠加层不会消失。

所以两部分的问题:

  1. 有没有办法知道哪些进程阻塞了注销/关闭进程?

  2. 有没有办法以编程方式取消此用户会话“关闭状态”?

PS。这个状态可以通过调用GetSystemMetrics(SM_SHUTTINGDOWN)来检测;

编辑:与下面的答案相反,我不是试图阻止系统关闭,也不是任何用户模式进程都“挂起”。 p>

EDIT2:这是我尝试取消/关闭的叠加层的屏幕截图:

【问题讨论】:

  • Windows Vista 是更严格的注销/关闭的开始。操作系统将会有所作为。
  • 从技术上讲我需要它,从 XP 开始。
  • 这可能会有所帮助forums.devx.com/…
  • @AnanthaSharma:谢谢,但这并不是我所需要的。你看,我所说的情况是在启动注销过程之后的那一刻。
  • 那么你想在哪里检测到这个?在用户模式进程中还是在您的服务中?

标签: c++ windows winapi shutdown logoff


【解决方案1】:

问题 2:“有没有办法以编程方式取消此关闭状态?”

简而言之,答案不是真的。而且您也不应该真正以编程方式停止关机,除非:关机会导致严重的数据丢失或严重影响后续系统启动时的用户体验。但仅举一个例子:想象一台计算机过热 - 以编程方式停止关机可能会导致系统崩溃(以及非常愤怒的用户)。

系统关闭也不是您唯一需要监控的事情。还有休眠和挂起事件(查看 WM_POWERBROADCAST 消息)。

也就是说,Windows 提供了大量用于检测系统关闭的机制。例如:

如果您的应用程序具有消息泵,您可以选择在 Windows 轮询正在运行的应用程序以对 WM_QUERYENDSESSION 进行投票时返回 FALSE,但是从 Vista 开始的 Windows 仍将在超时后强制关闭。从 Vista 开始,您可以(并且需要)ShutdownBlockReasonCreate 在将 false 返回到 WM_QUERYENDSESSION 之后。

如果您的应用程序作为服务运行,您可以使用RegisterServiceCtrHandlerEx 然后SetServiceStatus 通过设置 SERVICE_ACCEPT_PRESHUTDOWN 来获得 3 分钟的关机延期宽限期,这将为您提供 SERVICE_CONTROL_PRESHUTDOWN 通知。当然,您不会收到注销通知,因为服务不受注销的影响。 Pre-Vista 您可以注册 SERVICE_CONTROL_SHUTDOWN 通知。

控制台应用程序(以及 gui 应用程序,但它没有意义)可以使用 SetConsoleCtrlHandler 来获得 CTRL_LOGOFF 和 CTRL_SHUTDOWN_EVENT 的通知。

在低得多的级别上,可以尝试挂钩 API 函数,例如 NTShutdown 甚至 NtSetSystemPowerState,显然是 "the last thing called during ANY type of reboot"。但我强烈建议不要尝试。

也就是说,有一些方法可以真正强烈地坚持不应该关闭系统。 考虑以下几点:

1.) 尝试将您的应用程序注册为第一个接收关机通知。比如:

    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686227(v=vs.85).aspx
    if(!SetProcessShutdownParameters(0x4ff, 0)) //  greedy highest documented System reserved FirstShutdown
    {
        // Fallback
        if(!SetProcessShutdownParameters(0x3ff, 0)) // highest notification range for applications
        {
             // shouldn't happen
        }
    }

2.) 在 WM_QUERYENDSESSION 上返回 FALSE 从 Vista 开始,在 WM_QUERYENDSESSION 返回 false 后调用 ShutdownBlockReasonCreate()。

3.) 告诉 Windows 您需要系统保持正常运行并可用。看一下 http://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspx

SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);

4.) 清理,从 Vista 开始调用 ShutdownBlockReasonDestroy(),然后彻底关闭系统。

您也可以尝试未记录的功能(至少它不再在 MSDN 上)“user32.dll”中的 CancelShutdown,它在某些时候(仍然可能)曾经非常像使用中止标志调用 shutdown.exe。

您的里程可能会有所不同。

【讨论】:

  • 感谢您的详细解释。我同意这一切。但请注意,我并不是要取消关机。我上面引用的 API 将简单地开始注销用户,如果任何进程拒绝您上面列出的结束会话通知,该用户会话将进入无限期的“关闭状态”。他们为什么这么称呼它让我感到震惊......检查我在上面给出的 SM_SHUTTINGDOWN 的 MSDN 链接。这就是我需要取消的?
  • @ahmd0 这就是我想要说明的一点:SM_SHUTTINGDOWN(如果你幸运地通过在正确的时刻调用它来检测它)在 WM_QUERYENDSESSION 发送后设置,尽我所能out - 没有记录的方法可以在进入“关闭状态”后以编程方式取消它。您可以尝试在关闭状态宽限期内枚举进程并尝试终止它们以强制关闭....
  • 好吧,简单地列举它们并没有帮助,因为我确信还有大量 Microsoft 进程也在继续运行,例如 explorer.exe。
【解决方案2】:

根据您的编辑,您的问题会更清楚:

如果您监控 WM_QUERYENDSESSION 并对其做出 FALSE 响应,您可以从仍在运行的进程中轮询一段预定的时间,然后发出带有标志 EWX_FORCEIFHUNG 的 ExitWindowsEx 调用,例如在 WM_ENDSESSION 上。或者您实际上可以在收到 WM_QUERYENDSESSION 时先发制人地调用它 - 问题是:如果强制关闭导致严重的数据丢失怎么办?在这一点上,您对系统用户没有任何好处。

跟随你的 cmets 更新:

这个怎么样:

要找出阻塞的应用程序:

  1. 使用 SetProcessShutdownParameters 将您的应用程序注册为第一个 获取 WM_QUERYENDSESSION 的行。
  2. 对 WM_QUERYENDSESSION 响应 FALSE 并在 Vista 之后调用 ShutdownBlockReasonCreate 为自己争取时间。
  3. 获取链中的第一个窗口 HWND top = GetTopWindow(NULL)
  4. 从 hwnd GetWindowThreadProcessId() 获取进程 ThreadId(将其与您正在运行的应用程序的 ;) 进行比较)
  5. 使用 SendMessageTimeOut 向 hWnd 发送消息 - 如果您收到 没有响应(超时)您可能找到了阻止窗口。转到第 6 步。 如果没有,请跳至 7。
  6. 将 OpenProcess() 与从返回的句柄一起使用 GetWindowThreadProcessId 获取进程句柄并调用 GetModuleBaseName() 获取挂起进程的名称。
  7. 如果您还没有找到挂起的窗口,请枚举下一个窗口 GetNextWindow() 并返回第 4 步。

您也可以尝试使用上面的枚举窗口句柄技术来确定您是否可以获得“挂窗”覆盖层的句柄。这样做可能会让你有机会做一个发送键来取消状态。我敢打赌,您将无法访问它,但我还没有尝试过:)

同样,您的里程可能会有所不同:)

【讨论】:

  • 没有。我并没有试图终止任何进程,也没有任何进程被“挂起”。在我的示例中,记事本无法退出,因为它有未保存的数据,我不想强​​制它退出。我想要做的是终止 Windows“关闭状态”(参见上面的屏幕截图),因为“软”注销不成功。这应该将用户会话恢复到工作状态。这就是我想要做的:)
  • 这样做的目的是为了让我可以运行用户模式进程,因为如果用户会话处于该状态,CreateProcessAsUser 将不会通过。
  • 简短的回答是:你不能 :) 长的回答是:据我所知,你不能在不强制关机的情况下取消挂起状态。
  • 嗯,这似乎是我发现的。虽然那些“特殊”情况呢?我确信覆盖只是一个窗口。有什么方法可以模拟按下“取消”按钮吗?
猜你喜欢
  • 2012-01-18
  • 2013-08-02
  • 1970-01-01
  • 2012-05-19
  • 1970-01-01
  • 2015-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多