3

我正在制作一个应用程序,我想在系统关闭之前执行一些数据库查询。我正在使用此代码-

static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
        e.Cancel = true;
        MessageBox.Show("Shut down canceled");
}

我确实执行了此应用程序并尝试关闭系统,并且此代码也捕获了关闭事件,但问题是在显示消息框后它也显示此屏幕 - [我无法发布图像,因为我没有 10点。]

它显示了我的应用程序的名称,它阻止了系统关闭,它还提供了“强制关闭按钮”,我不希望显示此屏幕,因为用户可以在完成执行我的查询之前强制关闭系统。

需要这方面的专家建议,非常感谢。

4

2 回答 2

2

简短可靠的答案:

在任何最新的 Windows 版本上,您可以尝试取消关机,但 Windows 可能会决定忽略您;可悲的是,这是设计使然。如果您必须在进程终止之前完成操作,那么执行此操作的正确位置是在 SessionEnded 处理程序中。如果您有必须在流程终止之前完成的任务,则在完成所有工作之前,您不得从 SessionEnded 处理程序返回(因此您的查询等已完成。)

因此,不是(或者,如果您愿意,也可以)处理 SessionEnding,处理 SessionEnded 并在那里工作:

static void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
    WaitForQueriesToFinishOrSaveState(); // new code
}

您如何实现等待将取决于您的应用程序;如果您需要重新运行查询,您可以在其中执行它们,或者您可能需要 Thread.Join() 或以其他方式等待后台任务完成;但它必须是阻塞等待(所以在完成之前你不需要从函数返回)。

由于您不能完全停止关机,因此在这种情况下尝试取消可能没有什么意义,所以我建议不要在 SessionEnding 中设置 e.Cancel 。在较旧的 Windows 版本上,这更有意义,不幸的是,现在意义不大。

API 文档 还建议不要在 SessionEnding 中做任何重要的工作(包括消息框),而是设置您需要立即返回的任何标志,然后在 SessionEnded 中进行工作。(未经证实:我怀疑如果你没有足够快地返回,这可能会加速用户出现“程序行为不端,你想杀死它们”屏幕,因为 Windows 认为你没有在玩再好不过了。)

幕后花絮:

设置 e.Cancel 向 Windows 表明您不希望会话结束;但是用户仍然可以查看;并且 Windows 可能会出于任何相关的原因决定忽略您的请求。这就是饼干碎的方式。您可能会发现在某些情况下可以使用的 hack,但是没有任何 API 或方法是 Microsoft 批准的,因此现在和将来都可能始终如一地工作。

在幕后,Windows 正在向您的进程的窗口发送一条 WM_QUERYENDSESSION 消息,.NET 会为您接收并转换为 SessionEnding 事件)并将您的取消(或缺少)传递回 Windows;如果不取消则返回 TRUE,如果取消则返回 FALSE。

在此之后,Windows 会查看所有进程的请求,并且根据关闭的原因和其他因素,尽管有这些请求,仍然可能决定继续执行。如果进程不合作,它还可以提醒用户并让他们选择终止进程。

无论您是否处理 WM_QUERYENDSESSION (SessionEnding),您总是有最后一次清理的机会:您会收到一条 WM_ENDSESSION 消息(翻译为 SessionEnded)。棘手的部分是您必须在所有 SessionEnded 处理程序返回之前完成所有重要任务!

一旦 Windows 从它的 WM_ENDSESSION (SessionEnded) 调用中收到回复,就您的应用程序的生命周期而言,所有的赌注都将被取消,并且 Windows 可以随时终止您的进程。

Raymond Chen 最近非常专业地报道了这一点。

http://blogs.msdn.com/b/oldnewthing/archive/2013/06/27/10429232.aspx

作为脚注, SystemEvents.SessionEnded 是一种方便;如果您有一个顶级应用程序窗口,您可以完全绕过它并通过以下方式实现相同的目的:

protected override void WndProc(ref Message m)
{
    if (m.Msg == 0x16) // WM_ENDSESSION
    {
        WaitForQueriesToFinishOrSaveState();
        m.Result = IntPtr.Zero;
        return;
    }
    base.WndProc(ref m);
}
于 2013-06-30T21:44:35.183 回答
0

在关机命令中有一个用于中止关机的开关。您必须通过您的 c# 代码调用此命令

        Process cmd = new Process();
        cmd.StartInfo.FileName = "cmd.exe";
        cmd.StartInfo.RedirectStandardInput = true;
        cmd.StartInfo.RedirectStandardOutput = true;
        cmd.StartInfo.CreateNoWindow = false;
        cmd.StartInfo.UseShellExecute = false;

        cmd.Start();



        cmd.StandardInput.WriteLine(@"shutdown -a");
        cmd.StandardInput.Flush();
        cmd.StandardInput.Close();
        Console.WriteLine(cmd.StandardOutput.ReadToEnd());
于 2013-06-30T10:37:36.467 回答