表格在Hide()上处理

时间:2014-02-16 13:22:31

标签: c# winforms forms c#-4.0 dispose

我有一个在.NET Framework 4.0上运行的C#WinForms应用程序。

当用户在一段时间内处于非活动状态时,我希望它隐藏所有显示的表单并在通知区域中显示一个图标。当用户单击该图标时,将显示登录表单,如果凭据有效,则会打开之前打开的确切表单。

为此,我将打开的表单列表存储在ListForm个对象中并隐藏它们,就像这样。此方法由Timer

调用
private void LogOut()
{
    foreach (Form form in Application.OpenForms)
        if (form.Visible)
        {
            GlobalVariables.formList.Add(form);
            form.Hide();
        }
}

验证凭据后,我会尝试再次显示表单,如下所示:

//Show the previous forms.
foreach (Form form in GlobalVariables.formList)
    form.Visible = true;

//Clear the forms list.
GlobalVariables.formList.Clear();

如果我在隐藏表单时只打开MainForm,它会在重新登录时显示回来。如果我打开任何其他表单(使用MainForm中的ShowDialog()打开),程序将在form.Visible = true;崩溃并给我以下错误消息:

ObjectDisposedException was unhandled
Cannot access a disposed object

如何解决此问题?做我想要实现的目标的另一种方式也很棒。

请注意,使用try-catch块来确定表单是否已经处理并且只是重新启动表单不是一个选项,因为用户可能在隐藏表单中有未保存的输入。

我无法在超过3小时的搜索时间内找到任何相关内容,因此我们非常感谢您的帮助!

编辑:在尝试了各种各样的事情之后,我注意到问题只出现在我使用ShowDialog()打开表单的表单上。如果我只使用Show()打开表单,一切正常。

但是在我的情况下,使用Show()不是一个选项,因为我无法让用户点击父表单中的内容。隐藏父表单也不是一个选项,因为他需要查看父表单中的信息。

4 个答案:

答案 0 :(得分:4)

显然隐藏表单比您指望的更有影响力。您的代码参与了M​​icrosoft在Winforms上进行的安全审核。非常彻底,在其行为方式中不常见,但在源代码中非常明显。一条规则就是用户永远不应失去对应用程序的控制权。

这样的对话非常麻烦。核心问题是ShowDialog()创建了一个模态窗口,禁用所有其他窗口。这为恶意软件创造了一个机会,非常容易利用,它所要做的就是隐藏一个对话框,然后你嘲笑用户。用户无法再次获得对应用程序的控制权。启用的一个窗口是隐藏的,用户无法再次重新激活它。所有其他窗口都被禁用,因此尝试单击它们或其任务栏按钮不会产生任何影响。剩下的就是让用户使用任务管理器来杀死应用程序。如果用户帐户被锁定,那么这也不是一个选项。

我现在可以听到你的声音:“但是,但我的代码隐藏了对话,而不是恶意软件!”这不是它在Windows中的工作方式,没有办法告诉它实际上是你的代码。不仅因为它可以注入代码,它甚至不必是在您的进程中运行的代码。 任何代码都可以做到,它是winapi的一部分。

因此,针对Winforms中内置的特定对策,如果在对话模式下操作时隐藏了表单,它将自动关闭该表单。这当然会产生很大影响,现在可以运行ShowDialog()调用后编写的代码。任何事情都是可能的,但是你的情况可能发生的事故是,这会占用另一个窗口,并且试图恢复它将会死亡。

这里的粗略指导是你做错了。您正在尝试在已经高度安全且经过严格测试的安全系统之上构建安全系统。并且非常风险,自己处理密码是一种非常好的方法,可以使整个系统的安全性降低。普通用户当然喜欢选择与登录Windows时相同的密码。使攻击者更容易获得该密码。

改为呼叫LockWorkStation()

答案 1 :(得分:2)

测试,似乎Hide()模态对话框 - 关闭它。它实际上会触发FormClosing事件。

经过如下测试:(另请参阅this answer。)

private void button1_Click(object sender, EventArgs e)
{
    Form1 f1 = new Form1();
    f1.ShowDialog();
}

private void button2_Click(object sender, EventArgs e)
{
    Hide();
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    MessageBox.Show("Closing");
}

修改

我注意到这实际上并没有解开谜团,只是添加了更多信息。这是另一条信息:当你将Visible设置为true时 - 你再次显示模态。这次它相当于Show()

答案 2 :(得分:2)

经过多个小时的反复试验,我发现了问题所在。

我对模态表单的理解是,只有在关闭模态表单后,代码才会继续在父表单中执行。实际上,the specification found on MSDN表示:

  

在继续使用应用程序的其余部分之前,必须先关闭模式窗体或对话框或隐藏

这在我处理表单的方式中引入了一个微妙的错误。这是我用来显示表单的代码:

using (var theForm = new CreateInvoice())
{
    theForm.ShowDialog();

    if (theForm.Updated)
    {
        GetInvoiceStatus();
    }
}

语句一退出,using statement就会处置theForm。通常,这种方法非常好,因为只有在用户关闭theForm时才会调用它。但是,因为ShowDialog()允许父窗体在隐藏时继续工作,这意味着代码实际上退出了using语句,这有效地处理了theForm,导致我的错误。

答案 3 :(得分:0)

设计的一个建议:您应该保存表单所保存的数据(模型)而不是保存表单(视图),并销毁表单。当您再次需要表单时,请使用数据(模型)将其创建回来。首先,这可以解决这个神秘的处置问题,其次,每种形式都需要GDI资源,这是有限的,如果有两种形式,你会遇到内存和GDI问题。

如何执行此操作,请参阅MVCMVP设计模式。

BTW我对这个问题的猜测:当你使表格可见时,它会尝试找到它的父亲,但它的父亲可能已被处理掉了。我曾经遇到过这个问题,它会抛出对象处置异常。