2014-02-16 168 views
4

我有一個運行在.NET Framework 4.0上的C#WinForms應用程序。表單隱藏()

當用戶在一段時間內不活動時,我希望它隱藏所有顯示的表格並在通知區域顯示一個圖標。當用戶點擊該圖標時,會出現一個登錄表單,如果憑證有效,它將打開之前打開的確切表單。

爲此,我將對象的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;並給我以下錯誤消息:

ObjectDisposedException was unhandled 
Cannot access a disposed object 

我該如何解決這個問題?另一種做法,我試圖實現也將是偉大的。

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

我無法找到超過3小時的搜索在線相關的任何事情,所以任何幫助將不勝感激!

編輯:在嘗試過各種各樣的事情後,我注意到這個問題只發生在我用ShowDialog()打開窗體的窗體上。如果我只使用Show()打開表單,一切正常。

但在我的情況下,使用Show()不是一個選項,因爲我不能讓用戶點擊父窗體中的東西。隱藏父表單不是一種選擇,因爲他需要在父表單中查看信息。

+1

那麼這確實是一個很好的問題。 :) +1 – ispiro

+0

相關:http://stackoverflow.com/a/3751748/939213。 – ispiro

+0

Try form.Show();而不是form.Visible = true; –

回答

4

明顯隱藏表單比您指望的更具影響力。您的代碼參與了M​​icrosoft對Winforms進行的安全審查。非常徹底,在源代碼中不常見,但在源代碼中非常明顯。一個規則是施加的是用戶不應該失去對應用程序的控制。

對話是非常麻煩的方式。核心問題是ShowDialog()創建一個禁用所有其他窗口的模式窗口。這爲惡意軟件創造了機會,非常容易利用,它只需隱藏一個對話框並吸引用戶。用戶無法再次獲得對應用程序的控制權。被啓用的一個窗口被隱藏起來,用戶無法再次重新激活它。所有其他窗口都被禁用,因此嘗試點擊它們或其任務欄按鈕不會產生任何效果。剩下的就是讓用戶使用任務管理器來終止應用程序。如果用戶帳戶被鎖定,那麼這也不是一個選項。

我現在可以聽到你嗤之以鼻:「但是,它是我的隱藏對話框的代碼,而不是惡意軟件!」這不是它在Windows中的工作方式,也沒有辦法說明它實際上是你的代碼。不僅因爲它可以被注入代碼,它甚至不必是在你的進程中運行的代碼。 任何代碼都可以做到,它是winapi的一部分。

所以這是對這個內置的WinForms具體的應對措施,如果它而在對話模式下運行隱藏在它會自動關閉窗體。這當然會產生很大的影響,現在將運行ShowDialog()調用後編寫的代碼。任何事情都是可能的,但在你的情況下肯定會引發一場災難,那就是這會放置另一個窗口,並試圖恢復它。

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

改爲撥打LockWorkStation()

0

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

至於如何做到這一點,請參考MVCMVP的設計模式。

順便說一句我對這個問題的猜測:當你讓表單可見時,它會嘗試找到它的父類,但是它的父類可能已經被處置了。我遇到過這個問題一次,它拋出異常處理對象。

2

測試,似乎Hide() ing模式對話框 - 關閉它。它實際上觸發了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()

+0

這是一個很好的觀察,它是在正確的道路上的實際問題,所以+1!另外,我已經注意到,再次使它可見是等同於Show()而不是ShowDialog(),所以現在我必須找出另一種實現一切的方法。即使你對這兩個觀察都是正確的,我已經知道了。不過,公平的做法是將你的答案標記爲已接受的答案,因爲如果我今天比較忙,它肯定會使我走上正確的道路! :) –

+1

謝謝。但請隨時接受漢斯的回答,而不是爲有用的信息(儘管看起來「LockWorkStation」不完全是你所追求的)或你自己的答案(+1),這是實際的解決方案。我今天學到了新東西! – ispiro

2

經過數小時的反覆試驗,我發現問題所在。

我的模態形式的理解是,代碼將繼續在模式窗體被關閉後,才父窗體執行。事實上,the specification found on MSDN狀態:

模式窗體或對話框必須關閉或隱藏,然後才能繼續與應用程序的其餘工作。

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

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

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

theFormusing statement處置只要語句退出。通常情況下,這可以很好地工作,因爲只有當用戶關閉theForm時纔會調用它。但是,由於ShowDialog()允許父表單在隱藏時繼續工作,這意味着代碼實際上退出了使用語句,這有效地處理了theForm,導致我的錯誤。