首页 > 解决方案 > 防止 WinForm 应用程序关闭不起作用

问题描述

我正在使用以下c#代码暂时阻止WinForm应用程序的关闭而没有成功,我观察到系统根本没有关闭,可能是因为收到关闭通知时我必须做的工作是在 UI 线程上制作。如果应用程序在 30 秒后没有响应,则 Windows 不会终止应用程序,如文档所述。见附图。在此处输入图像描述

public Form1()
{
    InitializeComponent();

    // Define the priority of the application (0x3FF = The higher priority)
    SetProcessShutdownParameters(0x3FF, SHUTDOWN_NORETRY);
}

[DllImport("user32.dll")]
public extern static bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string pwszReason);

[DllImport("user32.dll")]
public extern static bool ShutdownBlockReasonDestroy(IntPtr hWnd);

[DllImport("kernel32.dll")]
static extern bool SetProcessShutdownParameters(uint dwLevel, uint dwFlags);

private static int WM_QUERYENDSESSION = 0x11;
private static int WM_ENDSESSION = 0x16;
public const uint SHUTDOWN_NORETRY = 0x00000001;

private ManualResetEvent rEvent = new ManualResetEvent(false);
private bool blocked = false;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
    if (m.Msg == WM_QUERYENDSESSION)
    {
        if (!blocked)
        {
            blocked = true;
            ShutdownBlockReasonCreate(this.Handle, "Closing in progress");

            this.BeginInvoke((Action)(() =>
            {
                // My clean-up work on UI thread
                Thread.Sleep(600000);

                // Allow Windows to shutdown
                ShutdownBlockReasonDestroy(this.Handle);
            }));

            m.Result = IntPtr.Zero;
        }
        else
        {
            m.Result = (IntPtr)1;
        }
    }

    if (m.Msg == WM_ENDSESSION)
    {
        if (blocked)
        {
            ShutdownBlockReasonDestroy(this.Handle);
            m.Result = (IntPtr)1;
        }
    }

    // If this is WM_QUERYENDSESSION, the closing event should be  
    // raised in the base WndProc.  
    base.WndProc(ref m);

} //WndProc

标签: winformsapplication-shutdown

解决方案


注意:不要从 Visual Studio IDE 测试此功能:
构建可执行文件并从中运行应用程序。

切换private bool AllowEndSession真/假以禁用/启用系统重启块。

WM_QUERYENDSESSION它模拟一个繁忙的应用程序,在收到消息时仍需要完成其工作。当然,您的 App 需要处于响应此消息的状态:UI 线程必须是响应式的(即,App 正在 UI 线程以外的线程上工作)。

此外,您应该评估lParam,因为它可能是ENDSESSION_CRITICAL(系统本身可能被迫关闭 - 电力短缺和 UPC 在烟雾中运行,作为一个可能的边缘情况。作为一个更普遍的原因,一个严重的系统服务故障)。

如果应用程序不忙,但它需要执行清理操作或其他可能需要更多时间的任务,它应该在收到时返回FALSEIntPtr.Zero)并在WM_QUERYENDSESSION收到时启动延迟请求的原因过程WM_ENDSESSION

当应用程序返回TRUE此消息时,它会收到该 WM_ENDSESSION消息,而不管其他应用程序如何响应该WM_QUERYENDSESSION消息。每个应用程序应在收到此消息后立即返回TRUEFALSE立即返回,并将任何清理操作推迟到收到WM_ENDSESSION 消息为止。

请注意,仅当用户数据可能因特定原因而受到损害或某些硬件正在完成操作(如 CD/DVD 写入)时,才应使用块请求。应用程序执行的其他程序/任务必须全部按时完成。


AllowEndSession = false;应用程序收到一条WM_QUERYENDSESSION消息时,将System.Windows.Forms.Timer启动 a,超时时间为 10 秒(模拟一个繁忙但响应迅速的应用程序,将花费该时间来终止关键作业)。

系统将向用户呈现经典的重启阻止屏幕,通知应用程序已请求延迟重启过程,并显示应用程序提供的阻止原因字符串(Cleaning Up/Doing stuff... Wait a sec传递给ShutdownBlockReasonCreate,此处)。

当计时器过去(滴答声)时,作业终止,应用程序将关闭其主窗体,调用Application.Exit或其他任何东西,也调用ShutdownBlockReasonDestroy()

此时,关机/重启程序将恢复,系统屏幕将更新其状态,关闭剩余的应用程序(如果有)仍在运行并继续关机。

private bool AllowEndSession = false;
private bool ShutbownBlockReasonCreated = false;
private System.Windows.Forms.Timer shutDownTimer = null;

protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case WM_QUERYENDSESSION:
            if (!AllowEndSession) {
                bool result = ShutdownBlockReasonCreate(this.Handle, "Cleaning Up/Doing stuff... Wait a sec");

                shutDownTimer = new System.Windows.Forms.Timer();
                shutDownTimer.Tick += (s, evt) => {
                    ShutbownBlockReasonCreated = false;
                    ShutdownBlockReasonDestroy(this.Handle);
                    shutDownTimer.Enabled = false;
                    shutDownTimer.Dispose();
                    this.Close();
                };
                shutDownTimer.Interval = 10000;
                shutDownTimer.Enabled = true;

                ShutbownBlockReasonCreated = true;
                m.Result = IntPtr.Zero;
            }
            else {
                m.Result = (IntPtr)1;
            }
            break;
        case WM_ENDSESSION:
            if (ShutbownBlockReasonCreated) {
                ShutdownBlockReasonDestroy(this.Handle);
            }
            m.Result = (IntPtr)1;
            break;
        default:
            base.WndProc(ref m);
            break;
    }
}

推荐阅读