这是一份优秀的类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等。
Remon提供的串口类网址为:http://codeguru.earthweb.com/network/serialport.shtml
由于已经运行十几年了,原文的问答部分列出来这么多年来的问题,经过网友们的总结,补充和修改原来代码后,整理出一份相对比较完美的代码。

此外还附带一份小项目的源代码,它超越了串口助手,给人一种耳目一新的感觉。亮点如下:

1. 它解决了串口关闭时出现死锁不响应问题,可以直接用到开发的项目上。
2. 并且是扩展了的串口助手,具有通信协议编辑和使用功能,
3. 软件升级检测,值得做软件升级例子使用。
4. 最重要的是源代码很不错,很值得大家去研究。

分享给需要的朋友们,有问题,可以在留言,以便做进一步修正。

VC串口修正类应用的小项目下载地址:http://download.csdn.net/detail/liquanhai/3763088

2014-01-11补充说明:

最新的CSerialPort类的下载地址在:https://github.com/liquanhai/CSerialPort

头文件CSerialPort.h如下:

  1. /*
  2. **  FILENAME            CSerialPort.h
  3. **
  4. **  PURPOSE             This class can read, write and watch one serial port.
  5. **                      It sends messages to its owner when something happends on the port
  6. **                      The class creates a thread for reading and writing so the main
  7. **                      program is not blocked.
  8. **
  9. **  CREATION DATE       15-09-1997
  10. **  LAST MODIFICATION   12-11-1997
  11. **
  12. **  AUTHOR              Remon Spekreijse
  13. **
  14. **
  15. ************************************************************************************
  16. **  author: mrlong date:2007-12-25
  17. **
  18. **  改进
  19. **    1) 增加ClosePort
  20. **    2) 增加 writetoProt() 两个方法
  21. **    3) 增加 SendData 与 RecvData 方法
  22. **************************************************************************************
  23. ***************************************************************************************
  24. **  author:liquanhai date:2011-11-04
  25. **  改进
  26. **    1)增加 ClosePort中交出控制权,防止死锁问题
  27. **    2) 增加 ReceiveChar中防止线程死锁
  28. */
  29. #ifndef __SERIALPORT_H__
  30. #define __SERIALPORT_H__
  31. #define WM_COMM_BREAK_DETECTED      WM_USER+1   // A break was detected on input.
  32. #define WM_COMM_CTS_DETECTED        WM_USER+2   // The CTS (clear-to-send) signal changed state.
  33. #define WM_COMM_DSR_DETECTED        WM_USER+3   // The DSR (data-set-ready) signal changed state.
  34. #define WM_COMM_ERR_DETECTED        WM_USER+4   // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
  35. #define WM_COMM_RING_DETECTED       WM_USER+5   // A ring indicator was detected.
  36. #define WM_COMM_RLSD_DETECTED       WM_USER+6   // The RLSD (receive-line-signal-detect) signal changed state.
  37. #define WM_COMM_RXCHAR              WM_USER+7   // A character was received and placed in the input buffer.
  38. #define WM_COMM_RXFLAG_DETECTED     WM_USER+8   // The event character was received and placed in the input buffer.
  39. #define WM_COMM_TXEMPTY_DETECTED    WM_USER+9   // The last character in the output buffer was sent.
  40. class CSerialPort
  41. {
  42. public:
  43. // contruction and destruction
  44. CSerialPort();
  45. virtual     ~CSerialPort();
  46. // port initialisation
  47. BOOL        InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200,
  48. char parity = 'N', UINT databits = 8, UINT stopsbits = 1,
  49. DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,
  50. DWORD ReadIntervalTimeout = 1000,
  51. DWORD ReadTotalTimeoutMultiplier = 1000,
  52. DWORD ReadTotalTimeoutConstant = 1000,
  53. DWORD WriteTotalTimeoutMultiplier = 1000,
  54. DWORD WriteTotalTimeoutConstant = 1000);
  55. // start/stop comm watching
  56. BOOL        StartMonitoring();
  57. BOOL        RestartMonitoring();
  58. BOOL        StopMonitoring();
  59. DWORD       GetWriteBufferSize();
  60. DWORD       GetCommEvents();
  61. DCB         GetDCB();
  62. void        WriteToPort(char* string);
  63. void        WriteToPort(char* string,int n); // add by mrlong 2007-12-25
  64. void        WriteToPort(LPCTSTR string);     // add by mrlong 2007-12-25
  65. void        WriteToPort(BYTE* Buffer, int n);// add by mrlong
  66. void        ClosePort();                     // add by mrlong 2007-12-2
  67. void SendData(LPCTSTR lpszData, const int nLength);   //串口发送函数 by mrlong 2008-2-15
  68. BOOL RecvData(LPTSTR lpszData, const int nSize);      //串口接收函数 by mrlong 2008-2-15
  69. protected:
  70. // protected memberfunctions
  71. void        ProcessErrorMessage(char* ErrorText);
  72. static UINT CommThread(LPVOID pParam);
  73. static void ReceiveChar(CSerialPort* port, COMSTAT comstat);
  74. static void WriteChar(CSerialPort* port);
  75. // thread
  76. CWinThread*         m_Thread;
  77. // synchronisation objects
  78. CRITICAL_SECTION    m_csCommunicationSync;
  79. BOOL                m_bThreadAlive;
  80. // handles
  81. HANDLE              m_hShutdownEvent;  //stop发生的事件
  82. HANDLE              m_hComm;           // read
  83. HANDLE              m_hWriteEvent;     // write
  84. // Event array.
  85. // One element is used for each event. There are two event handles for each port.
  86. // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).
  87. // There is a general shutdown when the port is closed.
  88. HANDLE              m_hEventArray[3];
  89. // structures
  90. OVERLAPPED          m_ov;
  91. COMMTIMEOUTS        m_CommTimeouts;
  92. DCB                 m_dcb;
  93. // owner window
  94. CWnd*               m_pOwner;
  95. // misc
  96. UINT                m_nPortNr;        //?????
  97. char*               m_szWriteBuffer;
  98. DWORD               m_dwCommEvents;
  99. DWORD               m_nWriteBufferSize;
  100. int                 m_nWriteSize; //add by mrlong 2007-12-25
  101. };
  102. #endif __SERIALPORT_H__
  1. /*
  2. ** FILENAME CSerialPort.h
  3. **
  4. ** PURPOSE This class can read, write and watch one serial port.
  5. ** It sends messages to its owner when something happends on the port
  6. ** The class creates a thread for reading and writing so the main
  7. ** program is not blocked.
  8. **
  9. ** CREATION DATE 15-09-1997
  10. ** LAST MODIFICATION 12-11-1997
  11. **
  12. ** AUTHOR Remon Spekreijse
  13. **
  14. **
  15. ************************************************************************************
  16. ** author: mrlong date:2007-12-25
  17. **
  18. ** 改进
  19. ** 1) 增加ClosePort
  20. ** 2) 增加 writetoProt() 两个方法
  21. ** 3) 增加 SendData 与 RecvData 方法
  22. **************************************************************************************
  23. ***************************************************************************************
  24. ** author:liquanhai date:2011-11-04
  25. ** 改进
  26. ** 1)增加 ClosePort中交出控制权,防止死锁问题
  27. ** 2) 增加 ReceiveChar中防止线程死锁
  28.  */
  29.  
  30. #ifndef __SERIALPORT_H__
  31. #define __SERIALPORT_H__
  32.  
  33. #define WM_COMM_BREAK_DETECTED WM_USER+1 // A break was detected on input.
  34. #define WM_COMM_CTS_DETECTED WM_USER+2 // The CTS (clear-to-send) signal changed state.
  35. #define WM_COMM_DSR_DETECTED WM_USER+3 // The DSR (data-set-ready) signal changed state.
  36. #define WM_COMM_ERR_DETECTED WM_USER+4 // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
  37. #define WM_COMM_RING_DETECTED WM_USER+5 // A ring indicator was detected.
  38. #define WM_COMM_RLSD_DETECTED WM_USER+6 // The RLSD (receive-line-signal-detect) signal changed state.
  39. #define WM_COMM_RXCHAR WM_USER+7 // A character was received and placed in the input buffer.
  40. #define WM_COMM_RXFLAG_DETECTED WM_USER+8 // The event character was received and placed in the input buffer.
  41. #define WM_COMM_TXEMPTY_DETECTED WM_USER+9 // The last character in the output buffer was sent.
  42.  
  43. class CSerialPort
  44. {
  45. public:
  46. // contruction and destruction
  47. CSerialPort();
  48. virtual ~CSerialPort();
  49.  
  50. // port initialisation
  51. BOOL InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200,
  52. char parity = 'N', UINT databits = 8, UINT stopsbits = 1,
  53. DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,
  54.  
  55. DWORD ReadIntervalTimeout = 1000,
  56. DWORD ReadTotalTimeoutMultiplier = 1000,
  57. DWORD ReadTotalTimeoutConstant = 1000,
  58. DWORD WriteTotalTimeoutMultiplier = 1000,
  59. DWORD WriteTotalTimeoutConstant = 1000);
  60.  
  61. // start/stop comm watching
  62. BOOL StartMonitoring();
  63. BOOL RestartMonitoring();
  64. BOOL StopMonitoring();
  65.  
  66. DWORD GetWriteBufferSize();
  67. DWORD GetCommEvents();
  68. DCB GetDCB();
  69.  
  70. void WriteToPort(char* string);
  71. void WriteToPort(char* string,int n); // add by mrlong 2007-12-25
  72. void WriteToPort(LPCTSTR string); // add by mrlong 2007-12-25
  73. void WriteToPort(BYTE* Buffer, int n);// add by mrlong
  74. void ClosePort(); // add by mrlong 2007-12-2
  75.  
  76. void SendData(LPCTSTR lpszData, const int nLength); //串口发送函数 by mrlong 2008-2-15
  77. BOOL RecvData(LPTSTR lpszData, const int nSize); //串口接收函数 by mrlong 2008-2-15
  78.  
  79. protected:
  80. // protected memberfunctions
  81. void ProcessErrorMessage(char* ErrorText);
  82. static UINT CommThread(LPVOID pParam);
  83. static void ReceiveChar(CSerialPort* port, COMSTAT comstat);
  84. static void WriteChar(CSerialPort* port);
  85.  
  86. // thread
  87. CWinThread* m_Thread;
  88.  
  89. // synchronisation objects
  90. CRITICAL_SECTION m_csCommunicationSync;
  91. BOOL m_bThreadAlive;
  92.  
  93. // handles
  94. HANDLE m_hShutdownEvent; //stop发生的事件
  95. HANDLE m_hComm; // read
  96. HANDLE m_hWriteEvent; // write
  97.  
  98. // Event array.
  99. // One element is used for each event. There are two event handles for each port.
  100. // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).
  101. // There is a general shutdown when the port is closed.
  102. HANDLE m_hEventArray[3];
  103.  
  104. // structures
  105. OVERLAPPED m_ov;
  106. COMMTIMEOUTS m_CommTimeouts;
  107. DCB m_dcb;
  108.  
  109. // owner window
  110. CWnd* m_pOwner;
  111.  
  112. // misc
  113. UINT m_nPortNr; //?????
  114. char* m_szWriteBuffer;
  115. DWORD m_dwCommEvents;
  116. DWORD m_nWriteBufferSize;
  117.  
  118. int m_nWriteSize; //add by mrlong 2007-12-25
  119. };
  120.  
  121. #endif __SERIALPORT_H__

