簡體   English   中英

表格在Hide()上處理

[英]Form gets disposed on Hide()

我有一個在.NET Framework 4.0上運行的C#WinForms應用程序。

當用戶處於非活動狀態一段時間后,我希望它隱藏所有顯示的表單並在通知區域中顯示一個圖標。 當用戶單擊該圖標時,將顯示登錄表單,如果憑據有效,則會打開之前打開的確切表單。

為此,我將打開表單列表存儲在Form對象List中並隱藏它們,如下所示。 這個方法由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;崩潰form.Visible = true; 並給我以下錯誤消息:

ObjectDisposedException was unhandled
Cannot access a disposed object

我該如何解決這個問題? 做我正在努力實現的另一種方式也很棒。

請注意,使用try-catch塊來確定表單是否已經處理並且只是重新啟動表單不是一個選項,因為用戶可能在隱藏表單中有未保存的輸入。

我無法在3個多小時的搜索中找到任何相關的在線信息,所以任何幫助都將非常感謝!

編輯:嘗試了各種各樣的事情后,我注意到問題只出現在我使用ShowDialog()打開表單的表單上。 如果我只使用Show()打開表單,一切正常。

但是在我的情況下,使用Show()不是一個選項,因為我無法讓用戶點擊父窗體中的內容。 隱藏父表單也不是一個選項,因為他需要查看父表單中的信息。

顯然隱藏表單比你指望的更有影響力。 您的代碼參與了M​​icrosoft在Winforms上進行的安全審核。 非常徹底,在其行為方式中不常見,但在源代碼中非常明顯。 一條規則是強制用戶永遠不應失去對應用程序的控制權。

這樣一個對話很麻煩。 核心問題是ShowDialog()創建一個模式窗口,禁用所有其他窗口。 這為惡意軟件創造了一個機會,非常容易利用,它所要做的就是隱藏一個對話框,然后你嘲笑用戶。 用戶無法再次獲得對應用程序的控制權。 啟用的一個窗口是隱藏的,用戶無法再次重新激活它。 所有其他窗口都被禁用,因此嘗試單擊它們或其任務欄按鈕不會產生任何影響。 剩下的就是讓用戶使用任務管理器來殺死應用程序。 如果用戶帳戶被鎖定,那么這也不是一個選項。

我現在可以聽到你的聲音:“但是,但是, 我的代碼隱藏了對話,而不是惡意軟件!” 這不是它在Windows中的工作方式,沒有辦法告訴它實際上是你的代碼做到了。 不僅因為它可以注入代碼,它甚至不必是在您的進程中運行的代碼。 任何代碼都可以做到,它是winapi的一部分。

因此,對於內置於Winforms中的內容存在特定的對策,如果在對話模式下操作時隱藏了表單,它將自動關閉該表單。 這當然會產生很大影響,現在可以運行ShowDialog()調用后編寫的代碼。 任何事都是可能的,但是你的情況下可能發生的事故是,這會占用另一個窗口並且試圖恢復它將會死亡。

這里粗略的指導是你做錯了 您正在嘗試在已經高度安全且經過嚴格測試的安全系統之上構建安全系統。 而且風險很大 ,自己處理密碼是一種非常好的方法,可以使整個系統的安全性降低。 普通用戶當然喜歡選擇與登錄Windows時相同的密碼。 使攻擊者更容易獲得該密碼。

請改為調用LockWorkStation()

測試,似乎Hide()一個模態對話框 - 關閉它。 它實際上觸發了FormClosing事件。

像這樣測試:(另外,請看這個答案 。)

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()

經過幾個小時的反復試驗,我發現了問題所在。

我對模態形式的理解是,只有在關閉模態形式之后,代碼才會繼續以父形式執行。 事實上, MSDN上的規范說明:

在繼續使用應用程序的其余部分之前,必須關閉或隱藏模式窗體或對話框。

這在我處理表單的方式中引入了一個微妙的錯誤。 這是我用來顯示表單的代碼:

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

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

一旦語句退出, using語句就會處理theForm 通常,這種方法非常好,因為只有在用戶關閉theForm時才會調用它。 但是,因為ShowDialog()允許父窗體在隱藏時繼續工作,這意味着代碼實際上退出了using語句,這有效地處理了theForm ,導致我的錯誤。

設計的一個建議:您應該保存表單所保存的數據(模型)並銷毀表單,而不是保存表單(視圖)。 當您再次需要表單時,請使用數據(模型)將其創建回來。 首先,這可以解決這個神秘的處置問題,其次,每種形式都需要GDI資源,這是有限的,如果有兩種形式,你會遇到內存和GDI問題。

至於如何做,請參考MVCMVP設計模式。

我對這個問題的猜測:當你使表格可見時,它會嘗試找到它的父親,但其父親可能已被處置。 我遇到過這個問題一次,它拋出對象處理異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM