System io IOException 連結 到系統的 某 個 裝置 失去 作用

小弟這邊出現了一個問題想請教

因為設備韌體上有點問題,所以發生以下狀況..雖然韌體修正後已解決,但要找出為何會這樣的原因...

小弟使用c#

開啟程式會啟動下面這塊

        SerialPort sp;
        public void Start(string COM_Port)
        {
            try
            {

                
                sp = new SerialPort();
                sp.PortName = COM_Port;
                sp.BaudRate = 9600;
                sp.Parity = Parity.Even;
                ...
                ...
                .....
                sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);

                if(sp.Open==true)
                {
                    sp.close();
                }
                sp.open();  //程式關閉後再開啟這邊會出錯
            }
            catch (Exception ex)
            {
                throw new Exception(ex.ToString());
            }
        }

設備第一次連線是正常的,但是當我會送一個命令出去,當那個設備接收到命令後..因為韌體出錯,所以我會看到裝置管理員裡面自動重新整理一下,可是該裝置還是存在的(應該是說搞不好設備有瞬間斷電行為),此時我已經和設備處於斷線狀態

這時我將我的程式關閉(確定處理程序內已經沒有我的程式),再次打開...執行到sp.Open();的地方,會出現"通訊埠 'COM11' 不存在"的錯誤

但其實我很確定裝置管理員中的設備是存在的.....必須重新插拔才能正常運行

當該設備出錯時,我嚐試再送一個Polling命令,得到的錯誤是"System.IO.IOException: 連結到系統的某個裝置失去作用。"

甚至我要在發生這個Exception的地方,加入sp.Close();或是sp.Dispose(0;

他都會跳出"通訊埠 'COM11' 不存在"的錯誤

可是

當使用VB6 採用MSComm

            MSComm(i).PortOpen = False
            Unload MSComm(i)
            Load MSComm(i)

反而沒問題,設備出錯後,程式關閉重新開啟也不會出現像是"通訊埠 'COM11' 不存在這種錯誤"

請問這可能是什麼問題


  • 已編輯 2012年8月6日 上午 04:19

我已經構建了一個C應用程式,它從串列埠讀取和寫入資料。連線到串列埠的裝置是一個FTDI USB到序列轉換器,它通過XBEE無線模組與硬體通訊。硬體測試電池模組的容量和穩態電壓等。這些測試需要幾天時間才能完成。
有時串列埠似乎停止響應並丟擲System.IO.IOException:連線到系統的裝置不工作錯誤。
下面是堆疊跟蹤:

at system.IO.Ports.InternalResources.WinIOError
at system.IO.Ports.SerialStream.EndWrite
at system.IO.Ports.SerialStream.Write
at system.IO.Ports.SerialPort.Write
at BatteryCharger.CommunicationClass.ApiTransmission

在丟擲此錯誤後,將丟擲一個System.UnauthorizedAccessException: Access to the port is denied錯誤,並且每當軟體嘗試寫入埠時都會出現此錯誤,並且在我停止除錯並重新啟動軟體之前,它永遠不會恢復,直到幾天後發生相同的事情。
如何防止發生此錯誤,或者是否有方法成功地從錯誤的catch語句中的這些錯誤中恢復?
我正在後臺工作執行緒中連續讀取串列埠,並從其他執行緒寫入。
我也已經嘗試過在這個論壇上建議的所有遺留錯誤處理片段,但它們似乎都沒有任何區別。錯誤發生在Windows XP Pro SP3 32位和Windows7 Pro 32位上。
這是CommunicationClass.cs-序列傳輸程式碼。

    public static bool ApiTransmission(TXpacket transmission)
    {
        //clear all previous data that may have been in the buffer before doing a transmission
        Variables.dataParserPacket_buff.Clear();
        //TXpacket xbeetransmision = new TXpacket();
        byte[] packet = transmission.GeneratePacket();

        try
        {
            if (_serialPort.IsOpen)
            {
#if Console
                Log.write("TX-Packet: " + StringHandler.listToString(packet.ToList<byte>()));
#endif
                _serialPort.Write(packet, 0, packet.Length);
                Thread.Sleep(100);
            }
            else
            {
#if Console
                Log.write("serial port is closed");
#endif
                return false;
            }
        }
        catch (UnauthorizedAccessException ex)
        {
            MessageBox.Show(ex.ToString());
            Log.write("UnauthorizedAccessException");
        }
        catch (IOException ex)
        {
            MessageBox.Show(ex.ToString());
            Log.write("IOexception");
            //_serialPort.Close();
            //Thread.Sleep(100);
            //_serialPort.Open();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
#if Console
            Log.write(ex.ToString());
#endif
        }
        return true;

    }

這是初始化串列埠的方法

    public CommunicationClass(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
    {
        _analysePacketBGW.DoWork += new DoWorkEventHandler(_analysePacketBGW_DoWork);
        _analysePacketBGW.WorkerReportsProgress = true;
        _analysePacketBGW.WorkerSupportsCancellation = true;

        _readBGW.DoWork += new DoWorkEventHandler(_readThread_DoWork);
        _readBGW.WorkerSupportsCancellation = true;
        _readBGW.WorkerReportsProgress = true;

        _parserStarterBGW.DoWork += new DoWorkEventHandler(_parserStarterThread_DoWork);
        _parserStarterBGW.WorkerSupportsCancellation = true;
        _parserStarterBGW.WorkerReportsProgress = true;
        if (_readBGW != null)
        {
            _readBGW.CancelAsync();
        }

        _serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);

        //SerialPortFixer.Execute(portName);
        //Thread.Sleep(1000);
        //using (_serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits))
        //{
        //    //_serialPort.Open();
        //}

        _serialPort.ErrorReceived += new SerialErrorReceivedEventHandler(_serialPort_ErrorReceived);

        _dataqueuepp = new ManualResetEvent(false);

        _serialPort.Open();
        _readBGW.RunWorkerAsync();
        _parserStarterBGW.RunWorkerAsync();
        CommunicationClass.PacketReceived += new DataPacketReceived(CommunicationClass_PacketReceived);
    }

以及處理串列埠讀取的後臺工作程式

    void _readThread_DoWork(object sender, DoWorkEventArgs e)
    {
#if Console
        Log.write("Read()");
#endif
        while (!_readBGW.CancellationPending)
        {
            try
            {
                int message = _serialPort.ReadByte();
                try
                {
                    Variables.dataQueue.Enqueue(message);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + "     " + message.ToString());
                }
                _dataqueuepp.Set();
                //Console.Write(String.Format("{0:X2}", message) + " ");
            }
            catch (TimeoutException) { Log.write("read timeout"); }
            catch (IOException) { Log.write("read IOException"); }
            catch (ThreadAbortException) { Log.write("read thread aborted"); }
            catch (Exception ex) { MessageBox.Show(ex.ToString()); }
            finally { }
        }
    }

