2012-04-26 39 views
0

我有一個WinForms應用程序,其中包含帶有後臺工作程序的單個窗體。表單包含一個按鈕,通過RunWorkerAsync()啓動後臺工作器,另一個按鈕將退出應用程序。約1/3的時間,後臺工作人員已完成其工作後,應用程序會後,我退出按鈕點擊,像這樣的異常崩潰:.NET WinForms應用程序在出現未處理的System.NullReferenceException時崩潰

System.NullReferenceException was unhandled 
    Message=Object reference not set to an instance of an object. 
    Source=System.Drawing 
    StackTrace: 
     at System.Drawing.Graphics.Dispose(Boolean disposing) 
     at System.Drawing.Graphics.Finalize() 

下面是該按鈕的事件處理程序退出應用程序:

private void buttonExit_Click(object sender, EventArgs e) 
    { 
     if (!buttonStartWorker.Enabled) 
     { 
      DialogResult dr = MessageBox.Show("Background worker is still running! Exit anyway?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); 

      if (dr == DialogResult.OK) 
      { 
       backgroundWorker.CancelAsync(); 
       Close(); 
      } 
     } 
     else 
     { 
      Close(); 
     } 
    } 

正如我剛纔所說,我不會退出應用程序,而後臺工作仍在運行,所以我們正在尋找這裏的代碼路徑只是關閉()調用。還有一個FormClosing事件處理程序,它調用close和處理我的USB相關句柄上的方法。該代碼如下:

private void MainForm_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     try 
     { 
      // close and dispose all open handles to the USB device 
      if (hidHandle != null) 
      { 
       if (!(hidHandle.IsInvalid)) 
       { 
        hidHandle.Close(); 
        hidHandle.Dispose(); 
       } 
      } 

      if (readHandle != null) 
      { 
       if (!(readHandle.IsInvalid)) 
       { 
        readHandle.Close(); 
        readHandle.Dispose(); 
       } 
      } 

      if (writeHandle != null) 
      { 
       if (!(writeHandle.IsInvalid)) 
       { 
        writeHandle.Close(); 
        writeHandle.Dispose(); // unhandled exception seems to occur after this 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.ToString()); 
     } 
    } 

有時writeHandle.Dispose之間()和應用程序實際上離開時,此異常正在發生。最讓我困惑的是事實是我的代碼從未明確地使用過System.Drawing,所以我無法跟蹤它。

對於它的價值,我的後臺工作執行以下操作:

  1. 它可以讀取和USB設備的一些數據寫入/
  2. 它創建了一個Web客戶端下載一些數據
  3. 它使幾個SOAP調用

有沒有人有什麼可以在System.Drawing中導致一個未處理的NullReferenceException的任何想法當應用程序(不顯式使用System.Dra翅膀)退出?

+0

發佈完整的堆棧跟蹤請 – wal 2012-04-26 15:44:50

回答

0

雖然您的程序可能不明確使用System.Drawing,但該命名空間已遍佈大多數WinForms對象代碼。看起來在你的表單的某個地方,一個GUI對象被給予了另一個對象的Graphics句柄的引用,然後它認爲它必須銷燬;但是,擁有該句柄的控件已經被處理,或者Graphics對象本身具有顯式調用的Dispose方法,因此Dispose()代碼在第二次運行時會失敗。我希望.NET開發人員能夠進行檢查,以確保雙重Dispose不會成爲問題,但在這種情況下,他們忽視了它。

不知道你的窗體上有什麼,以及System.Drawing.Graphics的哪個實例正在拋出,我無法進一步幫助。那將是我進一步調查的地方;嘗試發現正在處理的確切實例,其他對象擁有它的原因,以及爲什麼它在已經被處置後被終結(通常如果明確處置,則應該調用GC.SuppressFinalize())。

其中一個「反映」的代碼庫引用可能會幫助你;尋找System.Drawing.Graphics.Dispose(bool disposing),並瀏覽任何一行代碼,如果連續運行兩次將會失敗。這可能會給你一個提示。

1

儘管KeithS的答案在本質上是正確的,但您的代碼似乎確實存在明顯的問題。調用CancelAsync()後立即調用this.Close();。我會嘗試等待後臺工作人員完成業務或訂閱已取消的事件(如果存在)。

這很可能是您的後臺工作人員尚未完成。另外請注意,後臺工作人員是與表單的事件啓動相關的組件。

嘗試創建一個新任務:

this.BeginInvoke(new Action(() => (Thread.Sleep(1000); this.Close();)));

對不起,語法不正確,但我不在開發機器上。

+0

感謝您的建議,Raheel。對於在CancelAsync()後立即調用Close()的代碼路徑,您肯定會提出一個有效的觀點。不幸的是,那不是迄今爲止我一直在執行的代碼路徑。後臺工作人員仍在運行時,我一直沒有關閉應用程序。在我的backgroundWorker_RunWorkerCompleted()方法中(這裏沒有顯示),我設置了buttonStartWorker.Enabled = true,這會導致buttonExit_Click()中的else子句被執行。我通過在buttonExit_Click()的第一行放置一個斷點來驗證這一點。 – user685869 2012-04-27 19:49:37