源文件cpp文件如下:

  1. /*
  2. **  FILENAME            CSerialPort.cpp
  3. **
  4. **  PURPOSE             This class can read, write and watch one serial port.
  5. **                      It sends messages to its owner when something happends on the port
  6. **                      The class creates a thread for reading and writing so the main
  7. **                      program is not blocked.
  8. **
  9. **  CREATION DATE       15-09-1997
  10. **  LAST MODIFICATION   12-11-1997
  11. **
  12. **  AUTHOR              Remon Spekreijse
  13. **
  14. **
  15. */
  16. #include "stdafx.h"
  17. #include "SerialPort.h"
  18. #include <assert.h>
  19. //
  20. // Constructor
  21. //
  22. CSerialPort::CSerialPort()
  23. {
  24. m_hComm = NULL;
  25. // initialize overlapped structure members to zero
  26. m_ov.Offset = 0;
  27. m_ov.OffsetHigh = 0;
  28. // create events
  29. m_ov.hEvent = NULL;
  30. m_hWriteEvent = NULL;
  31. m_hShutdownEvent = NULL;
  32. m_szWriteBuffer = NULL;
  33. m_bThreadAlive = FALSE;
  34. m_nWriteSize = 1;
  35. }
  36. //
  37. // Delete dynamic memory
  38. //
  39. CSerialPort::~CSerialPort()
  40. {
  41. do
  42. {
  43. SetEvent(m_hShutdownEvent);
  44. } while (m_bThreadAlive);
  45. if (m_hComm != NULL)
  46. {
  47. CloseHandle(m_hComm);
  48. m_hComm = NULL;
  49. }
  50. // Close Handles
  51. if(m_hShutdownEvent!=NULL)
  52. CloseHandle( m_hShutdownEvent);
  53. if(m_ov.hEvent!=NULL)
  54. CloseHandle( m_ov.hEvent );
  55. if(m_hWriteEvent!=NULL)
  56. CloseHandle( m_hWriteEvent );
  57. TRACE("Thread ended\n");
  58. delete [] m_szWriteBuffer;
  59. }
  60. //
  61. // Initialize the port. This can be port 1 to 4.
  62. //
  63. //
  64. //parity:
  65. //  n=none
  66. //  e=even
  67. //  o=odd
  68. //  m=mark
  69. //  s=space
  70. //data:
  71. //  5,6,7,8
  72. //stop:
  73. //  1,1.5,2
  74. //
  75. BOOL CSerialPort::InitPort(CWnd* pPortOwner,    // the owner (CWnd) of the port (receives message)
  76. UINT  portnr,        // portnumber (1..4)
  77. UINT  baud,          // baudrate
  78. char  parity,        // parity
  79. UINT  databits,      // databits
  80. UINT  stopbits,      // stopbits
  81. DWORD dwCommEvents,  // EV_RXCHAR, EV_CTS etc
  82. UINT  writebuffersize,// size to the writebuffer
  83. DWORD   ReadIntervalTimeout,
  84. DWORD   ReadTotalTimeoutMultiplier,
  85. DWORD   ReadTotalTimeoutConstant,
  86. DWORD   WriteTotalTimeoutMultiplier,
  87. DWORD   WriteTotalTimeoutConstant )
  88. {
  89. assert(portnr > 0 && portnr < 5);
  90. assert(pPortOwner != NULL);
  91. // if the thread is alive: Kill
  92. if (m_bThreadAlive)
  93. {
  94. do
  95. {
  96. SetEvent(m_hShutdownEvent);
  97. } while (m_bThreadAlive);
  98. TRACE("Thread ended\n");
  99. }
  100. // create events
  101. if (m_ov.hEvent != NULL)
  102. ResetEvent(m_ov.hEvent);
  103. else
  104. m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  105. if (m_hWriteEvent != NULL)
  106. ResetEvent(m_hWriteEvent);
  107. else
  108. m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  109. if (m_hShutdownEvent != NULL)
  110. ResetEvent(m_hShutdownEvent);
  111. else
  112. m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  113. // initialize the event objects
  114. m_hEventArray[0] = m_hShutdownEvent;    // highest priority
  115. m_hEventArray[1] = m_ov.hEvent;
  116. m_hEventArray[2] = m_hWriteEvent;
  117. // initialize critical section
  118. InitializeCriticalSection(&m_csCommunicationSync);
  119. // set buffersize for writing and save the owner
  120. m_pOwner = pPortOwner;
  121. if (m_szWriteBuffer != NULL)
  122. delete [] m_szWriteBuffer;
  123. m_szWriteBuffer = new char[writebuffersize];
  124. m_nPortNr = portnr;
  125. m_nWriteBufferSize = writebuffersize;
  126. m_dwCommEvents = dwCommEvents;
  127. BOOL bResult = FALSE;
  128. char *szPort = new char[50];
  129. char *szBaud = new char[50];
  130. // now it critical!
  131. EnterCriticalSection(&m_csCommunicationSync);
  132. // if the port is already opened: close it
  133. if (m_hComm != NULL)
  134. {
  135. CloseHandle(m_hComm);
  136. m_hComm = NULL;
  137. }
  138. // prepare port strings
  139. sprintf(szPort, "COM%d", portnr);
  140. // stop is index 0 = 1 1=1.5 2=2
  141. int mystop;
  142. int myparity;
  143. switch(stopbits)
  144. {
  145. case 0:
  146. mystop = ONESTOPBIT;
  147. break;
  148. case 1:
  149. mystop = ONE5STOPBITS;
  150. break;
  151. case 2:
  152. mystop = TWOSTOPBITS;
  153. break;
  154. }
  155. myparity = 0;
  156. switch(parity)
  157. {
  158. case 'N':
  159. myparity = 0;
  160. break;
  161. case 'E':
  162. myparity = 1;
  163. break;
  164. case 'O':
  165. myparity = 2;
  166. break;
  167. case 'M':
  168. myparity = 3;
  169. break;
  170. case 'S':
  171. myparity = 4;
  172. break;
  173. }
  174. sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop);
  175. // get a handle to the port
  176. m_hComm = CreateFile(szPort,                        // communication port string (COMX)
  177. GENERIC_READ | GENERIC_WRITE,  // read/write types
  178. 0,                             // comm devices must be opened with exclusive access
  179. NULL,                          // no security attributes
  180. OPEN_EXISTING,                 // comm devices must use OPEN_EXISTING
  181. FILE_FLAG_OVERLAPPED,          // Async I/O
  182. 0);                            // template must be 0 for comm devices
  183. if (m_hComm == INVALID_HANDLE_VALUE)
  184. {
  185. // port not found
  186. delete [] szPort;
  187. delete [] szBaud;
  188. return FALSE;
  189. }
  190. // set the timeout values
  191. m_CommTimeouts.ReadIntervalTimeout         = ReadIntervalTimeout * 1000;
  192. m_CommTimeouts.ReadTotalTimeoutMultiplier  = ReadTotalTimeoutMultiplier * 1000;
  193. m_CommTimeouts.ReadTotalTimeoutConstant    = ReadTotalTimeoutConstant * 1000;
  194. m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
  195. m_CommTimeouts.WriteTotalTimeoutConstant   = WriteTotalTimeoutConstant * 1000;
  196. // configure
  197. if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
  198. {
  199. if (SetCommMask(m_hComm, dwCommEvents))
  200. {
  201. if (GetCommState(m_hComm, &m_dcb))
  202. {
  203. m_dcb.EvtChar = 'q';
  204. m_dcb.fRtsControl = RTS_CONTROL_ENABLE;     // set RTS bit high!
  205. m_dcb.BaudRate = baud;  // add by mrlong
  206. m_dcb.Parity   = myparity;
  207. m_dcb.ByteSize = databits;
  208. m_dcb.StopBits = mystop;
  209. //if (BuildCommDCB(szBaud, &m_dcb))
  210. //{
  211. if (SetCommState(m_hComm, &m_dcb))
  212. ; // normal operation... continue
  213. else
  214. ProcessErrorMessage("SetCommState()");
  215. //}
  216. //else
  217. //  ProcessErrorMessage("BuildCommDCB()");
  218. }
  219. else
  220. ProcessErrorMessage("GetCommState()");
  221. }
  222. else
  223. ProcessErrorMessage("SetCommMask()");
  224. }
  225. else
  226. ProcessErrorMessage("SetCommTimeouts()");
  227. delete [] szPort;
  228. delete [] szBaud;
  229. // flush the port
  230. PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  231. // release critical section
  232. LeaveCriticalSection(&m_csCommunicationSync);
  233. TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);
  234. return TRUE;
  235. }
  236. //
  237. //  The CommThread Function.
  238. //
  239. UINT CSerialPort::CommThread(LPVOID pParam)
  240. {
  241. // Cast the void pointer passed to the thread back to
  242. // a pointer of CSerialPort class
  243. CSerialPort *port = (CSerialPort*)pParam;
  244. // Set the status variable in the dialog class to
  245. // TRUE to indicate the thread is running.
  246. port->m_bThreadAlive = TRUE;
  247. // Misc. variables
  248. DWORD BytesTransfered = 0;
  249. DWORD Event = 0;
  250. DWORD CommEvent = 0;
  251. DWORD dwError = 0;
  252. COMSTAT comstat;
  253. BOOL  bResult = TRUE;
  254. // Clear comm buffers at startup
  255. if (port->m_hComm)       // check if the port is opened
  256. PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  257. // begin forever loop.  This loop will run as long as the thread is alive.
  258. for (;;)
  259. {
  260. // Make a call to WaitCommEvent().  This call will return immediatly
  261. // because our port was created as an async port (FILE_FLAG_OVERLAPPED
  262. // and an m_OverlappedStructerlapped structure specified).  This call will cause the
  263. // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to
  264. // be placed in a non-signeled state if there are no bytes available to be read,
  265. // or to a signeled state if there are bytes available.  If this event handle
  266. // is set to the non-signeled state, it will be set to signeled when a
  267. // character arrives at the port.
  268. // we do this for each port!
  269. bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
  270. if (!bResult)
  271. {
  272. // If WaitCommEvent() returns FALSE, process the last error to determin
  273. // the reason..
  274. switch (dwError = GetLastError())
  275. {
  276. case ERROR_IO_PENDING:
  277. {
  278. // This is a normal return value if there are no bytes
  279. // to read at the port.
  280. // Do nothing and continue
  281. break;
  282. }
  283. case 87:
  284. {
  285. // Under Windows NT, this value is returned for some reason.
  286. // I have not investigated why, but it is also a valid reply
  287. // Also do nothing and continue.
  288. break;
  289. }
  290. default:
  291. {
  292. // All other error codes indicate a serious error has
  293. // occured.  Process this error.
  294. port->ProcessErrorMessage("WaitCommEvent()");
  295. break;
  296. }
  297. }
  298. }
  299. else
  300. {
  301. // If WaitCommEvent() returns TRUE, check to be sure there are
  302. // actually bytes in the buffer to read.
  303. //
  304. // If you are reading more than one byte at a time from the buffer
  305. // (which this program does not do) you will have the situation occur
  306. // where the first byte to arrive will cause the WaitForMultipleObjects()
  307. // function to stop waiting.  The WaitForMultipleObjects() function
  308. // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
  309. // as it returns.
  310. //
  311. // If in the time between the reset of this event and the call to
  312. // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
  313. // to the signeled state. When the call to ReadFile() occurs, it will
  314. // read all of the bytes from the buffer, and the program will
  315. // loop back around to WaitCommEvent().
  316. //
  317. // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
  318. // but there are no bytes available to read.  If you proceed and call
  319. // ReadFile(), it will return immediatly due to the async port setup, but
  320. // GetOverlappedResults() will not return until the next character arrives.
  321. //
  322. // It is not desirable for the GetOverlappedResults() function to be in
  323. // this state.  The thread shutdown event (event 0) and the WriteFile()
  324. // event (Event2) will not work if the thread is blocked by GetOverlappedResults().
  325. //
  326. // The solution to this is to check the buffer with a call to ClearCommError().
  327. // This call will reset the event handle, and if there are no bytes to read
  328. // we can loop back through WaitCommEvent() again, then proceed.
  329. // If there are really bytes to read, do nothing and proceed.
  330. bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
  331. if (comstat.cbInQue == 0)
  332. continue;
  333. }   // end if bResult
  334. // Main wait function.  This function will normally block the thread
  335. // until one of nine events occur that require action.
  336. Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
  337. switch (Event)
  338. {
  339. case 0:
  340. {
  341. // Shutdown event.  This is event zero so it will be
  342. // the higest priority and be serviced first.
  343. port->m_bThreadAlive = FALSE;
  344. // Kill this thread.  break is not needed, but makes me feel better.
  345. AfxEndThread(100);
  346. break;
  347. }
  348. case 1: // read event
  349. {
  350. GetCommMask(port->m_hComm, &CommEvent);
  351. if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中
  352. ReceiveChar(port, comstat);
  353. if (CommEvent & EV_CTS) //CTS信号状态发生变化
  354. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  355. if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中
  356. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  357. if (CommEvent & EV_BREAK)  //输入中发生中断
  358. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  359. if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY
  360. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  361. if (CommEvent & EV_RING) //检测到振铃指示
  362. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  363. break;
  364. }
  365. case 2: // write event
  366. {
  367. // Write character event from port
  368. WriteChar(port);
  369. break;
  370. }
  371. } // end switch
  372. } // close forever loop
  373. return 0;
  374. }
  375. //
  376. // start comm watching
  377. //
  378. BOOL CSerialPort::StartMonitoring()
  379. {
  380. if (!(m_Thread = AfxBeginThread(CommThread, this)))
  381. return FALSE;
  382. TRACE("Thread started\n");
  383. return TRUE;
  384. }
  385. //
  386. // Restart the comm thread
  387. //
  388. BOOL CSerialPort::RestartMonitoring()
  389. {
  390. TRACE("Thread resumed\n");
  391. m_Thread->ResumeThread();
  392. return TRUE;
  393. }
  394. //
  395. // Suspend the comm thread
  396. //
  397. BOOL CSerialPort::StopMonitoring()
  398. {
  399. TRACE("Thread suspended\n");
  400. m_Thread->SuspendThread();
  401. return TRUE;
  402. }
  403. //
  404. // If there is a error, give the right message
  405. //
  406. void CSerialPort::ProcessErrorMessage(char* ErrorText)
  407. {
  408. char *Temp = new char[200];
  409. LPVOID lpMsgBuf;
  410. FormatMessage(
  411. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  412. NULL,
  413. GetLastError(),
  414. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  415. (LPTSTR) &lpMsgBuf,
  416. 0,
  417. NULL
  418. );
  419. sprintf(Temp, "WARNING:  %s Failed with the following error: \n%s\nPort: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr);
  420. MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);
  421. LocalFree(lpMsgBuf);
  422. delete[] Temp;
  423. }
  424. //
  425. // Write a character.
  426. //
  427. void CSerialPort::WriteChar(CSerialPort* port)
  428. {
  429. BOOL bWrite = TRUE;
  430. BOOL bResult = TRUE;
  431. DWORD BytesSent = 0;
  432. DWORD SendLen   = port->m_nWriteSize;
  433. ResetEvent(port->m_hWriteEvent);
  434. // Gain ownership of the critical section
  435. EnterCriticalSection(&port->m_csCommunicationSync);
  436. if (bWrite)
  437. {
  438. // Initailize variables
  439. port->m_ov.Offset = 0;
  440. port->m_ov.OffsetHigh = 0;
  441. // Clear buffer
  442. PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  443. bResult = WriteFile(port->m_hComm,                           // Handle to COMM Port
  444. port->m_szWriteBuffer,                   // Pointer to message buffer in calling finction
  445. SendLen,    // add by mrlong
  446. //strlen((char*)port->m_szWriteBuffer),  // Length of message to send
  447. &BytesSent,                             // Where to store the number of bytes sent
  448. &port->m_ov);                            // Overlapped structure
  449. // deal with any error codes
  450. if (!bResult)
  451. {
  452. DWORD dwError = GetLastError();
  453. switch (dwError)
  454. {
  455. case ERROR_IO_PENDING:
  456. {
  457. // continue to GetOverlappedResults()
  458. BytesSent = 0;
  459. bWrite = FALSE;
  460. break;
  461. }
  462. default:
  463. {
  464. // all other error codes
  465. port->ProcessErrorMessage("WriteFile()");
  466. }
  467. }
  468. }
  469. else
  470. {
  471. LeaveCriticalSection(&port->m_csCommunicationSync);
  472. }
  473. } // end if(bWrite)
  474. if (!bWrite)
  475. {
  476. bWrite = TRUE;
  477. bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
  478. &port->m_ov,       // Overlapped structure
  479. &BytesSent,       // Stores number of bytes sent
  480. TRUE);            // Wait flag
  481. LeaveCriticalSection(&port->m_csCommunicationSync);
  482. // deal with the error code
  483. if (!bResult)
  484. {
  485. port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");
  486. }
  487. } // end if (!bWrite)
  488. // Verify that the data size send equals what we tried to send
  489. if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/)  // add by
  490. {
  491. TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));
  492. }
  493. }
  494. //
  495. // Character received. Inform the owner
  496. //
  497. void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)
  498. {
  499. BOOL  bRead = TRUE;
  500. BOOL  bResult = TRUE;
  501. DWORD dwError = 0;
  502. DWORD BytesRead = 0;
  503. unsigned char RXBuff;
  504. for (;;)
  505. {
  506. //add by liquanhai 2011-11-06  防止死锁
  507. if(WaitForSingleObject(port->m_hShutdownEvent,0)==WAIT_OBJECT_0)
  508. return;
  509. // Gain ownership of the comm port critical section.
  510. // This process guarantees no other part of this program
  511. // is using the port object.
  512. EnterCriticalSection(&port->m_csCommunicationSync);
  513. // ClearCommError() will update the COMSTAT structure and
  514. // clear any other errors.
  515. bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
  516. LeaveCriticalSection(&port->m_csCommunicationSync);
  517. // start forever loop.  I use this type of loop because I
  518. // do not know at runtime how many loops this will have to
  519. // run. My solution is to start a forever loop and to
  520. // break out of it when I have processed all of the
  521. // data available.  Be careful with this approach and
  522. // be sure your loop will exit.
  523. // My reasons for this are not as clear in this sample
  524. // as it is in my production code, but I have found this
  525. // solutiion to be the most efficient way to do this.
  526. if (comstat.cbInQue == 0)
  527. {
  528. // break out when all bytes have been read
  529. break;
  530. }
  531. EnterCriticalSection(&port->m_csCommunicationSync);
  532. if (bRead)
  533. {
  534. bResult = ReadFile(port->m_hComm,        // Handle to COMM port
  535. &RXBuff,             // RX Buffer Pointer
  536. 1,                   // Read one byte
  537. &BytesRead,          // Stores number of bytes read
  538. &port->m_ov);     // pointer to the m_ov structure
  539. // deal with the error code
  540. if (!bResult)
  541. {
  542. switch (dwError = GetLastError())
  543. {
  544. case ERROR_IO_PENDING:
  545. {
  546. // asynchronous i/o is still in progress
  547. // Proceed on to GetOverlappedResults();
  548. bRead = FALSE;
  549. break;
  550. }
  551. default:
  552. {
  553. // Another error has occured.  Process this error.
  554. port->ProcessErrorMessage("ReadFile()");
  555. break;
  556. }
  557. }
  558. }
  559. else
  560. {
  561. // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
  562. bRead = TRUE;
  563. }
  564. }  // close if (bRead)
  565. if (!bRead)
  566. {
  567. bRead = TRUE;
  568. bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
  569. &port->m_ov,       // Overlapped structure
  570. &BytesRead,       // Stores number of bytes read
  571. TRUE);            // Wait flag
  572. // deal with the error code
  573. if (!bResult)
  574. {
  575. port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");
  576. }
  577. }  // close if (!bRead)
  578. LeaveCriticalSection(&port->m_csCommunicationSync);
  579. // notify parent that a byte was received
  580. ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
  581. } // end forever loop
  582. }
  583. //
  584. // Write a string to the port
  585. //
  586. void CSerialPort::WriteToPort(char* string)
  587. {
  588. assert(m_hComm != 0);
  589. memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  590. strcpy(m_szWriteBuffer, string);
  591. m_nWriteSize=strlen(string); // add by mrlong
  592. // set event for write
  593. SetEvent(m_hWriteEvent);
  594. }
  595. //
  596. // Return the device control block
  597. //
  598. DCB CSerialPort::GetDCB()
  599. {
  600. return m_dcb;
  601. }
  602. //
  603. // Return the communication event masks
  604. //
  605. DWORD CSerialPort::GetCommEvents()
  606. {
  607. return m_dwCommEvents;
  608. }
  609. //
  610. // Return the output buffer size
  611. //
  612. DWORD CSerialPort::GetWriteBufferSize()
  613. {
  614. return m_nWriteBufferSize;
  615. }
  616. void CSerialPort::ClosePort()
  617. {
  1. /*
  2. ** FILENAME CSerialPort.cpp
  3. **
  4. ** PURPOSE This class can read, write and watch one serial port.
  5. ** It sends messages to its owner when something happends on the port
  6. ** The class creates a thread for reading and writing so the main
  7. ** program is not blocked.
  8. **
  9. ** CREATION DATE 15-09-1997
  10. ** LAST MODIFICATION 12-11-1997
  11. **
  12. ** AUTHOR Remon Spekreijse
  13. **
  14. **
  15. */
  16.  
  17. #include "stdafx.h"
  18. #include "SerialPort.h"
  19.  
  20. #include <assert.h>
  21.  
  22. //
  23. // Constructor
  24. //
  25. CSerialPort::CSerialPort()
  26. {
  27. m_hComm = NULL;
  28.  
  29. // initialize overlapped structure members to zero
  30. m_ov.Offset = 0;
  31. m_ov.OffsetHigh = 0;
  32.  
  33. // create events
  34. m_ov.hEvent = NULL;
  35. m_hWriteEvent = NULL;
  36. m_hShutdownEvent = NULL;
  37.  
  38. m_szWriteBuffer = NULL;
  39.  
  40. m_bThreadAlive = FALSE;
  41. m_nWriteSize = 1;
  42. }
  43.  
  44. //
  45. // Delete dynamic memory
  46. //
  47. CSerialPort::~CSerialPort()
  48. {
  49. do
  50. {
  51. SetEvent(m_hShutdownEvent);
  52. } while (m_bThreadAlive);
  53.  
  54. if (m_hComm != NULL)
  55. {
  56. CloseHandle(m_hComm);
  57. m_hComm = NULL;
  58. }
  59. // Close Handles
  60. if(m_hShutdownEvent!=NULL)
  61. CloseHandle( m_hShutdownEvent);
  62. if(m_ov.hEvent!=NULL)
  63. CloseHandle( m_ov.hEvent );
  64. if(m_hWriteEvent!=NULL)
  65. CloseHandle( m_hWriteEvent );
  66.  
  67. TRACE("Thread ended\n");
  68.  
  69. delete [] m_szWriteBuffer;
  70. }
  71.  
  72. //
  73. // Initialize the port. This can be port 1 to 4.
  74. //
  75. //
  76. //parity:
  77. // n=none
  78. // e=even
  79. // o=odd
  80. // m=mark
  81. // s=space
  82. //data:
  83. // 5,6,7,8
  84. //stop:
  85. // 1,1.5,2
  86. //
  87. BOOL CSerialPort::InitPort(CWnd* pPortOwner, // the owner (CWnd) of the port (receives message)
  88. UINT portnr, // portnumber (1..4)
  89. UINT baud, // baudrate
  90. char parity, // parity
  91. UINT databits, // databits
  92. UINT stopbits, // stopbits
  93. DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc
  94. UINT writebuffersize,// size to the writebuffer
  95.  
  96. DWORD ReadIntervalTimeout,
  97. DWORD ReadTotalTimeoutMultiplier,
  98. DWORD ReadTotalTimeoutConstant,
  99. DWORD WriteTotalTimeoutMultiplier,
  100. DWORD WriteTotalTimeoutConstant )
  101.  
  102. {
  103. assert(portnr > 0 && portnr < 5);
  104. assert(pPortOwner != NULL);
  105.  
  106. // if the thread is alive: Kill
  107. if (m_bThreadAlive)
  108. {
  109. do
  110. {
  111. SetEvent(m_hShutdownEvent);
  112. } while (m_bThreadAlive);
  113. TRACE("Thread ended\n");
  114. }
  115.  
  116. // create events
  117. if (m_ov.hEvent != NULL)
  118. ResetEvent(m_ov.hEvent);
  119. else
  120. m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  121.  
  122. if (m_hWriteEvent != NULL)
  123. ResetEvent(m_hWriteEvent);
  124. else
  125. m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  126.  
  127. if (m_hShutdownEvent != NULL)
  128. ResetEvent(m_hShutdownEvent);
  129. else
  130. m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  131.  
  132. // initialize the event objects
  133. m_hEventArray[0] = m_hShutdownEvent; // highest priority
  134. m_hEventArray[1] = m_ov.hEvent;
  135. m_hEventArray[2] = m_hWriteEvent;
  136.  
  137. // initialize critical section
  138. InitializeCriticalSection(&m_csCommunicationSync);
  139.  
  140. // set buffersize for writing and save the owner
  141. m_pOwner = pPortOwner;
  142.  
  143. if (m_szWriteBuffer != NULL)
  144. delete [] m_szWriteBuffer;
  145. m_szWriteBuffer = new char[writebuffersize];
  146.  
  147. m_nPortNr = portnr;
  148.  
  149. m_nWriteBufferSize = writebuffersize;
  150. m_dwCommEvents = dwCommEvents;
  151.  
  152. BOOL bResult = FALSE;
  153. char *szPort = new char[50];
  154. char *szBaud = new char[50];
  155.  
  156. // now it critical!
  157. EnterCriticalSection(&m_csCommunicationSync);
  158.  
  159. // if the port is already opened: close it
  160. if (m_hComm != NULL)
  161. {
  162. CloseHandle(m_hComm);
  163. m_hComm = NULL;
  164. }
  165.  
  166. // prepare port strings
  167. sprintf(szPort, "COM%d", portnr);
  168. // stop is index 0 = 1 1=1.5 2=2
  169. int mystop;
  170. int myparity;
  171. switch(stopbits)
  172. {
  173. case 0:
  174. mystop = ONESTOPBIT;
  175. break;
  176. case 1:
  177. mystop = ONE5STOPBITS;
  178. break;
  179. case 2:
  180. mystop = TWOSTOPBITS;
  181. break;
  182. }
  183. myparity = 0;
  184. switch(parity)
  185. {
  186. case 'N':
  187. myparity = 0;
  188. break;
  189. case 'E':
  190. myparity = 1;
  191. break;
  192. case 'O':
  193. myparity = 2;
  194. break;
  195. case 'M':
  196. myparity = 3;
  197. break;
  198. case 'S':
  199. myparity = 4;
  200. break;
  201. }
  202. sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop);
  203.  
  204. // get a handle to the port
  205. m_hComm = CreateFile(szPort, // communication port string (COMX)
  206. GENERIC_READ | GENERIC_WRITE, // read/write types
  207. 0, // comm devices must be opened with exclusive access
  208. NULL, // no security attributes
  209. OPEN_EXISTING, // comm devices must use OPEN_EXISTING
  210. FILE_FLAG_OVERLAPPED, // Async I/O
  211. 0); // template must be 0 for comm devices
  212.  
  213. if (m_hComm == INVALID_HANDLE_VALUE)
  214. {
  215. // port not found
  216. delete [] szPort;
  217. delete [] szBaud;
  218.  
  219. return FALSE;
  220. }
  221.  
  222. // set the timeout values
  223. m_CommTimeouts.ReadIntervalTimeout = ReadIntervalTimeout * 1000;
  224. m_CommTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier * 1000;
  225. m_CommTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000;
  226. m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
  227. m_CommTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant * 1000;
  228.  
  229. // configure
  230. if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
  231. {
  232. if (SetCommMask(m_hComm, dwCommEvents))
  233. {
  234. if (GetCommState(m_hComm, &m_dcb))
  235. {
  236. m_dcb.EvtChar = 'q';
  237. m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high!
  238. m_dcb.BaudRate = baud; // add by mrlong
  239. m_dcb.Parity = myparity;
  240. m_dcb.ByteSize = databits;
  241. m_dcb.StopBits = mystop;
  242.  
  243. //if (BuildCommDCB(szBaud, &m_dcb))
  244. //{
  245. if (SetCommState(m_hComm, &m_dcb))
  246. ; // normal operation... continue
  247. else
  248. ProcessErrorMessage("SetCommState()");
  249. //}
  250. //else
  251. // ProcessErrorMessage("BuildCommDCB()");
  252. }
  253. else
  254. ProcessErrorMessage("GetCommState()");
  255. }
  256. else
  257. ProcessErrorMessage("SetCommMask()");
  258. }
  259. else
  260. ProcessErrorMessage("SetCommTimeouts()");
  261.  
  262. delete [] szPort;
  263. delete [] szBaud;
  264.  
  265. // flush the port
  266. PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  267.  
  268. // release critical section
  269. LeaveCriticalSection(&m_csCommunicationSync);
  270.  
  271. TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);
  272.  
  273. return TRUE;
  274. }
  275.  
  276. //
  277. // The CommThread Function.
  278. //
  279. UINT CSerialPort::CommThread(LPVOID pParam)
  280. {
  281. // Cast the void pointer passed to the thread back to
  282. // a pointer of CSerialPort class
  283. CSerialPort *port = (CSerialPort*)pParam;
  284.  
  285. // Set the status variable in the dialog class to
  286. // TRUE to indicate the thread is running.
  287. port->m_bThreadAlive = TRUE;
  288.  
  289. // Misc. variables
  290. DWORD BytesTransfered = 0;
  291. DWORD Event = 0;
  292. DWORD CommEvent = 0;
  293. DWORD dwError = 0;
  294. COMSTAT comstat;
  295. BOOL bResult = TRUE;
  296.  
  297. // Clear comm buffers at startup
  298. if (port->m_hComm) // check if the port is opened
  299. PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  300.  
  301. // begin forever loop. This loop will run as long as the thread is alive.
  302. for (;;)
  303. {
  304.  
  305. // Make a call to WaitCommEvent(). This call will return immediatly
  306. // because our port was created as an async port (FILE_FLAG_OVERLAPPED
  307. // and an m_OverlappedStructerlapped structure specified). This call will cause the
  308. // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to
  309. // be placed in a non-signeled state if there are no bytes available to be read,
  310. // or to a signeled state if there are bytes available. If this event handle
  311. // is set to the non-signeled state, it will be set to signeled when a
  312. // character arrives at the port.
  313.  
  314. // we do this for each port!
  315.  
  316. bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
  317.  
  318. if (!bResult)
  319. {
  320. // If WaitCommEvent() returns FALSE, process the last error to determin
  321. // the reason..
  322. switch (dwError = GetLastError())
  323. {
  324. case ERROR_IO_PENDING:
  325. {
  326. // This is a normal return value if there are no bytes
  327. // to read at the port.
  328. // Do nothing and continue
  329. break;
  330. }
  331. case 87:
  332. {
  333. // Under Windows NT, this value is returned for some reason.
  334. // I have not investigated why, but it is also a valid reply
  335. // Also do nothing and continue.
  336. break;
  337. }
  338. default:
  339. {
  340. // All other error codes indicate a serious error has
  341. // occured. Process this error.
  342. port->ProcessErrorMessage("WaitCommEvent()");
  343. break;
  344. }
  345. }
  346. }
  347. else
  348. {
  349. // If WaitCommEvent() returns TRUE, check to be sure there are
  350. // actually bytes in the buffer to read.
  351. //
  352. // If you are reading more than one byte at a time from the buffer
  353. // (which this program does not do) you will have the situation occur
  354. // where the first byte to arrive will cause the WaitForMultipleObjects()
  355. // function to stop waiting. The WaitForMultipleObjects() function
  356. // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
  357. // as it returns.
  358. //
  359. // If in the time between the reset of this event and the call to
  360. // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
  361. // to the signeled state. When the call to ReadFile() occurs, it will
  362. // read all of the bytes from the buffer, and the program will
  363. // loop back around to WaitCommEvent().
  364. //
  365. // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
  366. // but there are no bytes available to read. If you proceed and call
  367. // ReadFile(), it will return immediatly due to the async port setup, but
  368. // GetOverlappedResults() will not return until the next character arrives.
  369. //
  370. // It is not desirable for the GetOverlappedResults() function to be in
  371. // this state. The thread shutdown event (event 0) and the WriteFile()
  372. // event (Event2) will not work if the thread is blocked by GetOverlappedResults().
  373. //
  374. // The solution to this is to check the buffer with a call to ClearCommError().
  375. // This call will reset the event handle, and if there are no bytes to read
  376. // we can loop back through WaitCommEvent() again, then proceed.
  377. // If there are really bytes to read, do nothing and proceed.
  378.  
  379. bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
  380.  
  381. if (comstat.cbInQue == 0)
  382. continue;
  383. } // end if bResult
  384.  
  385. // Main wait function. This function will normally block the thread
  386. // until one of nine events occur that require action.
  387. Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
  388.  
  389. switch (Event)
  390. {
  391. case 0:
  392. {
  393. // Shutdown event. This is event zero so it will be
  394. // the higest priority and be serviced first.
  395.  
  396. port->m_bThreadAlive = FALSE;
  397.  
  398. // Kill this thread. break is not needed, but makes me feel better.
  399. AfxEndThread(100);
  400. break;
  401. }
  402. case 1: // read event
  403. {
  404. GetCommMask(port->m_hComm, &CommEvent);
  405. if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中
  406. ReceiveChar(port, comstat);
  407.  
  408. if (CommEvent & EV_CTS) //CTS信号状态发生变化
  409. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  410. if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中
  411. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  412. if (CommEvent & EV_BREAK) //输入中发生中断
  413. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  414. if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY
  415. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  416. if (CommEvent & EV_RING) //检测到振铃指示
  417. ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  418.  
  419. break;
  420. }
  421. case 2: // write event
  422. {
  423. // Write character event from port
  424. WriteChar(port);
  425. break;
  426. }
  427.  
  428. } // end switch
  429.  
  430. } // close forever loop
  431.  
  432. return 0;
  433. }
  434.  
  435. //
  436. // start comm watching
  437. //
  438. BOOL CSerialPort::StartMonitoring()
  439. {
  440. if (!(m_Thread = AfxBeginThread(CommThread, this)))
  441. return FALSE;
  442. TRACE("Thread started\n");
  443. return TRUE;
  444. }
  445.  
  446. //
  447. // Restart the comm thread
  448. //
  449. BOOL CSerialPort::RestartMonitoring()
  450. {
  451. TRACE("Thread resumed\n");
  452. m_Thread->ResumeThread();
  453. return TRUE;
  454. }
  455.  
  456. //
  457. // Suspend the comm thread
  458. //
  459. BOOL CSerialPort::StopMonitoring()
  460. {
  461. TRACE("Thread suspended\n");
  462. m_Thread->SuspendThread();
  463. return TRUE;
  464. }
  465.  
  466. //
  467. // If there is a error, give the right message
  468. //
  469. void CSerialPort::ProcessErrorMessage(char* ErrorText)
  470. {
  471. char *Temp = new char[200];
  472.  
  473. LPVOID lpMsgBuf;
  474.  
  475. FormatMessage(
  476. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  477. NULL,
  478. GetLastError(),
  479. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  480. (LPTSTR) &lpMsgBuf,
  481. 0,
  482. NULL
  483. );
  484.  
  485. sprintf(Temp, "WARNING: %s Failed with the following error: \n%s\nPort: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr);
  486. MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);
  487.  
  488. LocalFree(lpMsgBuf);
  489. delete[] Temp;
  490. }
  491.  
  492. //
  493. // Write a character.
  494. //
  495. void CSerialPort::WriteChar(CSerialPort* port)
  496. {
  497. BOOL bWrite = TRUE;
  498. BOOL bResult = TRUE;
  499.  
  500. DWORD BytesSent = 0;
  501. DWORD SendLen = port->m_nWriteSize;
  502. ResetEvent(port->m_hWriteEvent);
  503.  
  504. // Gain ownership of the critical section
  505. EnterCriticalSection(&port->m_csCommunicationSync);
  506.  
  507. if (bWrite)
  508. {
  509. // Initailize variables
  510. port->m_ov.Offset = 0;
  511. port->m_ov.OffsetHigh = 0;
  512.  
  513. // Clear buffer
  514. PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  515.  
  516. bResult = WriteFile(port->m_hComm, // Handle to COMM Port
  517. port->m_szWriteBuffer, // Pointer to message buffer in calling finction
  518. SendLen, // add by mrlong
  519. //strlen((char*)port->m_szWriteBuffer), // Length of message to send
  520. &BytesSent, // Where to store the number of bytes sent
  521. &port->m_ov); // Overlapped structure
  522.  
  523. // deal with any error codes
  524. if (!bResult)
  525. {
  526. DWORD dwError = GetLastError();
  527. switch (dwError)
  528. {
  529. case ERROR_IO_PENDING:
  530. {
  531. // continue to GetOverlappedResults()
  532. BytesSent = 0;
  533. bWrite = FALSE;
  534. break;
  535. }
  536. default:
  537. {
  538. // all other error codes
  539. port->ProcessErrorMessage("WriteFile()");
  540. }
  541. }
  542. }
  543. else
  544. {
  545. LeaveCriticalSection(&port->m_csCommunicationSync);
  546. }
  547. } // end if(bWrite)
  548.  
  549. if (!bWrite)
  550. {
  551. bWrite = TRUE;
  552.  
  553. bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
  554. &port->m_ov, // Overlapped structure
  555. &BytesSent, // Stores number of bytes sent
  556. TRUE); // Wait flag
  557.  
  558. LeaveCriticalSection(&port->m_csCommunicationSync);
  559.  
  560. // deal with the error code
  561. if (!bResult)
  562. {
  563. port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");
  564. }
  565. } // end if (!bWrite)
  566.  
  567. // Verify that the data size send equals what we tried to send
  568. if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/) // add by
  569. {
  570. TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));
  571. }
  572. }
  573.  
  574. //
  575. // Character received. Inform the owner
  576. //
  577. void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)
  578. {
  579. BOOL bRead = TRUE;
  580. BOOL bResult = TRUE;
  581. DWORD dwError = 0;
  582. DWORD BytesRead = 0;
  583. unsigned char RXBuff;
  584.  
  585. for (;;)
  586. {
  587.        //add by liquanhai 2011-11-06 防止死锁
  588.       if(WaitForSingleObject(port->m_hShutdownEvent,0)==WAIT_OBJECT_0)
  589.              return;
  590.  
  591.  // Gain ownership of the comm port critical section.
  592. // This process guarantees no other part of this program
  593. // is using the port object.
  594.  
  595. EnterCriticalSection(&port->m_csCommunicationSync);
  596.  
  597. // ClearCommError() will update the COMSTAT structure and
  598. // clear any other errors.
  599.  
  600. bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
  601.  
  602. LeaveCriticalSection(&port->m_csCommunicationSync);
  603.  
  604. // start forever loop. I use this type of loop because I
  605. // do not know at runtime how many loops this will have to
  606. // run. My solution is to start a forever loop and to
  607. // break out of it when I have processed all of the
  608. // data available. Be careful with this approach and
  609. // be sure your loop will exit.
  610. // My reasons for this are not as clear in this sample
  611. // as it is in my production code, but I have found this
  612. // solutiion to be the most efficient way to do this.
  613.  
  614. if (comstat.cbInQue == 0)
  615. {
  616. // break out when all bytes have been read
  617. break;
  618. }
  619.  
  620. EnterCriticalSection(&port->m_csCommunicationSync);
  621.  
  622. if (bRead)
  623. {
  624. bResult = ReadFile(port->m_hComm, // Handle to COMM port
  625. &RXBuff, // RX Buffer Pointer
  626. 1, // Read one byte
  627. &BytesRead, // Stores number of bytes read
  628. &port->m_ov); // pointer to the m_ov structure
  629. // deal with the error code
  630. if (!bResult)
  631. {
  632. switch (dwError = GetLastError())
  633. {
  634. case ERROR_IO_PENDING:
  635. {
  636. // asynchronous i/o is still in progress
  637. // Proceed on to GetOverlappedResults();
  638. bRead = FALSE;
  639. break;
  640. }
  641. default:
  642. {
  643. // Another error has occured. Process this error.
  644. port->ProcessErrorMessage("ReadFile()");
  645. break;
  646. }
  647. }
  648. }
  649. else
  650. {
  651. // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
  652. bRead = TRUE;
  653. }
  654. } // close if (bRead)
  655.  
  656. if (!bRead)
  657. {
  658. bRead = TRUE;
  659. bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
  660. &port->m_ov, // Overlapped structure
  661. &BytesRead, // Stores number of bytes read
  662. TRUE); // Wait flag
  663.  
  664. // deal with the error code
  665. if (!bResult)
  666. {
  667. port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");
  668. }
  669. } // close if (!bRead)
  670.  
  671. LeaveCriticalSection(&port->m_csCommunicationSync);
  672.  
  673. // notify parent that a byte was received
  674. ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
  675. } // end forever loop
  676.  
  677. }
  678.  
  679. //
  680. // Write a string to the port
  681. //
  682. void CSerialPort::WriteToPort(char* string)
  683. {
  684. assert(m_hComm != 0);
  685.  
  686. memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  687. strcpy(m_szWriteBuffer, string);
  688. m_nWriteSize=strlen(string); // add by mrlong
  689. // set event for write
  690. SetEvent(m_hWriteEvent);
  691. }
  692.  
  693. //
  694. // Return the device control block
  695. //
  696. DCB CSerialPort::GetDCB()
  697. {
  698. return m_dcb;
  699. }
  700.  
  701. //
  702. // Return the communication event masks
  703. //
  704. DWORD CSerialPort::GetCommEvents()
  705. {
  706. return m_dwCommEvents;
  707. }
  708.  
  709. //
  710. // Return the output buffer size
  711. //
  712. DWORD CSerialPort::GetWriteBufferSize()
  713. {
  714. return m_nWriteBufferSize;
  715. }
  716.  
  717. void CSerialPort::ClosePort()
  718. {
  1. MSG message;
  2. do
  3. {
  4. SetEvent(m_hShutdownEvent);
  5. if(::PeekMessage(&message,m_pOwner->m_hWnd,0,0,PM_REMOVE))
  6. {
  7. ::TranslateMessage(&message);
  8. ::DispatchMessage(&message);
  9. }
  10. } while (m_bThreadAlive);
  11. // if the port is still opened: close it
  12. if (m_hComm != NULL)
  13. {
  14. CloseHandle(m_hComm);
  15. m_hComm = NULL;
  16. }
  17. // Close Handles
  18. if(m_hShutdownEvent!=NULL)
  19. ResetEvent(m_hShutdownEvent);
  20. if(m_ov.hEvent!=NULL)
  21. ResetEvent(m_ov.hEvent);
  22. if(m_hWriteEvent!=NULL)
  23. ResetEvent(m_hWriteEvent);
  24. //delete [] m_szWriteBuffer;
  25. }
  26. void CSerialPort::WriteToPort(char* string,int n)
  27. {
  28. assert(m_hComm != 0);
  29. memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  30. memcpy(m_szWriteBuffer, string, n);
  31. m_nWriteSize = n;
  32. // set event for write
  33. SetEvent(m_hWriteEvent);
  34. }
  35. void CSerialPort::WriteToPort(LPCTSTR string)
  36. {
  37. assert(m_hComm != 0);
  38. memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  39. strcpy(m_szWriteBuffer, string);
  40. m_nWriteSize=strlen(string);
  41. // set event for write
  42. SetEvent(m_hWriteEvent);
  43. }
  44. void CSerialPort::WriteToPort(BYTE* Buffer, int n)
  45. {
  46. assert(m_hComm != 0);
  47. memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  48. int i;
  49. for(i=0; i<n; i++)
  50. {
  51. m_szWriteBuffer[i] = Buffer[i];
  52. }
  53. m_nWriteSize=n;
  54. // set event for write
  55. SetEvent(m_hWriteEvent);
  56. }
  57. void CSerialPort::SendData(LPCTSTR lpszData, const int nLength)
  58. {
  59. assert(m_hComm != 0);
  60. memset(m_szWriteBuffer, 0, nLength);
  61. strcpy(m_szWriteBuffer, lpszData);
  62. m_nWriteSize=nLength;
  63. // set event for write
  64. SetEvent(m_hWriteEvent);
  65. }
  66. BOOL CSerialPort::RecvData(LPTSTR lpszData, const int nSize)
  67. {
  68. //
  69. //接收数据
  70. //
  71. assert(m_hComm!=0);
  72. memset(lpszData,0,nSize);
  73. DWORD mylen  = 0;
  74. DWORD mylen2 = 0;
  75. while (mylen<nSize) {
  76. if(!ReadFile(m_hComm,lpszData,nSize,&mylen2,NULL))
  77. return FALSE;
  78. mylen += mylen2;
  79. }
  80. return TRUE;
  81. }
  1. MSG message;
  2. do
  3. {
  4. SetEvent(m_hShutdownEvent);
  5.              if(::PeekMessage(&message,m_pOwner->m_hWnd,0,0,PM_REMOVE))
  6.             {
  7.              ::TranslateMessage(&message);
  8.              ::DispatchMessage(&message);
  9.              }
  10.  
  11.  } while (m_bThreadAlive);
  12.  
  13. // if the port is still opened: close it
  14. if (m_hComm != NULL)
  15. {
  16. CloseHandle(m_hComm);
  17. m_hComm = NULL;
  18. }
  19.  
  20. // Close Handles
  21. if(m_hShutdownEvent!=NULL)
  22. ResetEvent(m_hShutdownEvent);
  23. if(m_ov.hEvent!=NULL)
  24. ResetEvent(m_ov.hEvent);
  25. if(m_hWriteEvent!=NULL)
  26. ResetEvent(m_hWriteEvent);
  27.  
  28. //delete [] m_szWriteBuffer;
  29.  
  30. }
  31.  
  32. void CSerialPort::WriteToPort(char* string,int n)
  33. {
  34. assert(m_hComm != 0);
  35. memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  36. memcpy(m_szWriteBuffer, string, n);
  37. m_nWriteSize = n;
  38.  
  39. // set event for write
  40. SetEvent(m_hWriteEvent);
  41. }
  42.  
  43. void CSerialPort::WriteToPort(LPCTSTR string)
  44. {
  45. assert(m_hComm != 0);
  46. memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  47. strcpy(m_szWriteBuffer, string);
  48. m_nWriteSize=strlen(string);
  49. // set event for write
  50. SetEvent(m_hWriteEvent);
  51. }
  52.  
  53. void CSerialPort::WriteToPort(BYTE* Buffer, int n)
  54. {
  55. assert(m_hComm != 0);
  56. memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  57. int i;
  58. for(i=0; i<n; i++)
  59. {
  60. m_szWriteBuffer[i] = Buffer[i];
  61. }
  62. m_nWriteSize=n;
  63.  
  64. // set event for write
  65. SetEvent(m_hWriteEvent);
  66. }
  67.  
  68. void CSerialPort::SendData(LPCTSTR lpszData, const int nLength)
  69. {
  70. assert(m_hComm != 0);
  71. memset(m_szWriteBuffer, 0, nLength);
  72. strcpy(m_szWriteBuffer, lpszData);
  73. m_nWriteSize=nLength;
  74. // set event for write
  75. SetEvent(m_hWriteEvent);
  76. }
  77.  
  78. BOOL CSerialPort::RecvData(LPTSTR lpszData, const int nSize)
  79. {
  80. //
  81. //接收数据
  82. //
  83. assert(m_hComm!=0);
  84. memset(lpszData,0,nSize);
  85. DWORD mylen = 0;
  86. DWORD mylen2 = 0;
  87. while (mylen<nSize) {
  88. if(!ReadFile(m_hComm,lpszData,nSize,&mylen2,NULL))
  89. return FALSE;
  90. mylen += mylen2;
  91.  
  92. }
  93.  
  94. return TRUE;
  95. }

