FormClosingEventArgs.CloseReason中存在Bug吗?

7

我的任务需求

大约有12人使用此应用程序,但我们只想允许4人通过传统方法(Alt+F4,文件>退出,关闭)关闭应用程序。

如果使用其他任何方法(任务管理器,Windows关闭),或者允许的用户之一关闭了应用程序,我们需要执行一些清理操作(关闭一些连接通道)。

我所使用的代码来满足上述要求

private void formClosing(object sender, FormClosingEventArgs e)
{
    // If a user is allowed to close the application, an empty file (filename)
    // will be in the root directory of the application.
    if(e.CloseReason == CloseReason.UserClosing && !File.Exists("filename"))
    {
        e.Cancel = true;
        return;
    }

    // Cleanup
}

问题

如果一个用户(无权关闭)试图通过传统方法关闭应用程序,然后尝试使用任务管理器关闭,则CloseReason枚举似乎不会重置自身,从而导致任务管理器弹出提示框以强制关闭,防止应用程序清理。

问题描述

这是一个 bug 吗?还是我漏掉了什么东西,可以在取消 FormClosing 事件后重置 CloseReason 呢?


1
我在MSDN文档中找不到任何信息,而且e.CloseReason是只读属性,所以您无法自行重置它。这是一个时间问题吗?如果您在从任务管理器关闭之前等待一段时间,那么可以吗? - ChrisF
我可以确认这一点,它取决于顺序。 - H H
2个回答

8

.NET Reflector是在研究WinForms操作时的好帮手。

Form类有一个名为closeReason的内部字段,用于生成您在Closing事件中检查的事件参数。我能找到的四个不同位置设置了这个内部字段。它们是...

1、Form.Close()方法将closeReason设置为UserClosing。

这是有道理的,因为手动调用Form.Close()方法通常是某个用户操作的结果,例如用户选择“文件->退出”菜单选项。显然,这是一种用户操作。

2、WM_SYSCOMMAND(SC_CLOSE)将closeReason设置为UserClosing。

Form的WndProc通过将closeReason设置为UserClosing来处理SC_CLOSE系统命令,并让默认窗口过程执行并关闭应用程序。这是有道理的,因为当用户按下窗口关闭铬按钮或从右键单击标题栏选择关闭选项时,会发送此SC_CLOSE。两者都是用户操作,因此将closeReason设置为UserClosing似乎是正确的。

3、WndProc使用closeReason = TaskManagerClosing处理消息WM_CLOSE(0x10)

WM_CLOSE由任务管理器和其他应用程序发送以关闭窗口,如果closeReason当前等于None,则将其更新为TaskManagerClosing。请注意,仅在其为None时更新它可能存在问题,因为我认为这对您是个问题。

4、WndProc使用closeReason = WindowsShutDown处理消息0x11和0x16

这并不是很有趣,因为您不关心这种情况,但它只是关闭消息的标准处理方式。

因此,您遇到的核心问题是,在取消Closing事件时,closeReason从未被重置回None。因此,如果在您取消后发生点3,它将永远无法正确更新值为TaskManagerClosing。由于closeReasson是一个内部字段,您无法直接更新它。但是,您可以欺骗,并且这是我过去使用的方法。您需要使用反射来访问内部字段,然后在事件处理程序中设置Cancel=true时将其重置为None。

我没有测试过这段代码,但您需要类似以下的内容...

PropertyInfo pi = typeof(Form).GetProperty("CloseReason",
                                           BindingFlags.Instance |
                                           BindingFlags.SetProperty |
                                           BindingFlags.NonPublic);

pi.SetValue(this, CloseReason.None, null);

0

我认为如果进程是由任务管理器启动的(也就是操作系统...他是“大老板”),你无法阻止它关闭你的程序。

下一个最好的方法是记录应用程序的状态,然后使用一些启动选项实例化另一个进程以接管您离开的状态。操作系统会终止您的进程,但您将立即启动另一个进程。

此外,如果用户在应用程序列表中单击“转到进程”,然后结束进程,我认为您根本不会收到任何事件...

也许最好的方法是拥有一个在后台运行并跟踪实例运行情况的Windows服务。这样,用户可能不会意识到这样的进程存在,因为它不是他们的应用程序,并且您可以使用它来跟踪应用程序关闭。


我并不是要阻止所有关闭应用程序的方式,只是针对一些客户端的常规方式。 - Slipfish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接