現在,我將重寫程式碼,以便從同一執行緒讀寫串列埠,以檢視這是否有任何區別。
編輯
根據Jim的評論,我在ioException catch語句中添加了以下內容:

        catch (IOException ex)
        {
            MessageBox.Show(ex.ToString());
            Log.write("IOexception");
            _readBGW.CancelAsync();
            Thread.Sleep(100);
            _serialPort.Close();
            Thread.Sleep(100);
            _serialPort.Open();
            Thread.Sleep(100);
            _readBGW.RunWorkerAsync();
            _serialPort.Write(packet, 0, packet.Length);
        }

希望通過停止後臺工作程式的serialport.read、關閉埠、重新開啟埠、再次執行後臺工作程式並再次嘗試寫入相同的命令,可以成功地從該錯誤中恢復。訊息框仍然會阻止程式碼,這樣我就可以看到錯誤發生的時間,並可以監視它是如何恢復的。
我不喜歡像這樣修補軟體,但如果它能工作,它就會工作。
編輯2
在將程式碼新增到上面之後,我的軟體再次崩潰,但現在當我呼叫_serialport.close()時,它丟擲一個“UnauthorizedAccessException-拒絕訪問埠”;

System.UnauthorizedAccessException was unhandled
Message=Access to the port is denied.
Source=System
StackTrace:
at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
at System.IO.Ports.InternalResources.WinIOError()
at System.IO.Ports.SerialStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Ports.SerialPort.Dispose(Boolean disposing)
at System.IO.Ports.SerialPort.Close()
at BatteryCharger.CommunicationClass.ApiTransmission(TXpacket transmission) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\CommunicationClass.cs:line 436
at BatteryCharger.CommunicationClass.tx1(TXpacket packet, String callingMethod) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\CommunicationClass.cs:line 356
at BatteryCharger.XBee.setPin(String pinID, Byte status, XBee slave) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\XBee.cs:line 215
at BatteryCharger.XBee.setPins(Int32 pins, XBee slave) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\XBee.cs:line 177
at BatteryCharger.BatteryCharger.requestVoltage(Int32 block) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\BatteryCharger.cs:line 595
at BatteryCharger.BatteryCharger.requestVoltages() in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\BatteryCharger.cs:line 612
at BatteryCharger.Form1.RunCommandOn(List`1 battList, Int32 command, Double lowerLimit, Double upperLimit) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\Form1.cs:line 522
at BatteryCharger.Form1.chargeBlock(Int32 blockNr, Double lowerLimit, Double upperLimit) in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\Form1.cs:line 689
at BatteryCharger.Form1.<btnCheckCapacities_Click>b__13() in E:\Mijn Documenten\Research\Copy of BatteryCharger_V12\BatteryCharger\Form1.cs:line 619
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: 

這是怎麼回事?

解決辦法

根據我的經驗,10個執行緒中有9個在另一個執行緒(無論是否終止)沒有對硬體埠的獨佔訪問時發生這種情況。
嘗試使用synclock為埠操作編寫一個包裝器,最重要的是它的開啟/關閉。https://msdn.microsoft.com/en-us/library/3a86s51t.aspx
類似地,我通常認為嘗試/捕獲控制硬體是一種壞做法,除非有足夠的異常處理。
原因(此處可能適用)是,在引發異常的情況下,硬體將鎖定,更糟的是,異常將掩蓋錯誤的真正原因。
在上面的程式碼中,我看到了樣式中訊息的輸出

DebugPrint(ex.Message); 

這樣做比較好

DebugPrint(ex.tostring()); 

因為這也將匯出異常中的堆疊跟蹤。
我要做的是實現一個異常記錄器,將這些異常寫入正在執行的計算機中某個位置的(時間戳)文字檔案。跟蹤記錄的異常資料(連同所有相關資訊)可以更好地理解為什麼會發生這種情況。