FormClosingEventArgs.CloseReason中的错误?

时间:2010-09-02 21:36:55

标签: c# winforms

我要求的要求

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

如果使用任何其他方法(TaskManager,WindowsShutdown)或其中一个允许的用户关闭应用程序,我们需要执行一些清理(关闭一些连接通道)

我曾用过的代码满足上述要求

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 枚举似乎不会自行重置,从而导致任务管理器弹出提示强制关闭,阻止应用程序清理。

问题

这是一个错误,还是我遗漏了一些东西,在 FormClosing 事件被取消后会重置 CloseReason

2 个答案:

答案 0 :(得分:6)

在研究WinForms的运行方式时,.NET Reflector是您的朋友。

Form类有一个名为 closeReason 的内部字段,在生成您在 Closing 事件中检查的事件参数时使用此字段。这个内部字段设置在我可以找到的四个不同的地方。这些是......

1, Form.Close()方法设置closeReason = UserClosing。

这是有道理的,因为手动调用 Form.Close()方法通常是某些用户操作的结果,例如 File-> Exit 菜单选项由用户选择。显然这是一个用户操作。

2, WM_SYSCOMMAND(SC_CLOSE)设置closeReason = UserClosing。

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

3, WndProc使用closeReadon = TaskManagerClosing处理消息WM_CLOSE(0x10)

任务管理器和其他应用程序发送

WM_CLOSE以关闭窗口,如果 closeReason 当前等于,则将其更新为 TaskManagerClosing 。请注意这个问题,只有当它是时它才会被更新,因为我觉得这对你来说是一个问题。

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

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

因此,您遇到的核心问题是,当您取消 Closing 事件时, closeReason 在任何时候都不会重置为 None 。因此,如果在取消后发生,则上面的第3点将永远不会正确地将值更新为 TaskManagerClosing 。由于closeReasson是内部字段,因此无法直接更新。但你可以作弊,这是我过去用过的一种方法。在事件处理程序中设置 Cancel = true 时,需要使用反射来访问内部字段,然后将其重置为

我没有测试过这段代码但你需要的东西......

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

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

答案 1 :(得分:0)

我认为如果由任务经理(也就是操作系统......他是'大老板')启动你的流程就不能让你的流程停止关闭,你可以否认它就像关闭你的程序一样没有意义)。

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

此外,如果用户点击应用程序列表中的TaskManager“go to process”,并从那里结束进程,我认为你根本不会收到任何活动...

如果你有一个在幕后运行的Windows服务并且跟踪一个实例正在运行,那么也许最好。这样,用户可能不会意识到存在这样的过程,因为它不是他们的应用程序,并且您可以使用它来跟踪应用程序关闭。