Remon Spekreijse CSerialPort串口类的修正版2014-01-10
生活随笔
收集整理的這篇文章主要介紹了
Remon Spekreijse CSerialPort串口类的修正版2014-01-10
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
轉自:http://m.blog.csdn.net/blog/itas109/18358297#
2014-1-16閱讀691?評論0
?如需轉載請標明出處:http://blog.csdn.net/itas109
?
?
這是一份優秀的類文件,好多的地方值得我們學習,具體在多線程,事件,自定義消息,類的封裝方面等等。
Remon提供的串口類網址為:?http://codeguru.earthweb.com/network/serialport.shtml,
?
其他貢獻者:http://blog.csdn.net/liquanhai/article/details/6941574
代碼下載:http://download.csdn.net/detail/itas109/6855323
代碼托管:https://code.csdn.net/itas109/cserialport
?
源代碼如下:
?CSerialPort.h
/* ** FILENAME CSerialPort.h ** ** PURPOSE This class can read, write and watch one serial port. ** It sends messages to its owner when something happends on the port ** The class creates a thread for reading and writing so the main ** program is not blocked. ** ** CREATION DATE 15-09-1997 ** LAST MODIFICATION 12-11-1997 ** ** AUTHOR Remon Spekreijse ** ** ************************************************************************************ ** author: mrlong date:2007-12-25 ** ** 改進 ** 1) 增加ClosePort ** 2) 增加 writetoProt() 兩個方法 ** 3) 增加 SendData 與 RecvData 方法 ** ************************************************************************************ ** author:liquanhai date:2011-11-04 ** 改進 ** 1)增加 ClosePort中交出控制權,防止死鎖問題 ** 2) 增加 ReceiveChar中防止線程死鎖 *************************************************************************************** ** author: itas109 date:2014-01-10 ** Blog:blog.csdn.net/itas109 ** ** 改進 ** 1) 解決COM10以上端口無法顯示的問題 ** 2) 擴展可選擇端口,最大值MaxSerialPortNum可以自定義 ** 3) 添加QueryKey()和Hkey2ComboBox兩個方法,用于自動查詢當前有效的串口號。 ** */#ifndef __SERIALPORT_H__ #define __SERIALPORT_H__#define WM_COMM_BREAK_DETECTED WM_USER+1 // A break was detected on input. #define WM_COMM_CTS_DETECTED WM_USER+2 // The CTS (clear-to-send) signal changed state. #define WM_COMM_DSR_DETECTED WM_USER+3 // The DSR (data-set-ready) signal changed state. #define WM_COMM_ERR_DETECTED WM_USER+4 // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. #define WM_COMM_RING_DETECTED WM_USER+5 // A ring indicator was detected. #define WM_COMM_RLSD_DETECTED WM_USER+6 // The RLSD (receive-line-signal-detect) signal changed state. #define WM_COMM_RXCHAR WM_USER+7 // A character was received and placed in the input buffer. #define WM_COMM_RXFLAG_DETECTED WM_USER+8 // The event character was received and placed in the input buffer. #define WM_COMM_TXEMPTY_DETECTED WM_USER+9 // The last character in the output buffer was sent. #define MaxSerialPortNum 20 ///有效的串口總個數,不是串口的號 //add by itas109 2014-01-09 class CSerialPort { public:// contruction and destructionCSerialPort();virtual ~CSerialPort();// port initialisation///串口初始化BOOL InitPort(CWnd* pPortOwner, //串口句柄UINT portnr = 1, //端口號UINT baud = 19200, //波特率char parity = 'N', //奇偶校驗UINT databits = 8, //數據位UINT stopbits = 1, //停止位DWORD dwCommEvents = EV_RXCHAR, //消息類型UINT writebuffersize = 1024//寫緩存);// start/stop comm watching///控制串口監視線程BOOL StartMonitoring();//開始監聽BOOL RestartMonitoring();//重新監聽BOOL StopMonitoring();//停止監聽DWORD GetWriteBufferSize();///獲取寫緩沖大小DWORD GetCommEvents();///獲取事件DCB GetDCB();///獲取DCBCOMMTIMEOUTS GetCommTimeOuts();//BOOL SetCommTimeOuts(COMMTIMEOUTS * lpTimeOuts);///寫數據到串口void WriteToPort(char* string);void WriteToPort(char* string,int n);//add by mrlong 2007-12-25void WriteToPort(LPCTSTR string);//add by mrlong 2007-12-25void WriteToPort(LPCTSTR string,int n);//add by mrlong 2007-12-2void WriteToPort(BYTE* Buffer, int n);// add by mrlongvoid ClosePort();//關閉串口 // add by mrlong 2007-12-25BOOL RecvData(LPTSTR lpszData, const int nSize);//串口發送函數 by mrlong 2008-2-15void SendData(LPCTSTR lpszData, const int nLength);//串口接收函數 by mrlong 2008-2-15void QueryKey(HKEY hKey);///查詢注冊表的串口號,將值存于數組中void Hkey2ComboBox(CComboBox& m_PortNO);///將QueryKey查詢到的串口號添加到CComboBox控件中protected:// protected memberfunctionsvoid ProcessErrorMessage(char* ErrorText);///錯誤處理static UINT CommThread(LPVOID pParam);///線程函數static void ReceiveChar(CSerialPort* port, COMSTAT comstat);///接收字符static void WriteChar(CSerialPort* port);///寫字符// threadCWinThread* m_Thread;///thread監視線程BOOL m_bIsSuspened;///thread監視線程是否掛起// synchronisation objectsCRITICAL_SECTION m_csCommunicationSync;///臨界資源BOOL m_bThreadAlive;///監視線程運行標志// handlesHANDLE m_hWriteEvent;HANDLE m_hComm;///串口句柄HANDLE m_hShutdownEvent;// Event array. // One element is used for each event. There are two event handles for each port.// A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).// There is a general shutdown when the port is closed.///事件數組,包括一個寫事件,接收事件,關閉事件///一個元素用于一個事件。有兩個事件線程處理端口。///寫事件和接收字符事件位于overlapped結構體(m_ov.hEvent)中///當端口關閉時,有一個通用的關閉。HANDLE m_hEventArray[3];// structuresOVERLAPPED m_ov;///異步I/OCOMMTIMEOUTS m_CommTimeouts;///超時設置DCB m_dcb;///設備控制塊// owner windowCWnd* m_pOwner;// miscUINT m_nPortNr;char* m_szWriteBuffer;///寫緩沖區DWORD m_dwCommEvents;DWORD m_nWriteBufferSize;///寫緩沖大小int m_nWriteSize; //寫入字節數 //add by mrlong 2007-12-25 };#endif __SERIALPORT_H__?
CSerialPort.cpp
/* ** FILENAME CSerialPort.cpp ** ** PURPOSE This class can read, write and watch one serial port. ** It sends messages to its owner when something happends on the port ** The class creates a thread for reading and writing so the main ** program is not blocked. ** ** CREATION DATE 15-09-1997 ** LAST MODIFICATION 12-11-1997 ** ** AUTHOR Remon Spekreijse ** ** */#include "stdafx.h" #include "SerialPort.h"#include <assert.h>int m_nComArray[20]; // // Constructor // CSerialPort::CSerialPort()///構造函數 {m_hComm = NULL;// initialize overlapped structure members to zero///初始化異步結構體m_ov.Offset = 0;m_ov.OffsetHigh = 0;// create eventsm_ov.hEvent = NULL;m_hWriteEvent = NULL;m_hShutdownEvent = NULL;m_szWriteBuffer = NULL;m_bThreadAlive = FALSE;m_nWriteSize=1;///m_bIsSuspened = FALSE;/// }// // Delete dynamic memory // CSerialPort::~CSerialPort()///析構函數 {do{SetEvent(m_hShutdownEvent);} while (m_bThreadAlive);// if the port is still opened: close it if (m_hComm != NULL){CloseHandle(m_hComm);m_hComm = NULL;}// Close Handles if(m_hShutdownEvent!=NULL)CloseHandle( m_hShutdownEvent); if(m_ov.hEvent!=NULL)CloseHandle( m_ov.hEvent ); if(m_hWriteEvent!=NULL)CloseHandle( m_hWriteEvent ); TRACE("Thread ended\n");delete [] m_szWriteBuffer; }// // Initialize the port. This can be port 1 to MaxSerialPortNum. ///初始化串口。只能是1-MaxSerialPortNum // BOOL CSerialPort::InitPort(CWnd* pPortOwner, // the owner (CWnd) of the port (receives message)UINT portnr, // portnumber (1..MaxSerialPortNum)UINT baud, // baudratechar parity, // parity UINT databits, // databits UINT stopbits, // stopbits DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etcUINT writebuffersize) // size to the writebuffer {assert(portnr > 0 && portnr < MaxSerialPortNum+1);///add by itas109 2014-01-09assert(pPortOwner != NULL);// if the thread is alive: Kill///如果線程存在,則關掉進程if (m_bThreadAlive){do{SetEvent(m_hShutdownEvent);} while (m_bThreadAlive);TRACE("Thread ended\n");}// create eventsif (m_ov.hEvent != NULL)ResetEvent(m_ov.hEvent);elsem_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);if (m_hWriteEvent != NULL)ResetEvent(m_hWriteEvent);elsem_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);if (m_hShutdownEvent != NULL)ResetEvent(m_hShutdownEvent);elsem_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);// initialize the event objects///事件數組初始化,設定優先級別m_hEventArray[0] = m_hShutdownEvent; // highest prioritym_hEventArray[1] = m_ov.hEvent;m_hEventArray[2] = m_hWriteEvent;// initialize critical section///初始化臨界資源InitializeCriticalSection(&m_csCommunicationSync);// set buffersize for writing and save the ownerm_pOwner = pPortOwner;if (m_szWriteBuffer != NULL)delete [] m_szWriteBuffer;m_szWriteBuffer = new char[writebuffersize];m_nPortNr = portnr;m_nWriteBufferSize = writebuffersize;m_dwCommEvents = dwCommEvents;BOOL bResult = FALSE;char *szPort = new char[50];char *szBaud = new char[50];/*多個線程操作相同的數據時,一般是需要按順序訪問的,否則會引導數據錯亂,無法控制數據,變成隨機變量。為解決這個問題,就需要引入互斥變量,讓每個線程都按順序地訪問變量。這樣就需要使用EnterCriticalSection和LeaveCriticalSection函數。*/// now it critical!///進入臨界區EnterCriticalSection(&m_csCommunicationSync);// if the port is already opened: close it///串口已打開就關掉if (m_hComm != NULL){CloseHandle(m_hComm);m_hComm = NULL;}// prepare port strings///串口參數sprintf(szPort, "\\\\.\\COM%d", portnr);///可以顯示COM10以上端口//add by itas109 2014-01-09sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits);// get a handle to the port/*通信程序在CreateFile處指定串口設備及相關的操作屬性,再返回一個句柄,該句柄將被用于后續的通信操作,并貫穿整個通信過程串口打開后,其屬性被設置為默認值,根據具體需要,通過調用GetCommState(hComm,&&dcb)讀取當前串口設備控制塊DCB設置,修改后通過SetCommState(hComm,&&dcb)將其寫入。運用ReadFile()與WriteFile()這兩個API函數實現串口讀寫操作,若為異步通信方式,兩函數中最后一個參數為指向OVERLAPPED結構的非空指針,在讀寫函數返回值為FALSE的情況下,調用GetLastError()函數,返回值為ERROR_IO_PENDING,表明I/O操作懸掛,即操作轉入后臺繼續執行。此時,可以用WaitForSingleObject()來等待結束信號并設置最長等待時間*/m_hComm = CreateFile(szPort, // communication port string (COMX)GENERIC_READ | GENERIC_WRITE, // read/write types0, // comm devices must be opened with exclusive accessNULL, // no security attributesOPEN_EXISTING, // comm devices must use OPEN_EXISTINGFILE_FLAG_OVERLAPPED, // Async I/O0); // template must be 0 for comm devices///創建失敗if (m_hComm == INVALID_HANDLE_VALUE){// port not founddelete [] szPort;delete [] szBaud;return FALSE;}// set the timeout values///設置超時m_CommTimeouts.ReadIntervalTimeout = 1000;m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;m_CommTimeouts.ReadTotalTimeoutConstant = 1000;m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;m_CommTimeouts.WriteTotalTimeoutConstant = 1000;// configure///配置///分別調用Windows API設置串口參數if (SetCommTimeouts(m_hComm, &m_CommTimeouts))///設置超時{/*若對端口數據的響應時間要求較嚴格,可采用事件驅動方式。事件驅動方式通過設置事件通知,當所希望的事件發生時,Windows發出該事件已發生的通知,這與DOS環境下的中斷方式很相似。Windows定義了9種串口通信事件,較常用的有以下三種:EV_RXCHAR:接收到一個字節,并放入輸入緩沖區;EV_TXEMPTY:輸出緩沖區中的最后一個字符,發送出去;EV_RXFLAG:接收到事件字符(DCB結構中EvtChar成員),放入輸入緩沖區在用SetCommMask()指定了有用的事件后,應用程序可調用WaitCommEvent()來等待事件的發生。SetCommMask(hComm,0)可使WaitCommEvent()中止*/if (SetCommMask(m_hComm, dwCommEvents))///設置通信事件{if (GetCommState(m_hComm, &m_dcb))///獲取當前DCB參數{m_dcb.EvtChar = 'q';///設置字件字符m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high!if (BuildCommDCB(szBaud, &m_dcb))///填寫DCB結構{if (SetCommState(m_hComm, &m_dcb))///配置DCB; // normal operation... continueelseProcessErrorMessage("SetCommState()");}elseProcessErrorMessage("BuildCommDCB()");}elseProcessErrorMessage("GetCommState()");}elseProcessErrorMessage("SetCommMask()");}elseProcessErrorMessage("SetCommTimeouts()");delete [] szPort;delete [] szBaud;// flush the port///終止讀寫并清空接收和發送PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);// release critical section///釋放臨界資源LeaveCriticalSection(&m_csCommunicationSync);TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);return TRUE; }// // The CommThread Function. ///線程函數 ///監視線程的大致流程: ///檢查串口-->進入循環{WaitCommEvent(不阻塞詢問)詢問事件-->如果有事件來到-->到相應處理(關閉\讀\寫)} // UINT CSerialPort::CommThread(LPVOID pParam) {// Cast the void pointer passed to the thread back to// a pointer of CSerialPort class///CSerialPort類的指針CSerialPort *port = (CSerialPort*)pParam;// Set the status variable in the dialog class to// TRUE to indicate the thread is running.///TRUE表示線程正在運行port->m_bThreadAlive = TRUE; // Misc. variablesDWORD BytesTransfered = 0; DWORD Event = 0;DWORD CommEvent = 0;DWORD dwError = 0;COMSTAT comstat;BOOL bResult = TRUE;// Clear comm buffers at startup///開始時清除串口緩沖if (port->m_hComm) // check if the port is openedPurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);// begin forever loop. This loop will run as long as the thread is alive.///只要線程存在就不斷讀取數據for (;;) { // Make a call to WaitCommEvent(). This call will return immediatly// because our port was created as an async port (FILE_FLAG_OVERLAPPED// and an m_OverlappedStructerlapped structure specified). This call will cause the // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to // be placed in a non-signeled state if there are no bytes available to be read,// or to a signeled state if there are bytes available. If this event handle // is set to the non-signeled state, it will be set to signeled when a // character arrives at the port.// we do this for each port!/*WaitCommEvent函數第3個參數1pOverlapped可以是一個OVERLAPPED結構的變量指針,也可以是NULL,當用NULL時,表示該函數是同步的,否則表示該函數是異步的。調用WaitCommEvent時,如果異步操作不能立即完成,會立即返回FALSE,系統在WaitCommEvent返回前將OVERLAPPED結構成員hEvent設為無信號狀態,等到產生通信事件時,系統將其置有信號*/bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);///表示該函數是異步的if (!bResult) { // If WaitCommEvent() returns FALSE, process the last error to determin// the reason..///如果WaitCommEvent返回Error為FALSE,則查詢錯誤信息switch (dwError = GetLastError()) { case ERROR_IO_PENDING: ///正常情況,沒有字符可讀{ // This is a normal return value if there are no bytes// to read at the port.// Do nothing and continuebreak;}case 87:///系統錯誤{// Under Windows NT, this value is returned for some reason.// I have not investigated why, but it is also a valid reply// Also do nothing and continue.break;}default:///發生其他錯誤,其中有串口讀寫中斷開串口連接的錯誤{// All other error codes indicate a serious error has// occured. Process this error.port->ProcessErrorMessage("WaitCommEvent()");break;}}}else ///WaitCommEvent()能正確返回{// If WaitCommEvent() returns TRUE, check to be sure there are// actually bytes in the buffer to read. //// If you are reading more than one byte at a time from the buffer // (which this program does not do) you will have the situation occur // where the first byte to arrive will cause the WaitForMultipleObjects() // function to stop waiting. The WaitForMultipleObjects() function // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state// as it returns. //// If in the time between the reset of this event and the call to // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again// to the signeled state. When the call to ReadFile() occurs, it will // read all of the bytes from the buffer, and the program will// loop back around to WaitCommEvent().// // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,// but there are no bytes available to read. If you proceed and call// ReadFile(), it will return immediatly due to the async port setup, but// GetOverlappedResults() will not return until the next character arrives.//// It is not desirable for the GetOverlappedResults() function to be in // this state. The thread shutdown event (event 0) and the WriteFile()// event (Event2) will not work if the thread is blocked by GetOverlappedResults().//// The solution to this is to check the buffer with a call to ClearCommError().// This call will reset the event handle, and if there are no bytes to read// we can loop back through WaitCommEvent() again, then proceed.// If there are really bytes to read, do nothing and proceed.bResult = ClearCommError(port->m_hComm, &dwError, &comstat);if (comstat.cbInQue == 0)continue;} // end if bResult///主等待函數,會阻塞線程// Main wait function. This function will normally block the thread// until one of nine events occur that require action.///等待3個事件:關斷/讀/寫,有一個事件發生就返回Event = WaitForMultipleObjects(3, ///3個事件port->m_hEventArray, ///事件數組FALSE, ///有一個事件發生就返回INFINITE);///超時時間switch (Event){case 0:{// Shutdown event. This is event zero so it will be// the higest priority and be serviced first.///關斷事件,關閉串口CloseHandle(port->m_hComm);port->m_hComm=NULL;port->m_bThreadAlive = FALSE;// Kill this thread. break is not needed, but makes me feel better.AfxEndThread(100);break;}case 1: /// read event將定義的各種消息發送出去{memset(&comstat, 0, sizeof(COMSTAT)); GetCommMask(port->m_hComm, &CommEvent);if (CommEvent & EV_RXCHAR)//接收到字符,并置于輸入緩沖區中// Receive character event from port.ReceiveChar(port, comstat);if (CommEvent & EV_CTS)//CTS信號狀態發生變化::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);if (CommEvent & EV_BREAK)//輸入中發生中斷 ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);if (CommEvent & EV_ERR)//發生線路狀態錯誤,線路狀態錯誤包括CE_FRAME,CE_OVERRUN和CE_RXPARITY ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr); if (CommEvent & EV_RING)//檢測到振鈴指示 ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr); if (CommEvent & EV_RXFLAG)//接收到事件字符,并置于輸入緩沖區中 ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);break;} case 2: /// write event發送數據{// Write character event from portWriteChar(port);break;}default:{AfxMessageBox("接收有問題!");break;}} // end switch} // close forever loopreturn 0; }// // start comm watching ///開啟監視線程 // BOOL CSerialPort::StartMonitoring() {if (!(m_Thread = AfxBeginThread(CommThread, this)))return FALSE;TRACE("Thread started\n");m_bIsSuspened = false;return TRUE; }// // Restart the comm thread ///復位監視線程 // BOOL CSerialPort::RestartMonitoring() {TRACE("Thread resumed\n");m_bIsSuspened = false;m_Thread->ResumeThread();return TRUE; }// // Suspend the comm thread ///掛起監視線程 // BOOL CSerialPort::StopMonitoring() {TRACE("Thread suspended\n");m_bIsSuspened = true;m_Thread->SuspendThread(); return TRUE; }// // If there is a error, give the right message ///如果有錯誤,給出提示 // void CSerialPort::ProcessErrorMessage(char* ErrorText) {char *Temp = new char[200];LPVOID lpMsgBuf;FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language(LPTSTR) &lpMsgBuf,0,NULL );sprintf(Temp, "WARNING: %s Failed with the following error: \n%s\nPort: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr); MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);LocalFree(lpMsgBuf);delete [] Temp;return;/// }// // Write a character. ///寫數據 // void CSerialPort::WriteChar(CSerialPort* port) {BOOL bWrite = TRUE;BOOL bResult = TRUE;DWORD BytesSent = 0;ResetEvent(port->m_hWriteEvent);///復位寫事件句柄// Gain ownership of the critical sectionEnterCriticalSection(&port->m_csCommunicationSync);if (bWrite){// Initailize variablesport->m_ov.Offset = 0;port->m_ov.OffsetHigh = 0;// Clear bufferPurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);///串口寫入bResult = WriteFile(port->m_hComm, // Handle to COMM Portport->m_szWriteBuffer, // Pointer to message buffer in calling finction // strlen((char*)port->m_szWriteBuffer), // Length of message to sendport->m_nWriteSize, // Length of message to send // add by mrlong&BytesSent, // Where to store the number of bytes sent&port->m_ov); // Overlapped structure// deal with any error codesif (!bResult) {DWORD dwError = GetLastError();switch (dwError){case ERROR_IO_PENDING:{// continue to GetOverlappedResults()BytesSent = 0;bWrite = FALSE;break;}default:{// all other error codesport->ProcessErrorMessage("WriteFile()");break;///}}} else{LeaveCriticalSection(&port->m_csCommunicationSync);}} // end if(bWrite)if (!bWrite){bWrite = TRUE;bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port &port->m_ov, // Overlapped structure&BytesSent, // Stores number of bytes sentTRUE); // Wait flagLeaveCriticalSection(&port->m_csCommunicationSync);// deal with the error code //if (!bResult) ///注釋掉了,為什么?{//port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");} } // end if (!bWrite)//Verify that the data size send equals what we tried to sendif (BytesSent != port->m_nWriteSize) // Length of message to send){TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));} // ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_TXEMPTY_DETECTED, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr); // ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_TXEMPTY_DETECTED,0,(LPARAM) port->m_nPortNr); }// // Character received. Inform the owner ///讀數據 // void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat) {BOOL bRead = TRUE; BOOL bResult = TRUE;DWORD dwError = 0;DWORD BytesRead = 0;unsigned char RXBuff;for (;;) { //add by liquanhai 防止死鎖 2011-11-06if(WaitForSingleObject(port->m_hShutdownEvent,0) == WAIT_OBJECT_0)return;// Gain ownership of the comm port critical section.// This process guarantees no other part of this program // is using the port object. EnterCriticalSection(&port->m_csCommunicationSync);// ClearCommError() will update the COMSTAT structure and// clear any other errors.///更新COMSTATbResult = ClearCommError(port->m_hComm, &dwError, &comstat);LeaveCriticalSection(&port->m_csCommunicationSync);// start forever loop. I use this type of loop because I// do not know at runtime how many loops this will have to// run. My solution is to start a forever loop and to// break out of it when I have processed all of the// data available. Be careful with this approach and// be sure your loop will exit.// My reasons for this are not as clear in this sample // as it is in my production code, but I have found this // solutiion to be the most efficient way to do this.///所有字符均被讀出,中斷循環if (comstat.cbInQue == 0){// break out when all bytes have been readbreak;}EnterCriticalSection(&port->m_csCommunicationSync);if (bRead){///串口讀出,讀出緩沖區中字節bResult = ReadFile(port->m_hComm, // Handle to COMM port &RXBuff, // RX Buffer Pointer1, // Read one byte&BytesRead, // Stores number of bytes read&port->m_ov); // pointer to the m_ov structure// deal with the error code ///若返回錯誤,錯誤處理if (!bResult) { switch (dwError = GetLastError()) { case ERROR_IO_PENDING: { // asynchronous i/o is still in progress // Proceed on to GetOverlappedResults();///異步IO仍在進行bRead = FALSE;break;}default:{// Another error has occured. Process this error.port->ProcessErrorMessage("ReadFile()");break;//return;///防止讀寫數據時,串口非正常斷開導致死循環一直執行。add by itas109 2014-01-09 與上面liquanhai添加防死鎖的代碼差不多} }}else///ReadFile返回TRUE{// ReadFile() returned complete. It is not necessary to call GetOverlappedResults()bRead = TRUE;}} // close if (bRead)///異步IO操作仍在進行,需要調用GetOverlappedResult查詢if (!bRead){bRead = TRUE;bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port &port->m_ov, // Overlapped structure&BytesRead, // Stores number of bytes readTRUE); // Wait flag// deal with the error code if (!bResult) {port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");} } // close if (!bRead)LeaveCriticalSection(&port->m_csCommunicationSync);// notify parent that a byte was received::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);} // end forever loop}// // Write a string to the port // void CSerialPort::WriteToPort(char* string) { assert(m_hComm != 0);memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));strcpy(m_szWriteBuffer, string);m_nWriteSize=strlen(string);// set event for writeSetEvent(m_hWriteEvent); }void CSerialPort::WriteToPort(char* string,int n) { assert(m_hComm != 0);memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); // memset(m_szWriteBuffer, 0, n); // strncpy(m_szWriteBuffer, string, n);memcpy(m_szWriteBuffer, string, n);m_nWriteSize=n;// set event for writeSetEvent(m_hWriteEvent); }void CSerialPort::WriteToPort(LPCTSTR string,int n) { assert(m_hComm != 0);memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));memcpy(m_szWriteBuffer, string, n);m_nWriteSize = n;// set event for writeSetEvent(m_hWriteEvent); }void CSerialPort::WriteToPort(LPCTSTR string) { assert(m_hComm != 0);memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));strcpy(m_szWriteBuffer, string);m_nWriteSize=strlen(string);// set event for writeSetEvent(m_hWriteEvent); }void CSerialPort::WriteToPort(BYTE* Buffer, int n) {assert(m_hComm != 0);memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));int i;for(i=0; i<n; i++){m_szWriteBuffer[i] = Buffer[i];}m_nWriteSize=n;// set event for writeSetEvent(m_hWriteEvent); }// // Return the device control block // DCB CSerialPort::GetDCB() {return m_dcb; }// // Return the communication event masks // DWORD CSerialPort::GetCommEvents() {return m_dwCommEvents; }// // Return the output buffer size // DWORD CSerialPort::GetWriteBufferSize() {return m_nWriteBufferSize; }void CSerialPort::ClosePort() {if(m_bIsSuspened){RestartMonitoring();}if (m_bThreadAlive){MSG message;while (m_bThreadAlive){//add by liquanhai 防止死鎖 2011-11-06if(::PeekMessage(&message, m_pOwner->m_hWnd, 0, 0, PM_REMOVE)){::TranslateMessage(&message);::DispatchMessage(&message);}SetEvent(m_hShutdownEvent);}TRACE("Thread ended\n");}if(m_szWriteBuffer != NULL){delete [] m_szWriteBuffer;m_szWriteBuffer = NULL;}if(m_hComm){CloseHandle(m_hComm);m_hComm = NULL;}// Close Handles if(m_hShutdownEvent!=NULL)ResetEvent(m_hShutdownEvent);if(m_ov.hEvent!=NULL)ResetEvent(m_ov.hEvent);if(m_hWriteEvent!=NULL)ResetEvent(m_hWriteEvent); }void CSerialPort::SendData(LPCTSTR lpszData, const int nLength) {assert(m_hComm != 0);memset(m_szWriteBuffer, 0, nLength);strcpy(m_szWriteBuffer, lpszData);m_nWriteSize=nLength;// set event for writeSetEvent(m_hWriteEvent); }BOOL CSerialPort::RecvData(LPTSTR lpszData, const int nSize) {////接收數據//assert(m_hComm!=0);memset(lpszData,0,nSize);DWORD mylen = 0;DWORD mylen2 = 0;while (mylen < nSize) {if(!ReadFile(m_hComm,lpszData,nSize,&mylen2,NULL)) return FALSE;mylen += mylen2; }return TRUE; }COMMTIMEOUTS CSerialPort:: GetCommTimeOuts() {return m_CommTimeouts; }BOOL CSerialPort::SetCommTimeOuts(COMMTIMEOUTS *lpTimeOuts) {SetCommTimeouts(m_hComm, lpTimeOuts);//設置超時 return true; } /* void CSerialPort::ClosePort() {do{SetEvent(m_hShutdownEvent);} while (m_bThreadAlive);// if the port is still opened: close it if (m_hComm != NULL){CloseHandle(m_hComm);m_hComm = NULL;}// Close Handles if(m_hShutdownEvent!=NULL)CloseHandle( m_hShutdownEvent); if(m_ov.hEvent!=NULL)CloseHandle( m_ov.hEvent ); if(m_hWriteEvent!=NULL)CloseHandle( m_hWriteEvent ); TRACE("Thread ended\n");delete [] m_szWriteBuffer; } */// ///查詢注冊表的串口號,將值存于數組中 ///本代碼參考于mingojiang的獲取串口邏輯名代碼 // void CSerialPort::QueryKey(HKEY hKey) { #define MAX_KEY_LENGTH 255#define MAX_VALUE_NAME 16383// TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name// DWORD cbName; // size of name string TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name DWORD cchClassName = MAX_PATH; // size of class string DWORD cSubKeys=0; // number of subkeys DWORD cbMaxSubKey; // longest subkey size DWORD cchMaxClass; // longest class string DWORD cValues; // number of values for key DWORD cchMaxValue; // longest value name DWORD cbMaxValueData; // longest value data DWORD cbSecurityDescriptor; // size of security descriptor FILETIME ftLastWriteTime; // last write time DWORD i, retCode; TCHAR achValue[MAX_VALUE_NAME]; DWORD cchValue = MAX_VALUE_NAME; // Get the class name and the value count. retCode = RegQueryInfoKey(hKey, // key handle achClass, // buffer for class name &cchClassName, // size of class string NULL, // reserved &cSubKeys, // number of subkeys &cbMaxSubKey, // longest subkey size &cchMaxClass, // longest class string &cValues, // number of values for this key &cchMaxValue, // longest value name &cbMaxValueData, // longest value data &cbSecurityDescriptor, // security descriptor &ftLastWriteTime); // last write time for (i=0;i<20;i++)///存放串口號的數組初始化{m_nComArray[i] = -1;}// Enumerate the key values. if (cValues > 0) {for (i=0, retCode=ERROR_SUCCESS; i<cValues; i++) { cchValue = MAX_VALUE_NAME; achValue[0] = '\0'; if (ERROR_SUCCESS == RegEnumValue(hKey, i, achValue, &cchValue, NULL, NULL, NULL, NULL)) { CString szName(achValue);if (-1 != szName.Find(_T("Serial")) || -1 != szName.Find(_T("VCom")) ){BYTE strDSName[10]; memset(strDSName, 0, 10);DWORD nValueType = 0, nBuffLen = 10;if (ERROR_SUCCESS == RegQueryValueEx(hKey, (LPCTSTR)achValue, NULL, &nValueType, strDSName, &nBuffLen)){int nIndex = -1;while(++nIndex < MaxSerialPortNum){if (-1 == m_nComArray[nIndex]) {m_nComArray[nIndex] = atoi((char*)(strDSName + 3));break;}}}}} }}else{AfxMessageBox(_T("本機沒有串口....."));}}void CSerialPort::Hkey2ComboBox(CComboBox& m_PortNO) {HKEY hTestKey;bool Flag = FALSE;///僅是XP系統的注冊表位置,其他系統根據實際情況做修改if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_READ, &hTestKey) ){QueryKey(hTestKey);}RegCloseKey(hTestKey);int i = 0;m_PortNO.ResetContent();///刷新時,清空下拉列表內容while(i < MaxSerialPortNum && -1 != m_nComArray[i]){CString szCom; szCom.Format(_T("COM%d"), m_nComArray[i]);m_PortNO.InsertString(i, szCom.GetBuffer(5));++i;Flag = TRUE;if (Flag)///把第一個發現的串口設為下拉列表的默認值m_PortNO.SetCurSel(0);}}轉載于:https://www.cnblogs.com/zjoch/p/4247835.html
總結
以上是生活随笔為你收集整理的Remon Spekreijse CSerialPort串口类的修正版2014-01-10的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 笔记 -凸函数 /KL距离
- 下一篇: idea快捷键汇总mac_Intelli