winforms - 防止 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
解决方案
注意:不要从 Visual Studio IDE 测试此功能:
构建可执行文件并从中运行应用程序。
切换private bool AllowEndSession
真/假以禁用/启用系统重启块。
WM_QUERYENDSESSION
它模拟一个繁忙的应用程序,在收到消息时仍需要完成其工作。当然,您的 App 需要处于响应此消息的状态:UI 线程必须是响应式的(即,App 正在 UI 线程以外的线程上工作)。
此外,您应该评估lParam
,因为它可能是ENDSESSION_CRITICAL
(系统本身可能被迫关闭 - 电力短缺和 UPC 在烟雾中运行,作为一个可能的边缘情况。作为一个更普遍的原因,一个严重的系统服务故障)。
如果应用程序不忙,但它需要执行清理操作或其他可能需要更多时间的任务,它应该在收到时返回FALSE
(IntPtr.Zero
)并在WM_QUERYENDSESSION
收到时启动延迟请求的原因过程WM_ENDSESSION
当应用程序返回
TRUE
此消息时,它会收到该WM_ENDSESSION
消息,而不管其他应用程序如何响应该WM_QUERYENDSESSION
消息。每个应用程序应在收到此消息后立即返回TRUE
或FALSE
立即返回,并将任何清理操作推迟到收到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;
}
}
推荐阅读
- docker - 在 docker 容器中运行 delve - debug.test:没有这样的文件或目录
- javascript - 在 if 语句中控制对象是否被点击
- sql - Group by - 为不同的组应用不同的过滤器
- saleor - 如何在 Saleor 中动态更新产品的可用性和价格
- android-studio - java.lang.NullPointerException:尝试在空对象上调用虚拟方法“java.lang.String com.example.xxx.Model.Users.getName()”
- javascript - 从 JSON API 提取的输入意外结束
- css - 制作具有不同和意外高度的css网格行是真的吗?
- javascript - 我的 javascript 机器人在结束前停止音乐
- reactjs - 如何在 jsx 文件/React 组件中解析 markdown 的 yaml frontmatter?
- excel - 对多个级别进行分组时,Excel VBA 脚本不起作用