VC串口修正类应用的小项目下载地址:http://download.csdn.net/detail/liquanhai/3763088

CSerialPort串口类最新修正版(解决关闭死锁问题)2014-01-11的更多相关文章

  1. CSerialPort串口类最新修正版(解决关闭死锁问题)

    这是一份优秀的类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等.Remon提供的串口类网址为:http://codeguru.earthweb.com/network/ ...

  2. Remon Spekreijse CSerialPort串口类的修正版2014-01-10

    转自:http://m.blog.csdn.net/blog/itas109/18358297# 2014-1-16阅读691 评论0 如需转载请标明出处:http://blog.csdn.net/i ...

  3. mfc的一个串口类

    这几天一直再看串口相关知识,对于其总结为如下串口类: 头文件声明如下: #pragma once // 声明当串口接收到线程的时候调用的函数指针 // 参数: 1,接收到的数据: 2,数据长度: 3, ...

  4. 一个使用MVC3+NHibernate “增删改查” 的项目(修正版)

      前言: 谈到NHibernate大伙并不陌生,搞Java的更是清楚,Hibernate是一个目前应用的最广泛的开放源代码的对象关系映射框架,它对Java的JDBC(类似于ADO.Net)进行了非常 ...

  5. 谢欣伦 - OpenDev原创教程 - 串口类CxSerial

    这是一个精练的串口类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. 串口类CxSerial的使用如下(以某个叫做CSomeClass ...

  6. 一个由印度人编写的VC串口类

    http://www.cnblogs.com/lwngreat/p/4098374.html 软件介绍 一个由印度人编写的VC串口类(也是一种VC串口控件),他还配合这个类写了VC 串口通信方面的一些 ...

  7. RT-thread v2.1.0修正版

    RT-Thread v2.1.0是v2.0.1正式版这个系列的bug修正版.RT-Thread v2.1.0修正的主要内容包括: 这个版本经历的时间比较长,并且原定的一些目标也还未能完成(更全的POS ...

  8. <QT之Bug制造机>QT中串口类“QSerialPort”的学习笔记

    QT5中已经增加了串口类QSrialPort,可以直接调用API函数进行快速开发. 1. 获取串口信息 Dialog::Dialog(QWidget *parent) : QDialog(parent ...

  9. C#串口类封装 SuperSerialPort

    C#串口类封装 SuperSerialPort 基于SerialPort类做了简单的封装方便调用 代码 /// <summary> /// SuperSerialPort /// < ...

随机推荐

  1. HDU 4667 Building Fence(2013多校7 1002题 计算几何,凸包,圆和三角形)

    Building Fence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)To ...

  2. Struts2 include(包含)多个配置文件

    Struts 2自带有“包含文件”功能,包含多个Struts配置文件合并为一个单元. 单个Struts配置文件 让我们来看看一个糟糕的 Struts 2 配置示例. struts.xml <?x ...

  3. The differentiation program with abstract data

    #!r6rs ( import ( rnrs base ( 6 ) )          ( rnrs io simple ( 6 ) ) ) ( define ( deriv exp var )   ...

  4. 在 Linux 系统中安装Load Generator ,并在windows 调用

    原文地址:http://www.blogjava.net/qileilove/archive/2012/03/14/371861.html 由于公司需要测试系统的最大用户承受能力,所以需要学习使用lo ...

  5. ios修改textField的placeholder的字体颜色、大小

    textField.placeholder = @"username is in here!"; [textField setValue:[UIColor redColor] fo ...

  6. 第十五章 php时区报错 We selected the timezone 'UTC'

    Warning: phpinfo(): It is not safe to rely on the system's timezone settings. You are *required* to ...

  7. mysql DATE_FORMAT(date, format) 函数

    DATE_FORMAT(date, format) 函数用法 DATE_FORMAT(date, format) 函数根据format字符串格式化date值. 1.把字符串转为日期格式 实例: SEL ...

  8. iOS:切换视图时,反向传递数据方法一:通知

    通知方式: 1.有一个(单例)通知中心,负责管理iOS中的所有通知 2.需要获取某种通知,必须注册成为观察者(订阅) 3.不再需要取某种通知时,要取消注册. 4.你可以向通知中心发送某种通知,通知中心 ...

  9. adore-ng笔记和Linux普通用户提权

    官网:https://github.com/trimpsyw/adore-ng 安装: [root@xuegod63 ~]# unzipadore-ng-master.zip [root@xuegod ...

  10. sbusurface scattering

    http://www.iryoku.com 感觉Jorge有这个世界的全部.... 2012年那篇  那年他刚博士毕业.... 抄了他很多东西 ....抄了他这么多年..... 言归正传 对sss我之 ...