最近花了差不多1天的時間在折騰一個Bug,該Bug的表象如下: 這個Bug還特別獨特,在開發電腦中無提示,在終端用戶那裡每次使用軟體的時候都報這個。仔細思考了一下最近在源碼中新添加的功能,沒發現有啥特別明顯的問題。於是,根據字面意思的理解是“運行時錯誤”,所以一開始解決這個問題的思路是將所有應用程式 ...
最近花了差不多1天的時間在折騰一個Bug,該Bug的表象如下:
這個Bug還特別獨特,在開發電腦中無提示,在終端用戶那裡每次使用軟體的時候都報這個。仔細思考了一下最近在源碼中新添加的功能,沒發現有啥特別明顯的問題。於是,根據字面意思的理解是“運行時錯誤”,所以一開始解決這個問題的思路是將所有應用程式的運行時拷貝至應用程式目錄。嘗試過之後,依然報這個異常。分析可能跟運行時的動態鏈接庫沒有關係。於是,調整解決問題的思路,考慮將工程中新添加的代碼進行分割。部分部分的測試新添加的代碼到底那裡有問題,排查到最後是這個函數內部發生了異常。異常函數的代碼如下:
void CleanSerialPort() { if(g_hEvent != NULL) { CloseHandle(g_hEvent); g_hEvent = NULL; } if(g_SerialPort.IsOpen()) { COMMPROP properties; memset(&properties, 0, sizeof(properties)); g_SerialPort.GetProperties(); g_SerialPort.ClearWriteBuffer(); g_SerialPort.ClearReadBuffer(); g_SerialPort.Flush(); g_SerialPort.CancelIo(); g_SerialPort.Close(); } }
一開始仔細讀了幾遍代碼,沒發現有啥異常的地方。在這個時候,只能通過一點一點調試編譯器來最終確定問題在那裡。調試編譯器的方式比較傳統,是通過MessageBox消息來實現。最終定位到g_SerialPort的Close函數。該函數的原型如下:
void CSerialPort::Close() { if(IsOpen()) { CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; } }
仔細讀了幾遍,依然沒發現問題在那裡。但是,又反覆琢磨了一段時間之後,把這個問題想清楚了。當把串口數據線拔出之後,串口設備已經在操作系統中不存在,這個時候卻還要去強行關閉串口設備,此時當然會發生異常,不發生異常才是不正常的情況。於是,考慮在這一大段代碼的外圍加一個try…catch…,果不其然,成功捕獲到異常,異常代碼的值為0x05,代表的含義為ERROR_ACCESS_DENIED,表示拒絕訪問。目前操作系統中已經不存在該串口設備,因此拒絕訪問是正常的狀態。最終的代碼如下:
void CleanSerialPort() { try { if(g_hEvent != NULL) { CloseHandle(g_hEvent); g_hEvent = NULL; } if(g_SerialPort.IsOpen()) { COMMPROP properties; memset(&properties, 0, sizeof(properties)); g_SerialPort.GetProperties(); g_SerialPort.ClearWriteBuffer(); g_SerialPort.ClearReadBuffer(); g_SerialPort.Flush(); g_SerialPort.CancelIo(); g_SerialPort.Close(); } } catch(CSerialException &e) { ATLTRACE("Unexpected CSerialPort exception, Error:%u\n", e.m_dwError); UNREFERENCED_PARAMETER(e); } }
捕獲到這個異常之後,在不關閉應用程式的情況下,不影響再次初始化串口設備,而應用程式也不報一開始的Runtime error,因此預設目前的處理方案可行。