这是一份优秀的类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等。
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. 使用sklearn进行交叉验证

    模型评估方法 假如我们有一个带标签的数据集D,我们如何选择最优的模型? 衡量模型好坏的标准是看这个模型在新的数据集上面表现的如何,也就是看它的泛化误差.因为实际的数据没有标签,所以泛化误差是不可能直接 ...

  2. Java是对象引用按值传递的

    原因:JAVA核心编程1卷7版P116 Employee E1 = new Employee("Alice",...); Employee E2 = new Employee(&q ...

  3. JLink v8克隆版破解向导

    JLink v8克隆版破解向导 摘要 Jlink 4.5版本之后驱动会识别老的克隆版的JlinkV8,Jlink软件在启动时会提示为克隆版本后退出. 目前主流的破解方式主要有两种: 方法一,继续使用老 ...

  4. DLL Dynamic-Link Library Search Order

    http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586(v=vs.85).aspx A system can contain ...

  5. react+redux+generation-modation脚手架搭建一个todolist

    TodoList 1. 编写actions.js 2. 分析state 试着拆分成多个reducer 3. 了解store 4. 了解redux数据流生命周期 5. 分析容器组件和展示组件 搞清楚,数 ...

  6. Mysql配置文件my.ini详解

    以下是Mysql数据库服务器配置文件my.ini的详细配置.应用场合是InnoDB引擎,2核CPU, 32位SUSE. [client] #password = your_password port  ...

  7. system.data.sqlite.dll

    记录下最新的system.data.sqlite.dll下载地址和官网:http://system.data.sqlite.org

  8. jsp:include 动作指令 与 include 指令

    include动作指令可以在JSP页面中动态包含一个文件,这与include指令不同,前者可以动态包含一个文件,文件的内容可以是静态的文件也可以是动态的脚本,而且当包含的动态文件被修改的时候JSP引擎 ...

  9. 【mybatis】在mybatis分页查询时,主表对关联表 一对多 分页查询怎么实现

    现在有这样一个需求: 1.积分商品分页查询 2.一个积分商品会有多张商品图片在商品图片表  1:n的关系 这样在积分商品分页查询的时候,想要顺便把每个积分商品对应的商品图片信息也带出来 实现如下: 1 ...

  10. Linux的进程优先级NI和PR

    为什么要有进程优先级? 这似乎不用过多的解释,毕竟自从多任务操作系统诞生以来,进程执行占用cpu的能力就是一个必须要可以人为控制的事情.因为有的进程相对重要,而有的进程则没那么重要. 进程优先级起作用 ...