这是一份优秀的类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等。
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__
/*
** 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中防止线程死锁
 */ #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. class CSerialPort
{
public:
// contruction and destruction
CSerialPort();
virtual ~CSerialPort(); // port initialisation
BOOL InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200,
char parity = 'N', UINT databits = 8, UINT stopsbits = 1,
DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512, DWORD ReadIntervalTimeout = 1000,
DWORD ReadTotalTimeoutMultiplier = 1000,
DWORD ReadTotalTimeoutConstant = 1000,
DWORD WriteTotalTimeoutMultiplier = 1000,
DWORD WriteTotalTimeoutConstant = 1000); // start/stop comm watching
BOOL StartMonitoring();
BOOL RestartMonitoring();
BOOL StopMonitoring(); DWORD GetWriteBufferSize();
DWORD GetCommEvents();
DCB GetDCB(); void WriteToPort(char* string);
void WriteToPort(char* string,int n); // add by mrlong 2007-12-25
void WriteToPort(LPCTSTR string); // add by mrlong 2007-12-25
void WriteToPort(BYTE* Buffer, int n);// add by mrlong
void ClosePort(); // add by mrlong 2007-12-2 void SendData(LPCTSTR lpszData, const int nLength); //串口发送函数 by mrlong 2008-2-15
BOOL RecvData(LPTSTR lpszData, const int nSize); //串口接收函数 by mrlong 2008-2-15 protected:
// protected memberfunctions
void ProcessErrorMessage(char* ErrorText);
static UINT CommThread(LPVOID pParam);
static void ReceiveChar(CSerialPort* port, COMSTAT comstat);
static void WriteChar(CSerialPort* port); // thread
CWinThread* m_Thread; // synchronisation objects
CRITICAL_SECTION m_csCommunicationSync;
BOOL m_bThreadAlive; // handles
HANDLE m_hShutdownEvent; //stop发生的事件
HANDLE m_hComm; // read
HANDLE m_hWriteEvent; // write // 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.
HANDLE m_hEventArray[3]; // structures
OVERLAPPED m_ov;
COMMTIMEOUTS m_CommTimeouts;
DCB m_dcb; // owner window
CWnd* m_pOwner; // misc
UINT m_nPortNr; //?????
char* m_szWriteBuffer;
DWORD m_dwCommEvents;
DWORD m_nWriteBufferSize; int m_nWriteSize; //add by mrlong 2007-12-25
}; #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. {
/*
** 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> //
// Constructor
//
CSerialPort::CSerialPort()
{
m_hComm = NULL; // initialize overlapped structure members to zero
m_ov.Offset = 0;
m_ov.OffsetHigh = 0; // create events
m_ov.hEvent = NULL;
m_hWriteEvent = NULL;
m_hShutdownEvent = NULL; m_szWriteBuffer = NULL; m_bThreadAlive = FALSE;
m_nWriteSize = 1;
} //
// Delete dynamic memory
//
CSerialPort::~CSerialPort()
{
do
{
SetEvent(m_hShutdownEvent);
} while (m_bThreadAlive); 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 4.
//
//
//parity:
// n=none
// e=even
// o=odd
// m=mark
// s=space
//data:
// 5,6,7,8
//stop:
// 1,1.5,2
//
BOOL CSerialPort::InitPort(CWnd* pPortOwner, // the owner (CWnd) of the port (receives message)
UINT portnr, // portnumber (1..4)
UINT baud, // baudrate
char parity, // parity
UINT databits, // databits
UINT stopbits, // stopbits
DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc
UINT writebuffersize,// size to the writebuffer DWORD ReadIntervalTimeout,
DWORD ReadTotalTimeoutMultiplier,
DWORD ReadTotalTimeoutConstant,
DWORD WriteTotalTimeoutMultiplier,
DWORD WriteTotalTimeoutConstant ) {
assert(portnr > 0 && portnr < 5);
assert(pPortOwner != NULL); // if the thread is alive: Kill
if (m_bThreadAlive)
{
do
{
SetEvent(m_hShutdownEvent);
} while (m_bThreadAlive);
TRACE("Thread ended\n");
} // create events
if (m_ov.hEvent != NULL)
ResetEvent(m_ov.hEvent);
else
m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (m_hWriteEvent != NULL)
ResetEvent(m_hWriteEvent);
else
m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (m_hShutdownEvent != NULL)
ResetEvent(m_hShutdownEvent);
else
m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // initialize the event objects
m_hEventArray[0] = m_hShutdownEvent; // highest priority
m_hEventArray[1] = m_ov.hEvent;
m_hEventArray[2] = m_hWriteEvent; // initialize critical section
InitializeCriticalSection(&m_csCommunicationSync); // set buffersize for writing and save the owner
m_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]; // 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);
// stop is index 0 = 1 1=1.5 2=2
int mystop;
int myparity;
switch(stopbits)
{
case 0:
mystop = ONESTOPBIT;
break;
case 1:
mystop = ONE5STOPBITS;
break;
case 2:
mystop = TWOSTOPBITS;
break;
}
myparity = 0;
switch(parity)
{
case 'N':
myparity = 0;
break;
case 'E':
myparity = 1;
break;
case 'O':
myparity = 2;
break;
case 'M':
myparity = 3;
break;
case 'S':
myparity = 4;
break;
}
sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop); // get a handle to the port
m_hComm = CreateFile(szPort, // communication port string (COMX)
GENERIC_READ | GENERIC_WRITE, // read/write types
0, // comm devices must be opened with exclusive access
NULL, // no security attributes
OPEN_EXISTING, // comm devices must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // Async I/O
0); // template must be 0 for comm devices if (m_hComm == INVALID_HANDLE_VALUE)
{
// port not found
delete [] szPort;
delete [] szBaud; return FALSE;
} // set the timeout values
m_CommTimeouts.ReadIntervalTimeout = ReadIntervalTimeout * 1000;
m_CommTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier * 1000;
m_CommTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000;
m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
m_CommTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant * 1000; // configure
if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
{
if (SetCommMask(m_hComm, dwCommEvents))
{
if (GetCommState(m_hComm, &m_dcb))
{
m_dcb.EvtChar = 'q';
m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high!
m_dcb.BaudRate = baud; // add by mrlong
m_dcb.Parity = myparity;
m_dcb.ByteSize = databits;
m_dcb.StopBits = mystop; //if (BuildCommDCB(szBaud, &m_dcb))
//{
if (SetCommState(m_hComm, &m_dcb))
; // normal operation... continue
else
ProcessErrorMessage("SetCommState()");
//}
//else
// ProcessErrorMessage("BuildCommDCB()");
}
else
ProcessErrorMessage("GetCommState()");
}
else
ProcessErrorMessage("SetCommMask()");
}
else
ProcessErrorMessage("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.
//
UINT CSerialPort::CommThread(LPVOID pParam)
{
// Cast the void pointer passed to the thread back to
// a pointer of CSerialPort class
CSerialPort *port = (CSerialPort*)pParam; // Set the status variable in the dialog class to
// TRUE to indicate the thread is running.
port->m_bThreadAlive = TRUE; // Misc. variables
DWORD 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 opened
PurgeComm(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! bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov); if (!bResult)
{
// If WaitCommEvent() returns FALSE, process the last error to determin
// the reason..
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 continue
break;
}
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
{
// 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.
Event = WaitForMultipleObjects(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. port->m_bThreadAlive = FALSE; // Kill this thread. break is not needed, but makes me feel better.
AfxEndThread(100);
break;
}
case 1: // read event
{
GetCommMask(port->m_hComm, &CommEvent);
if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中
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_RXFLAG) //接收到事件字符,并置于输入缓冲区中
::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_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); break;
}
case 2: // write event
{
// Write character event from port
WriteChar(port);
break;
} } // end switch } // close forever loop return 0;
} //
// start comm watching
//
BOOL CSerialPort::StartMonitoring()
{
if (!(m_Thread = AfxBeginThread(CommThread, this)))
return FALSE;
TRACE("Thread started\n");
return TRUE;
} //
// Restart the comm thread
//
BOOL CSerialPort::RestartMonitoring()
{
TRACE("Thread resumed\n");
m_Thread->ResumeThread();
return TRUE;
} //
// Suspend the comm thread
//
BOOL CSerialPort::StopMonitoring()
{
TRACE("Thread suspended\n");
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;
} //
// Write a character.
//
void CSerialPort::WriteChar(CSerialPort* port)
{
BOOL bWrite = TRUE;
BOOL bResult = TRUE; DWORD BytesSent = 0;
DWORD SendLen = port->m_nWriteSize;
ResetEvent(port->m_hWriteEvent); // Gain ownership of the critical section
EnterCriticalSection(&port->m_csCommunicationSync); if (bWrite)
{
// Initailize variables
port->m_ov.Offset = 0;
port->m_ov.OffsetHigh = 0; // Clear buffer
PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); bResult = WriteFile(port->m_hComm, // Handle to COMM Port
port->m_szWriteBuffer, // Pointer to message buffer in calling finction
SendLen, // add by mrlong
//strlen((char*)port->m_szWriteBuffer), // Length of message to send
&BytesSent, // Where to store the number of bytes sent
&port->m_ov); // Overlapped structure // deal with any error codes
if (!bResult)
{
DWORD dwError = GetLastError();
switch (dwError)
{
case ERROR_IO_PENDING:
{
// continue to GetOverlappedResults()
BytesSent = 0;
bWrite = FALSE;
break;
}
default:
{
// all other error codes
port->ProcessErrorMessage("WriteFile()");
}
}
}
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 sent
TRUE); // Wait flag LeaveCriticalSection(&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 send
if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/) // add by
{
TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));
}
} //
// 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-06 防止死锁
      if(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. bResult = 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 read
break;
} EnterCriticalSection(&port->m_csCommunicationSync); if (bRead)
{
bResult = ReadFile(port->m_hComm, // Handle to COMM port
&RXBuff, // RX Buffer Pointer
1, // 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();
bRead = FALSE;
break;
}
default:
{
// Another error has occured. Process this error.
port->ProcessErrorMessage("ReadFile()");
break;
}
}
}
else
{
// ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
bRead = TRUE;
}
} // close if (bRead) if (!bRead)
{
bRead = TRUE;
bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
&port->m_ov, // Overlapped structure
&BytesRead, // Stores number of bytes read
TRUE); // 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); // add by mrlong
// set event for write
SetEvent(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()
{
  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. }
        MSG message;
do
{
SetEvent(m_hShutdownEvent);
             if(::PeekMessage(&message,m_pOwner->m_hWnd,0,0,PM_REMOVE))
            {
             ::TranslateMessage(&message);
             ::DispatchMessage(&message);
             }  } 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)
ResetEvent(m_hShutdownEvent);
if(m_ov.hEvent!=NULL)
ResetEvent(m_ov.hEvent);
if(m_hWriteEvent!=NULL)
ResetEvent(m_hWriteEvent); //delete [] m_szWriteBuffer; } void CSerialPort::WriteToPort(char* 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 write
SetEvent(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 write
SetEvent(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 write
SetEvent(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 write
SetEvent(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;
}

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. Codeforces Beta Round #8 A. Train and Peter KMP

    A. Train and Peter 题目连接: http://www.codeforces.com/contest/8/problem/A Description Peter likes to tr ...

  2. Codeforces Round #300 C. Tourist's Notes 水题

    C. Tourist's Notes Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/538/pr ...

  3. Swift 笔记1

    // Playground - noun: a place where people can play import Cocoa var str = "Hello, playground&q ...

  4. 分布式服务的事务如何处理?比如dubbo,服务与服务之间的事务怎么处理比较好,现在有没有开源的解决方案?

    作者:何明璐链接:http://www.zhihu.com/question/29483490/answer/98237582来源:知乎著作权归作者所有,转载请联系作者获得授权. 首先是不建议采用XA ...

  5. GCC安装UBUNTU

    在Ubuntu下安装GCC和其他一些Linux系统有点不一样. 方法一: 该方法超简单:sudo apt-get  build-depgcc 就上面这条命令就可以搞定 方法二:sudo apt-get ...

  6. 学习mfc书籍

    Visual C++ and MFC Programming http://www.math.hcmuns.edu.vn/~tatuana/C%20For%20Win/MFC/Tai%20Lieu%2 ...

  7. Android之旅十四 android中的xml文件解析

    在我们做有关android项目的时候,肯定会涉及到对xml文件的解析操作.以下给大家介绍一下xml文件的解析.包括DOM.SAX.Pull以及曾经我们用到的DOM4J和JDOM: 要解析的XML文件: ...

  8. PhD Positions opening at University of Nevada, Reno (Wireless Networking / Cognitive Radio / Wireless Security)

    PhD Positions opening at University of Nevada, RenoDept. of Computer Science and Engineering Researc ...

  9. ICLR 2013 International Conference on Learning Representations深度学习论文papers

    ICLR 2013 International Conference on Learning Representations May 02 - 04, 2013, Scottsdale, Arizon ...

  10. iOS:带主标题、副标题、图像类型的表格视图UITableView

    制作一个通讯录,包括姓名.电话.头像,将表格视图类型设置为UITableViewCellStyleSubtitle 效果图: //创建一个联系人的类,初始化数据 在视图控制器中实现表格内容的显示 #i ...