2011-06-23 73 views
3

我正在從一個非常簡單的程序(VS2010中的C#WPF應用程序)工作,它顯示來自SerialPort的數據並顯示在TextBox上。該程序在正常情況下工作正常。但是,當用戶打開一個連接時,收集一些數據,關閉它並再次打開,並執行此操作幾個週期,程序最終會拋出一個異常:WPF中的SerialPort拋出I/O異常

「I/O操作因爲線程退出或應用程序請求。「 [在ReadLine()中發生I/O異常]

有時程序會給我一個異常;有時程序只是掛起。 下面是我的代碼:

/* Click to Open ComPort */ 
private void PortOpen_Click(object sender, RoutedEventArgs e) 
{ 
    if (!serialPort1.IsOpen) 
    { 
     serialPort1.PortName = "COM1"; 
     serialPort1.BaudRate = 9600; 
     serialPort1.ReceivedBytesThreshold = 1; 
     serialPort1.NewLine = "\r\n"; 
     serialPort1.Parity = Parity.None; 
     serialPort1.StopBits = StopBits.One; 
     serialPort1.DataBits = 8; 
     serialPort1.Handshake = Handshake.None; 

     serialPort1.Open(); 

     serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(Receive); 
    } 
} 

/* Receive data from ComPort */ 
private void Receive(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) 
{ 
    if (serialPort1.IsOpen) 
    { 
     try 
     { 
      string1 = serialPort1.ReadLine(); /* This is where I/O Exception occurred */ 
      Dispatcher.Invoke(DispatcherPriority.Send, new UpdateUiTextDelegate(DisplayText), string1); 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.ToString()); 
     } 
    } 
} 

private void DisplayText(string string1) 
{ 
    textBox1.Text = string1; 
} 
/* Close ComPort */ 
private void PortClose_Click(object sender, RoutedEventArgs e) 
{ 
    if (serialPort1.IsOpen) 
    { 
     serialPort1.Close(); 
    }    
} 

下面總結一下我的頭撞抵表企圖在過去的40小時,這導致沒有進展:

  1. 我已經嘗試過和Open()後添加Thread.Sleep(3000)Close()。而且我開始非常沮喪,我把Thread.Sleep放在每一行之間。儘管如此,我仍有足夠的時間在後臺完成一些未完成的工作。不解決問題。
  2. 我試過Zach Saw's Post。博客文章留下的很多評論都非常積極。我嘗試了這種方法,甚至複製並粘貼了我的確切代碼。不解決問題。一個很長的帖子浪費了我一天的一半時間。
  3. 金漢密爾頓解決問題here。其中建議使用BeginInvoke而不是Invoke。試過並仍然存在同樣的問題。
  4. 有一個非常好的商業SerialPort庫Franson SerialTools這是相當便宜,沒有錯誤,無論多少時間和多快,我打開()或關閉()的串口,奇妙的工作。然而,他們已經停止了他們的開發,他們只能在Form應用程序中工作,而不是WPF。他們的API中的一些參數只接受Forms.Control。太糟糕了。還有其他商業產品在那裏,但他們要麼過於定價或不提供免費試用,所以我不知道它是否有效或不購買前

有誰拿到.NET的SerialPort工作和實際檢查(多次打開()和關閉()(即使沒有傳入數據)?

+1

MSDN文檔提示您不應該在事件處理程序中使用此語句「一次只能執行一個事件處理程序」來放置阻塞讀取。我強烈建議您不要在處理程序中使用ReadLine。 – dbasnett

+0

@dbasnett,好點,我想我需要創建一個線程來完成這個工作,並在關閉時加入/關閉? – KMC

+1

爲什麼不讀取可用的字節,並將這些字節排隊以供其他線程處理?這種方法將要求您手動檢測換行符。 – dbasnett

回答

1

當你打開端口你添加事件處理程序,關閉時我認爲你應該刪除它。

試着這樣做:

private void PortClose_Click(object sender, RoutedEventArgs e) 
{ 
    if (serialPort1.IsOpen) 
    { 
     serialPort1.DataReceived -= Receive; 
     serialPort1.Close(); 
    }    
} 

希望這能解決問題。

+0

就像您的解決方案一樣簡單,它解決了我的問題。你可以節省我週末的打擊頭,希望有更多的提議。我很好奇,爲什麼ReadLine()在我打電話時仍然在閱讀?我必須刪除事件以避免它.. – KMC

+0

這不能解決問題,DataReceived已在運行時引發異常。 –

+0

所以這就是爲什麼你需要在關閉之前刪除處理程序。 – Reniuz

0

我想象一下,阻止來自ReadLine的呼叫被來自UI線程的Close調用中斷。捕獲這個錯誤有什麼問題?我預計會發生。

+0

是的你是正確的ReadLine()中斷關閉()。那麼我該怎麼做?我試着讓線程在ReadLine和Close之前進入睡眠狀態。還沒有成功。 – KMC

+1

也許添加變量bool isClosing,在if(serialPort1.IsOpen &&!isClosing)中檢查它並在點擊按鈕時設置isClosing = true? – Reniuz

+0

@Reniuz,聽起來很不錯。我試過它在同一行給了我I/O異常,區別在於:「由於線程退出或應用程序請求,I/O操作已中止。」 – KMC

0

@Reniuz解決方案也沒有幫助我。 其實我無法理解它是如何提供幫助的,顯然作爲@Hans Passant的評論,DataReceived可能已經在取消訂閱該事件。 我的解決方案是在DataReceived事件中取消訂閱,每當需要取消訂閱時提出一個標誌,並在DataReceived事件中檢查它。

public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 
// Brief: Handle data received event 
{ 
    // Read the data 
    // ~~~~~~~~~~~~~ 

    // Check for unsubscribe 
    // ~~~~~~~~~~~~~~~~~~~~~ 
    if (bStopDataRequested) 
    { 
    serialPort1.DataReceived -= DataReceivedHandler; // Unsubscribe to DataReceived event 

    // Only then close the port 
    } 
}