17

我知道当 Windows 关闭时,它会向每个应用程序发送一条WM_QUERYENDSESSION消息。这样可以轻松检测 Windows 何时关闭。但是,是否有可能知道计算机是否会关闭电源,或者在 Windows 关闭后是否会重新启动。

我不是特别有希望,考虑到 MSDN 上的文档有这样的说法WM_QUERYENDSESSION:“......不可能确定正在发生的事件”,但 stackoverflow 的累积聪明才智从未停止让我惊讶。

4

4 回答 4

9

在 Windows 7(也可能在 Vista / 8 / Server 中)中,您可以使用系统事件来跟踪 Windows 是正在关闭(并关闭计算机电源)还是只是重新启动。每次启动关机/重启时(通过任何方式 - 单击开始菜单中的按钮,或以编程方式),Windows 7 都会在系统日志中写入一个或两个事件,源 USER32,事件 ID 1074。您可以看到记录的这些事件,如果您从管理工具中打开事件查看器(过滤系统日志以仅查看 ID 1074)。这些事件的描述(消息)包含关闭类型。因此,您可以解析这种类型的最新事件的描述(在启动关闭之后),寻找必要的词(关闭、重新启动/重新启动)。

使用电源按钮正常关闭 Windows 时,我没有尝试查看事件中写入的关闭类型(我通常禁用此功能),但某些网站建议它声明“关机”类型而不是“关机” -所以检查一下,如果你需要确定的话。或者只是寻找“重新启动”类型 - 如果找不到,则假定为“关闭”类型。

在 Windows XP 中,根据我的经验,只有在以编程方式完成关机/重新启动时(例如,在程序安装期间或使用 shutdown.exe 实用程序),才会记录事件 1074。因此它不会注册从 shell(资源管理器)启动的关闭,但也许您可以将此方法与从注册表中读取值结合起来,如另一个答案中所建议的那样。另外,请记住,在 WinXP 中,事件 1074 的消息包含单词“restart”,无论真正的关机类型是什么,因此您应该查看“Shutdown Type:”字段,该字段将显示“shutdown”或“重启”。

与此相关的是,每当 Windows 由于某种原因(例如,如果应用程序不允许作为对 WM_QUERYENDSESSION 的响应而关闭)时无法关闭/重新启动,就会记录一个事件 ID 1073。在这种情况下,该消息还将包含诸如“关机”、“重新启动”或“关机”之类的词 - 在 WinXP 中。对于 Win7,这种类型的事件在我们的例子中不太有用,因为它不会在关机和重启之间产生任何区别。但是对于 WinXP - 如果您只需要拦截关机/重启,执行一些操作,然后继续相应的关机或重启过程 - 它应该可以按预期工作。

于 2013-12-25T02:29:56.450 回答
6

这里

您可以从“HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shutdown Setting”中读取 DWORD 值,以确定用户上次从“关机”对话框中选择的内容。

有点迂回的解决方案,但它应该可以解决问题。

于 2009-06-11T14:13:35.900 回答
5

通常有效的一个技巧是捕获WM_ENDSESSION并记录它。现在跟踪时间。如果系统在合理的周期内(比如 5 分钟)恢复。然后那是重新启动,而不是关机。

想法:如果系统在 5 分钟内恢复正常,用户单击“关机”或“重启”真的很重要吗?

如果您确实需要检测关机(我认为您需要这样做的唯一原因是,如果您依赖于关机与重新启动之间的模糊行为软件差异),您可以调查API hooking相关ExitWindowsEx功能,但我不推荐这种方法。如果您真的需要直接检测到这一点,请重新考虑。

于 2011-01-07T18:45:35.830 回答
2

Windows7 的可能实验解决方案如下。(我不确定这是否适用于其他本地化,因此我将其称为解决方法)

using System.Diagnostics.Eventing.Reader;

namespace MyApp
{
public class RestartDetector : IDisposable
{
    public delegate void OnShutdownRequsted(bool restart);
    public OnShutdownRequsted onShutdownRequsted;

    private EventLogWatcher watcher = null;

    public RestartDetector()
    {
        try
        {
            EventLogQuery subscriptionQuery = new EventLogQuery(
                "System", PathType.LogName, "*[System[Provider[@Name='USER32'] and (EventID=1074)]]");

            watcher = new EventLogWatcher(subscriptionQuery);

            // Make the watcher listen to the EventRecordWritten
            // events.  When this event happens, the callback method
            // (EventLogEventRead) is called.
            watcher.EventRecordWritten +=
                new EventHandler<EventRecordWrittenEventArgs>(
                    EventLogEventRead);

            // Activate the subscription
            watcher.Enabled = true;
        }
        catch (EventLogReadingException e)
        {
        }
    }

    public void EventLogEventRead(object obj, EventRecordWrittenEventArgs arg)
    {
        bool restart = false;
        try
        {
            // Make sure there was no error reading the event.
            if (arg.EventRecord != null)
            {
                String[] xPathRefs = new String[1];
                xPathRefs[0] = "Event/EventData/Data";
                IEnumerable<String> xPathEnum = xPathRefs;

                EventLogPropertySelector logPropertyContext = new EventLogPropertySelector(xPathEnum);
                IList<object> logEventProps = ((EventLogRecord)arg.EventRecord).GetPropertyValues(logPropertyContext);

                string[] eventData = (string[])logEventProps[0];

                foreach (string attribute in eventData)
                {
                    if (attribute.Contains("restart")) { restart = true; break; }
                }
            }
        }
        catch (Exception e)
        {
        }
        finally
        {
            if (onShutdownRequsted != null) { onShutdownRequsted(restart); }
        }   
    }

    public void Dispose()
    {
        // Stop listening to events
        if (watcher != null)
        {
            watcher.Enabled = false;
            watcher.Dispose();
        }
    }
}
}

以下是 PC 重新启动时写入事件日志的 XML 示例:

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="USER32" /> 
  <EventID Qualifiers="32768">1074</EventID> 
  <Level>4</Level> 
  <Task>0</Task> 
  <Keywords>0x80000000000000</Keywords> 
  <TimeCreated SystemTime="2015-12-15T11:10:43.000000000Z" /> 
  <EventRecordID>90416</EventRecordID> 
  <Channel>System</Channel> 
  <Computer>WIN7PC</Computer> 
  <Security UserID="S-1-5-21-1257383181-1549154685-2724014583-1000" /> 
  </System>
- <EventData>
  <Data>C:\Windows\system32\winlogon.exe (WIN7PC)</Data> 
  <Data>WIN7PC</Data> 
  <Data>No title for this reason could be found</Data> 
  <Data>0x500ff</Data> 
  <Data>restart</Data> 
  <Data /> 
  <Data>WIN7PC\WIN7PCUser</Data> 
 <Binary>FF00050000000000000000000000000000000000000000000000000000000000</Binary> 
  </EventData>
  </Event>
于 2015-12-16T14:40:41.887 回答