CSerialPort串口类最新修正版2016-08-02

原博客网址:http://blog.csdn.NET/itas109 

QQ技术交流群:129518033

 

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

 

CSerialPort类地址:https://github.com/itas109/CSerialPort

 

2016-08-02补充说明

CSDN下载:http://download.csdn.net/detail/itas109/9598347

 更新内容:

1、 改进IsOpen方法,m_hComm增加INVALID_HANDLE_VALUE的情况,因为CreateFile方法失败返回的是INVALID_HANDLE_VALUE,不是NULL
2、改进ClosePort方法:增加串口句柄无效的判断(防止关闭死锁);m_hWriteEvent不使用CloseHandle关闭
3、 改进CommThread、ReceiveChar、ReceiveStr和WriteChar方法中异常处理的判断,增加三种判断:串口打开失败(error code:ERROR_INVALID_HANDLE)、连接过程中非法断开(error code:ERROR_BAD_COMMAND)和拒绝访问(error code:ERROR_ACCESS_DENIED)
4、  采用安全函数sprintf_s和strcpy_s函数替换掉sprintf和strcpy
5、 改进QueryKey方法,用于查询注册表的可用串口值,可以搜索到任意的可用串口
6、改进InitPort方法,串口打开失败,增加提示信息:串口不存在(error code:ERROR_FILE_NOT_FOUND)和串口拒绝访问(error code:ERROR_ACCESS_DENIED)
7、加入viruscamp 取消对 MFC 的依赖
8、改进InitPort方法,如果上次串口是打开,再次调用InitPort方法,关闭串口需要做一定的延时,否则有几率导致ERROR_ACCESS_DENIED拒绝访问,也就是串口占用问题
9、初始化默认波特率修改为9600
10、修复一些释放的BUG
11、规范了一些错误信息,参考winerror.h --  error code definitions for the Win32 API functions
12、删除SendData和RecvData方法

 

2016-06-29补充说明:

CSDN下载:http://download.csdn.Net/detail/itas109/9563059

 更新内容:               

1、解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。 将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。

2、增加IsThreadSuspend方法,用于判断线程是否挂起。

3、改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。

4、增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收。

 

2016-06-22补充说明:

 更新内容:

1、增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。 解决ReceiveChar只能接收单个字符的问题。

 

2016-05-07补充说明:

CSDN下载:http://download.csdn.net/detail/itas109/9512828

 更新内容:

1、修复每次打开串口发送一次,当串口无应答时,需要关闭再打开或者接收完数据才能发送的问题。 解决办法:在m_hEventArray中调整m_hWriteEvent的优先级高于读的优先级。CommThread(LPVOID pParam)函数中读写的位置也调换。 参考:http://zhidao.baidu.com/link?url=RSrbPcfTZRULFFd2ziHZPBwnoXv1iCSu_Nmycb_yEw1mklT8gkoNZAkWpl3UDhk8L35DtRPo5VV5kEGpOx-Gea

2、修复停止位在头文件中定义成1导致SetCommState报错的问题,应为1对应的停止位是1.5。UINT stopsbits = ONESTOPBIT

3、switch(stopbits)和switch(parity)增加默认情况,增强程序健壮性

 

历史更新:

First Version by Remon Spekreijse on 2000-02-08
http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2483/A-communication-class-for-serial-port.htm


Second Version by mrlong on 2007-12-25
https://code.google.com/p/mycom/
* 增加 ClosePort
* 增加 WriteToPort 两个方法
* 增加 SendData 与 RecvData 方法


by liquanhai on 2011-11-04
http://blog.csdn.net/liquanhai/article/details/4955253
* 增加 ClosePort 中交出控制权,防止死锁问题


by liquanhai on 2011-11-06
http://blog.csdn.net/liquanhai/article/details/6941574
* 增加 ReceiveChar 中防止线程死锁


by viruscamp on 2013-12-04
https://github.com/viruscamp/CSerialPort
* 增加 IsOpen 判断是否打开
* 修正 InitPort 中 parity Odd Even 参数取值错误
* 修改 InitPort 中 portnr 取值范围,portnr>9 时特殊处理
* 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的
* 增加用户消息编号自定义,方法来自 CnComm


by itas109 on 2014-01-10
http://blog.csdn.net/itas109/article/details/18358297
* 解决COM10以上端口无法显示的问题 
* 扩展可选择端口,最大值MaxSerialPortNum可以自定义 
* 添加QueryKey()和Hkey2ComboBox两个方法,用于自动查询当前有效的串口号。


by liquanhai on 2014-12-18
* 增加一些处理措施,主要是对减少CPU占用率

 

by itas109 on 2016-05-06
* 修复每次打开串口发送一次,当串口无应答时,需要关闭再打开或者接收完数据才能发送的问题。
   解决办法:在m_hEventArray中调整m_hWriteEvent的优先级高于读的优先级。CommThread(LPVOID pParam)函数中读写的位置也调换。
   参考:http://zhidao.baidu.com/link?url=RSrbPcfTZRULFFd2ziHZPBwnoXv1iCSu_Nmycb_yEw1mklT8gkoNZAkWpl3UDhk8L35DtRPo5VV5kEGpOx-Gea
* 修复停止位在头文件中定义成1导致SetCommState报错的问题,应为1对应的停止位是1.5。UINT stopsbits = ONESTOPBIT
* switch(stopbits)和switch(parity)增加默认情况,增强程序健壮性


by itas109 on 2016-06-22
* 增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。
* 解决ReceiveChar只能接收单个字符的问题。

by itas109 on 2016-06-29
* 解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。
* 将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。
* 增加IsThreadSuspend方法,用于判断线程是否挂起。
* 改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。
* 增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收

 

[cpp]  view plain  copy
 
  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) 增加 WriteToPort 两个方法 
  21. **  3) 增加 SendData 与 RecvData 方法 
  22. ************************************************************************************** 
  23. *************************************************************************************** 
  24. **  author:liquanhai date:2011-11-06 
  25. ** 
  26. **  改进 
  27. **  1) 增加 ClosePort 中交出控制权,防止死锁问题 
  28. **  2) 增加 ReceiveChar 中防止线程死锁 
  29. ************************************************************************************** 
  30. *************************************************************************************** 
  31. **  author:viruscamp date:2013-12-04 
  32. ** 
  33. **  改进 
  34. **  1) 增加 IsOpen 判断是否打开 
  35. **  2) 修正 InitPort 中 parity Odd Even 参数取值错误 
  36. **  3) 修改 InitPort 中 portnr 取值范围,portnr>9 时特殊处理 
  37. **  4) 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的 
  38. **  5) 增加用户消息编号自定义,方法来自 CnComm 
  39. ***************************************************************************************  
  40. *************************************************************************************** 
  41. **  author: itas109  date:2014-01-10 
  42. **  Blog:blog.csdn.net/itas109 
  43. **  Git:https://github.com/itas109 
  44. ** 
  45. **  改进 
  46. **    1) 解决COM10以上端口无法显示的问题 
  47. **    2) 扩展可选择端口,最大值MaxSerialPortNum可以自定义 
  48. **    3) 添加QueryKey()和Hkey2ComboBox两个方法,用于自动查询当前有效的串口号。 
  49. *************************************************************************************** 
  50. **  author: liquanhai  date:2014-12-18 
  51. **   
  52. ** 增加一些处理措施,主要是对减少CPU占用率 
  53. *************************************************************************************** 
  54. **  author: itas109  date:2016-05-06 
  55. **  Blog:blog.csdn.net/itas109 
  56. **  Git:https://github.com/itas109 
  57. ** 
  58. **  改进 
  59. **    1) 修复每次打开串口发送一次,当串口无应答时,需要关闭再打开或者接收完数据才能发送的问题。 
  60. **       解决办法:在m_hEventArray中调整m_hWriteEvent的优先级高于读的优先级。CommThread(LPVOID pParam)函数中读写的位置也调换。 
  61. **       参考:http://zhidao.baidu.com/link?url=RSrbPcfTZRULFFd2ziHZPBwnoXv1iCSu_Nmycb_yEw1mklT8gkoNZAkWpl3UDhk8L35DtRPo5VV5kEGpOx-Gea 
  62. **    2) 修复停止位在头文件中定义成1导致SetCommState报错的问题,应为1对应的停止位是1.5。UINT stopsbits = ONESTOPBIT 
  63. **    3) switch(stopbits)和switch(parity)增加默认情况,增强程序健壮性 
  64. ** *************************************************************************************** 
  65. **  author: itas109  date:2016-06-22 
  66. **  Blog:blog.csdn.net/itas109 
  67. **  Git:https://github.com/itas109 
  68. ** 
  69. **  改进 
  70. **  1) 增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。 
  71. **      解决ReceiveChar只能接收单个字符的问题。 
  72. ** *************************************************************************************** 
  73. **  author: itas109  date:2016-06-29 
  74. **  Blog:blog.csdn.net/itas109 
  75. **  Git:https://github.com/itas109 
  76. ** 
  77. **  改进 
  78. **  1) 解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。 
  79. **      将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。 
  80. **  2)  增加IsThreadSuspend方法,用于判断线程是否挂起。 
  81. **  3)  改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。 
  82. **  4) 增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收 
  83. ** *************************************************************************************** 
  84. **  author: itas109  date:2016-08-02 
  85. **  Blog:blog.csdn.net/itas109 
  86. **  Git:https://github.com/itas109 
  87. **   
  88. **  改进 
  89. **  1) 改进IsOpen方法,m_hComm增加INVALID_HANDLE_VALUE的情况,因为CreateFile方法失败返回的是INVALID_HANDLE_VALUE,不是NULL 
  90. **  2) 改进ClosePort方法:增加串口句柄无效的判断(防止关闭死锁);m_hWriteEvent不使用CloseHandle关闭 
  91. **  3)  改进CommThread、ReceiveChar、ReceiveStr和WriteChar方法中异常处理的判断,增加三种判断:串口打开失败(error code:ERROR_INVALID_HANDLE)、连接过程中非法断开(error code:ERROR_BAD_COMMAND)和拒绝访问(error code:ERROR_ACCESS_DENIED) 
  92. **  4)  采用安全函数sprintf_s和strcpy_s函数替换掉sprintf和strcpy 
  93. **  5)  改进QueryKey方法,用于查询注册表的可用串口值,可以搜索到任意的可用串口 
  94. **  6) 改进InitPort方法,串口打开失败,增加提示信息:串口不存在(error code:ERROR_FILE_NOT_FOUND)和串口拒绝访问(error code:ERROR_ACCESS_DENIED) 
  95. **  7) 加入viruscamp 取消对 MFC 的依赖 
  96. **  8) 改进InitPort方法,如果上次串口是打开,再次调用InitPort方法,关闭串口需要做一定的延时,否则有几率导致ERROR_ACCESS_DENIED拒绝访问,也就是串口占用问题 
  97. **  9) 初始化默认波特率修改为9600 
  98. **  10)修复一些释放的BUG 
  99. **  11)规范了一些错误信息,参考winerror.h --  error code definitions for the Win32 API functions 
  100. **  12)删除SendData和RecvData方法 
  101. */  
  102.   
  103. #ifndef __SERIALPORT_H__  
  104. #define __SERIALPORT_H__  
  105.   
  106. #ifndef WM_COMM_MSG_BASE   
  107.     #define WM_COMM_MSG_BASE        WM_USER + 617       //!< 消息编号的基点    
  108. #endif  
  109.   
  110. #define WM_COMM_BREAK_DETECTED      WM_COMM_MSG_BASE + 1    // A break was detected on input.  
  111. #define WM_COMM_CTS_DETECTED        WM_COMM_MSG_BASE + 2    // The CTS (clear-to-send) signal changed state.   
  112. #define WM_COMM_DSR_DETECTED        WM_COMM_MSG_BASE + 3    // The DSR (data-set-ready) signal changed state.   
  113. #define WM_COMM_ERR_DETECTED        WM_COMM_MSG_BASE + 4    // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.   
  114. #define WM_COMM_RING_DETECTED       WM_COMM_MSG_BASE + 5    // A ring indicator was detected.   
  115. #define WM_COMM_RLSD_DETECTED       WM_COMM_MSG_BASE + 6    // The RLSD (receive-line-signal-detect) signal changed state.   
  116. #define WM_COMM_RXCHAR              WM_COMM_MSG_BASE + 7    // A character was received and placed in the input buffer.   
  117. #define WM_COMM_RXFLAG_DETECTED     WM_COMM_MSG_BASE + 8    // The event character was received and placed in the input buffer.    
  118. #define WM_COMM_TXEMPTY_DETECTED    WM_COMM_MSG_BASE + 9    // The last character in the output buffer was sent.    
  119. #define WM_COMM_RXSTR               WM_COMM_MSG_BASE + 10   // Receive string  
  120.   
  121. #define MaxSerialPortNum 200   ///有效的串口总个数,不是串口的号 //add by itas109 2014-01-09  
  122. #define IsReceiveString  1     //采用何种方式接收:ReceiveString 1多字符串接收(对应响应函数为WM_COMM_RXSTR),ReceiveString 0一个字符一个字符接收(对应响应函数为WM_COMM_RXCHAR)  
  123. class CSerialPort  
  124. {                                                          
  125. public:  
  126.     // contruction and destruction  
  127.     CSerialPort();  
  128.     virtual     ~CSerialPort(   );  
  129.   
  130.     // port initialisation        
  131.     // UINT stopsbits = ONESTOPBIT   stop is index 0 = 1 1=1.5 2=2   
  132.     // 切记:stopsbits = 1,不是停止位为1。  
  133.     // by itas109 20160506  
  134.     BOOL        InitPort(HWND pPortOwner, UINT portnr = 1, UINT baud = 9600,   
  135.                 char parity = 'N'UINT databits = 8, UINT stopsbits = ONESTOPBIT,   
  136.                 DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,  
  137.               
  138.                 DWORD ReadIntervalTimeout = 1000,  
  139.                 DWORD ReadTotalTimeoutMultiplier = 1000,  
  140.                 DWORD ReadTotalTimeoutConstant = 1000,  
  141.                 DWORD WriteTotalTimeoutMultiplier = 1000,  
  142.                 DWORD WriteTotalTimeoutConstant = 1000);  
  143.   
  144.     // start/stop comm watching  
  145.     ///控制串口监视线程  
  146.     BOOL         StartMonitoring();//开始监听  
  147.     BOOL         ResumeMonitoring();//恢复监听  
  148.     BOOL         SuspendMonitoring();//挂起监听  
  149.     BOOL         IsThreadSuspend(HANDLE hThread);//判断线程是否挂起 //add by itas109 2016-06-29  
  150.   
  151.     DWORD        GetWriteBufferSize();///获取写缓冲大小  
  152.     DWORD        GetCommEvents();///获取事件  
  153.     DCB          GetDCB();///获取DCB  
  154.   
  155. ///写数据到串口  
  156.     void        WriteToPort(char* string);  
  157.     void        WriteToPort(char* string,int n); // add by mrlong 2007-12-25  
  158.     void        WriteToPort(LPCTSTR string);     // add by mrlong 2007-12-25  
  159.     void        WriteToPort(LPCTSTR string,int n);//add by mrlong 2007-12-2  
  160.     void        WriteToPort(BYTE* Buffer, int n);// add by mrlong  
  161.     void        ClosePort();                     // add by mrlong 2007-12-2    
  162.     BOOL        IsOpen();  
  163.   
  164.     //void SendData(LPCTSTR lpszData, const int nLength);   //串口发送函数 by mrlong 2008-2-15  
  165.     //BOOL RecvData(LPTSTR lpszData, const int nSize);    //串口接收函数 by mrlong 2008-2-15  
  166.     void QueryKey(HKEY hKey);///查询注册表的串口号,将值存于数组中  
  167.     void Hkey2ComboBox(CComboBox& m_PortNO);///将QueryKey查询到的串口号添加到CComboBox控件中  
  168.   
  169. protected:  
  170.     // protected memberfunctions  
  171.     void        ProcessErrorMessage(char* ErrorText);///错误处理  
  172.     static DWORD WINAPI CommThread(LPVOID pParam);///线程函数  
  173.     static void ReceiveChar(CSerialPort* port);  
  174.     static void ReceiveStr(CSerialPort* port); //add by itas109 2016-06-22  
  175.     static void WriteChar(CSerialPort* port);  
  176.   
  177.     // thread  
  178.     //CWinThread*           m_Thread;  
  179.     HANDLE              m_Thread;  
  180.     BOOL                m_bIsSuspened;///thread监视线程是否挂起  
  181.   
  182.     // synchronisation objects  
  183.     CRITICAL_SECTION    m_csCommunicationSync;///临界资源  
  184.     BOOL                m_bThreadAlive;///监视线程运行标志  
  185.   
  186.     // handles  
  187.     HANDLE              m_hShutdownEvent;  //stop发生的事件  
  188.     HANDLE              m_hComm;           // 串口句柄   
  189.     HANDLE              m_hWriteEvent;   // write  
  190.   
  191.     // Event array.   
  192.     // One element is used for each event. There are two event handles for each port.  
  193.     // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).  
  194.     // There is a general shutdown when the port is closed.   
  195.     ///事件数组,包括一个写事件,接收事件,关闭事件  
  196.     ///一个元素用于一个事件。有两个事件线程处理端口。  
  197.     ///写事件和接收字符事件位于overlapped结构体(m_ov.hEvent)中  
  198.     ///当端口关闭时,有一个通用的关闭。  
  199.     HANDLE              m_hEventArray[3];  
  200.   
  201.     // structures  
  202.     OVERLAPPED          m_ov;///异步I/O  
  203.     COMMTIMEOUTS        m_CommTimeouts;///超时设置  
  204.     DCB                 m_dcb;///设备控制块  
  205.   
  206.     // owner window  
  207.     //CWnd*             m_pOwner;  
  208.     HWND                m_pOwner;  
  209.   
  210.   
  211.     // misc  
  212.     UINT                m_nPortNr;      ///串口号  
  213.     char*               m_szWriteBuffer;///写缓冲区  
  214.     DWORD               m_dwCommEvents;  
  215.     DWORD               m_nWriteBufferSize;///写缓冲大小  
  216.   
  217.     int                 m_nWriteSize;//写入字节数 //add by mrlong 2007-12-25  
  218. };  
  219.   
  220. #endif __SERIALPORT_H__  


 

[cpp]  view plain  copy
 
  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. **  2007-12-25 mrlong    https://code.google.com/p/mycom/ 
  15. **  2011-11-06 liquanhai http://blog.csdn.net/liquanhai/article/details/6941574 
  16. **  2013-12-04 viruscamp https://github.com/viruscamp 
  17. **  2014-01-10 itas109   http://blog.csdn.net/itas109 
  18. **  2014-12-18 liquanhai http://blog.csdn.net/liquanhai/article/details/6941574 
  19. **  2016-05-06 itas109   http://blog.csdn.net/itas109 
  20. **  2016-06-22 itas109   http://blog.csdn.net/itas109 
  21. **  2016-06-29 itas109   http://blog.csdn.net/itas109 
  22. **  2016-08-02 itas109   http://blog.csdn.net/itas109 
  23. */  
  24.   
  25. #include "stdafx.h"  
  26. #include "SerialPort.h"  
  27.   
  28. #include   
  29.   
  30. int m_nComArray[20];//存放活跃的串口号  
  31. //  
  32. // Constructor  
  33. //  
  34. CSerialPort::CSerialPort()  
  35. {  
  36.     m_hComm = NULL;  
  37.   
  38.     // initialize overlapped structure members to zero  
  39.     ///初始化异步结构体  
  40.     m_ov.Offset = 0;  
  41.     m_ov.OffsetHigh = 0;  
  42.   
  43.     // create events  
  44.     m_ov.hEvent = NULL;  
  45.     m_hWriteEvent = NULL;  
  46.     m_hShutdownEvent = NULL;  
  47.   
  48.     m_szWriteBuffer = NULL;  
  49.   
  50.     m_bThreadAlive = FALSE;  
  51.     m_nWriteSize = 1;  
  52.     m_bIsSuspened = FALSE;  
  53. }  
  54.   
  55. //  
  56. // Delete dynamic memory  
  57. //  
  58. CSerialPort::~CSerialPort()  
  59. {  
  60.     MSG message;  
  61.   
  62.     //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29  
  63.     if(IsThreadSuspend(m_Thread))  
  64.     {  
  65.         ResumeThread(m_Thread);  
  66.     }  
  67.   
  68.     //串口句柄无效  add by itas109 2016-07-29  
  69.     if(m_hComm == INVALID_HANDLE_VALUE)  
  70.     {  
  71.         CloseHandle(m_hComm);  
  72.         m_hComm = NULL;  
  73.         return;  
  74.     }  
  75.   
  76.     do  
  77.     {  
  78.         SetEvent(m_hShutdownEvent);  
  79.         //add by liquanhai  防止死锁  2011-11-06  
  80.         if(::PeekMessage(&message,m_pOwner,0,0,PM_REMOVE))  
  81.         {  
  82.             ::TranslateMessage(&message);  
  83.             ::DispatchMessage(&message);  
  84.         }  
  85.     } while (m_bThreadAlive);  
  86.   
  87.     // if the port is still opened: close it   
  88.     if (m_hComm != NULL)  
  89.     {  
  90.         CloseHandle(m_hComm);  
  91.         m_hComm = NULL;  
  92.     }  
  93.     // Close Handles    
  94.     if(m_hShutdownEvent!=NULL)  
  95.         CloseHandle( m_hShutdownEvent);   
  96.     if(m_ov.hEvent!=NULL)  
  97.         CloseHandle( m_ov.hEvent );   
  98.     if(m_hWriteEvent!=NULL)  
  99.         CloseHandle( m_hWriteEvent );   
  100.   
  101.     //TRACE("Thread ended\n");  
  102.   
  103.     if(m_szWriteBuffer != NULL)  
  104.     {  
  105.         delete [] m_szWriteBuffer;  
  106.         m_szWriteBuffer = NULL;  
  107.     }  
  108. }  
  109.   
  110. //  
  111. // Initialize the port. This can be port 1 to MaxSerialPortNum.  
  112. ///初始化串口。只能是1-MaxSerialPortNum  
  113. //  
  114. //parity:  
  115. //  n=none  
  116. //  e=even  
  117. //  o=odd  
  118. //  m=mark  
  119. //  s=space  
  120. //data:  
  121. //  5,6,7,8  
  122. //stop:  
  123. //  1,1.5,2   
  124. //  
  125. BOOL CSerialPort::InitPort(HWND pPortOwner, // the owner (CWnd) of the port (receives message)  
  126.     UINT  portnr,       // portnumber (1..MaxSerialPortNum)  
  127.     UINT  baud,         // baudrate  
  128.     char  parity,       // parity   
  129.     UINT  databits,     // databits   
  130.     UINT  stopbits,     // stopbits   
  131.     DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc  
  132.     UINT  writebuffersize,// size to the writebuffer  
  133.   
  134.     DWORD   ReadIntervalTimeout,  
  135.     DWORD   ReadTotalTimeoutMultiplier,  
  136.     DWORD   ReadTotalTimeoutConstant,  
  137.     DWORD   WriteTotalTimeoutMultiplier,  
  138.     DWORD   WriteTotalTimeoutConstant )   
  139.   
  140. {  
  141.     assert(portnr > 0 && portnr < MaxSerialPortNum);  
  142.     assert(pPortOwner != NULL);  
  143.   
  144.     MSG message;  
  145.   
  146.     //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29  
  147.     if(IsThreadSuspend(m_Thread))  
  148.     {  
  149.         ResumeThread(m_Thread);  
  150.     }  
  151.   
  152.     // if the thread is alive: Kill  
  153.     if (m_bThreadAlive)  
  154.     {  
  155.         do  
  156.         {  
  157.             SetEvent(m_hShutdownEvent);  
  158.             //add by liquanhai  防止死锁  2011-11-06  
  159.             if(::PeekMessage(&message,m_pOwner,0,0,PM_REMOVE))  
  160.             {  
  161.                 ::TranslateMessage(&message);  
  162.                 ::DispatchMessage(&message);  
  163.             }  
  164.         } while (m_bThreadAlive);  
  165.         //TRACE("Thread ended\n");  
  166.         //此处的延时很重要,因为如果串口开着,发送关闭指令到彻底关闭需要一定的时间,这个延时应该跟电脑的性能相关  
  167.         Sleep(50);//add by itas109 2016-08-02  
  168.     }  
  169.   
  170.     // create events  
  171.     if (m_ov.hEvent != NULL)  
  172.         ResetEvent(m_ov.hEvent);  
  173.     else  
  174.         m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
  175.   
  176.     if (m_hWriteEvent != NULL)  
  177.         ResetEvent(m_hWriteEvent);  
  178.     else  
  179.         m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
  180.   
  181.     if (m_hShutdownEvent != NULL)  
  182.         ResetEvent(m_hShutdownEvent);  
  183.     else  
  184.         m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  
  185.   
  186.     // initialize the event objects  
  187.     ///事件数组初始化,设定优先级别  
  188.     m_hEventArray[0] = m_hShutdownEvent;    // highest priority  
  189.     //为避免有些串口设备无数据输入,但一直返回读事件,使监听线程阻塞,  
  190.     //可以将读写放在两个线程中,或者修改读写事件优先级  
  191.     //修改优先级有两个方案:  
  192.     //方案一为监听线程中WaitCommEvent()后,添加如下两条语句:  
  193.     //if (WAIT_OBJECT_O == WaitForSingleObject(port->m_hWriteEvent, 0))  
  194.     //  ResetEvent(port->m_ov.hEvent);  
  195.     //方案二为初始化时即修改,即下面两条语句:  
  196.     m_hEventArray[1] = m_hWriteEvent;  
  197.     m_hEventArray[2] = m_ov.hEvent;  
  198.   
  199.   
  200.     // initialize critical section  
  201.     ///初始化临界资源  
  202.     InitializeCriticalSection(&m_csCommunicationSync);  
  203.   
  204.     // set buffersize for writing and save the owner  
  205.     m_pOwner = pPortOwner;  
  206.   
  207.     if(m_szWriteBuffer != NULL)  
  208.     {  
  209.         delete [] m_szWriteBuffer;  
  210.         m_szWriteBuffer = NULL;  
  211.     }  
  212.     m_szWriteBuffer = new char[writebuffersize];  
  213.   
  214.     m_nPortNr = portnr;  
  215.   
  216.     m_nWriteBufferSize = writebuffersize;  
  217.     m_dwCommEvents = dwCommEvents;  
  218.   
  219.     BOOL bResult = FALSE;  
  220.     char *szPort = new char[50];  
  221.     char *szBaud = new char[50];  
  222.   
  223.     /* 
  224.     多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱, 
  225.     无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每 
  226.     个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和 
  227.     LeaveCriticalSection函数。 
  228.     */  
  229.     // now it critical!  
  230.     EnterCriticalSection(&m_csCommunicationSync);  
  231.       
  232.     // if the port is already opened: close it  
  233.     ///串口已打开就关掉  
  234.     if (m_hComm != NULL)  
  235.     {  
  236.         CloseHandle(m_hComm);  
  237.         m_hComm = NULL;  
  238.     }  
  239.       
  240.     // prepare port strings  
  241.     sprintf_s(szPort,50, "\\\\.\\COM%d", portnr);///可以显示COM10以上端口//add by itas109 2014-01-09  
  242.   
  243.     // stop is index 0 = 1 1=1.5 2=2  
  244.     int mystop;  
  245.     int myparity;  
  246.     switch(stopbits)  
  247.     {  
  248.     case 0:  
  249.         mystop = ONESTOPBIT;  
  250.         break;  
  251.     case 1:  
  252.         mystop = ONE5STOPBITS;  
  253.         break;  
  254.     case 2:  
  255.         mystop = TWOSTOPBITS;  
  256.         break;  
  257.         //增加默认情况,因为stopbits=1.5时,SetCommState会报错。  
  258.         //一般的电脑串口不支持1.5停止位,这个1.5停止位似乎用在红外传输上的。  
  259.         //by itas109 20160506  
  260.     default:  
  261.         mystop = ONESTOPBIT;  
  262.         break;  
  263.     }  
  264.     myparity = 0;  
  265.     parity = toupper(parity);  
  266.     switch(parity)  
  267.     {  
  268.     case 'N':  
  269.         myparity = 0;  
  270.         break;  
  271.     case 'O':  
  272.         myparity = 1;  
  273.         break;  
  274.     case 'E':  
  275.         myparity = 2;  
  276.         break;  
  277.     case 'M':  
  278.         myparity = 3;  
  279.         break;  
  280.     case 'S':  
  281.         myparity = 4;  
  282.         break;  
  283.         //增加默认情况。  
  284.         //by itas109 20160506  
  285.     default:  
  286.         myparity = 0;  
  287.         break;  
  288.     }  
  289.     sprintf_s(szBaud,50, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop);  
  290.   
  291.     // get a handle to the port  
  292.     /* 
  293.     通信程序在CreateFile处指定串口设备及相关的操作属性,再返回一个句柄, 
  294.     该句柄将被用于后续的通信操作,并贯穿整个通信过程串口打开后,其属性 
  295.     被设置为默认值,根据具体需要,通过调用GetCommState(hComm,&&dcb)读取 
  296.     当前串口设备控制块DCB设置,修改后通过SetCommState(hComm,&&dcb)将其写 
  297.     入。运用ReadFile()与WriteFile()这两个API函数实现串口读写操作,若为异 
  298.     步通信方式,两函数中最后一个参数为指向OVERLAPPED结构的非空指针,在读 
  299.     写函数返回值为FALSE的情况下,调用GetLastError()函数,返回值为ERROR_IO_PENDING, 
  300.     表明I/O操作悬挂,即操作转入后台继续执行。此时,可以用WaitForSingleObject() 
  301.     来等待结束信号并设置最长等待时间 
  302.     */  
  303.     m_hComm = CreateFile(szPort,                        // communication port string (COMX)  
  304.         GENERIC_READ | GENERIC_WRITE,   // read/write types  
  305.         0,                              // comm devices must be opened with exclusive access  
  306.         NULL,                           // no security attributes  
  307.         OPEN_EXISTING,                  // comm devices must use OPEN_EXISTING  
  308.         FILE_FLAG_OVERLAPPED,           // Async I/O  
  309.         0);                         // template must be 0 for comm devices  
  310.   
  311.     ///创建失败  
  312.     if (m_hComm == INVALID_HANDLE_VALUE)  
  313.     {  
  314.         //add by itas109 2016-08-02  
  315.         //串口打开失败,增加提示信息  
  316.         switch(GetLastError())  
  317.         {  
  318.             //串口不存在  
  319.         case ERROR_FILE_NOT_FOUND:  
  320.             {  
  321.                 CString str;  
  322.                 str.Format("COM%d ERROR_FILE_NOT_FOUND,Error Code:%d",portnr,GetLastError());  
  323.                 AfxMessageBox(str);  
  324.                 break;  
  325.             }  
  326.             //串口拒绝访问  
  327.         case ERROR_ACCESS_DENIED:  
  328.             {  
  329.                 CString str;  
  330.                 str.Format("COM%d ERROR_ACCESS_DENIED,Error Code:%d",portnr,GetLastError());  
  331.                 AfxMessageBox(str);  
  332.                 break;  
  333.             }  
  334.         default:  
  335.             break;  
  336.         }  
  337.         // port not found  
  338.         delete [] szPort;  
  339.         delete [] szBaud;  
  340.   
  341.         return FALSE;  
  342.     }  
  343.   
  344.     // set the timeout values  
  345.     ///设置超时  
  346.     m_CommTimeouts.ReadIntervalTimeout       = ReadIntervalTimeout * 1000;  
  347.     m_CommTimeouts.ReadTotalTimeoutMultiplier  = ReadTotalTimeoutMultiplier * 1000;  
  348.     m_CommTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000;  
  349.     m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;  
  350.     m_CommTimeouts.WriteTotalTimeoutConstant   = WriteTotalTimeoutConstant * 1000;  
  351.   
  352.     // configure  
  353.     ///配置  
  354.     ///分别调用Windows API设置串口参数  
  355.     if (SetCommTimeouts(m_hComm, &m_CommTimeouts))///设置超时  
  356.     {                            
  357.         /* 
  358.         若对端口数据的响应时间要求较严格,可采用事件驱动方式。 
  359.         事件驱动方式通过设置事件通知,当所希望的事件发生时,Windows 
  360.         发出该事件已发生的通知,这与DOS环境下的中断方式很相似。Windows 
  361.         定义了9种串口通信事件,较常用的有以下三种: 
  362.         EV_RXCHAR:接收到一个字节,并放入输入缓冲区; 
  363.         EV_TXEMPTY:输出缓冲区中的最后一个字符,发送出去; 
  364.         EV_RXFLAG:接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区 
  365.         在用SetCommMask()指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事 
  366.         件的发生。SetCommMask(hComm,0)可使WaitCommEvent()中止 
  367.         */  
  368.         if (SetCommMask(m_hComm, dwCommEvents))///设置通信事件  
  369.         {  
  370.   
  371.             if (GetCommState(m_hComm, &m_dcb))///获取当前DCB参数  
  372.             {  
  373.                 m_dcb.EvtChar = 'q';  
  374.                 m_dcb.fRtsControl = RTS_CONTROL_ENABLE;     // set RTS bit high!  
  375.                 m_dcb.BaudRate = baud;  // add by mrlong  
  376.                 m_dcb.Parity   = myparity;  
  377.                 m_dcb.ByteSize = databits;  
  378.                 m_dcb.StopBits = mystop;  
  379.   
  380.                 //if (BuildCommDCB(szBaud &m_dcb))///填写DCB结构  
  381.                 //{  
  382.                 if (SetCommState(m_hComm, &m_dcb))///配置DCB  
  383.                     ; // normal operation... continue  
  384.                 else  
  385.                     ProcessErrorMessage("SetCommState()");  
  386.                 //}  
  387.                 //else  
  388.                 //  ProcessErrorMessage("BuildCommDCB()");  
  389.             }  
  390.             else  
  391.                 ProcessErrorMessage("GetCommState()");  
  392.         }  
  393.         else  
  394.             ProcessErrorMessage("SetCommMask()");  
  395.     }  
  396.     else  
  397.         ProcessErrorMessage("SetCommTimeouts()");  
  398.   
  399.     delete [] szPort;  
  400.     delete [] szBaud;  
  401.   
  402.     // flush the port  
  403.     ///终止读写并清空接收和发送  
  404.     PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
  405.   
  406.     // release critical section  
  407.     ///释放临界资源  
  408.     LeaveCriticalSection(&m_csCommunicationSync);  
  409.   
  410.     //TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);  
  411.   
  412.     return TRUE;  
  413. }  
  414.   
  415. //  
  416. //  The CommThread Function.  
  417. ///线程函数  
  418. ///监视线程的大致流程:  
  419. ///检查串口-->进入循环{WaitCommEvent(不阻塞询问)询问事件-->如果有事件来到-->到相应处理(关闭\读\写)}  
  420. //  
  421. DWORD WINAPI CSerialPort::CommThread(LPVOID pParam)  
  422. {  
  423.     // Cast the void pointer passed to the thread back to  
  424.     // a pointer of CSerialPort class  
  425.     CSerialPort *port = (CSerialPort*)pParam;  
  426.   
  427.     // Set the status variable in the dialog class to  
  428.     // TRUE to indicate the thread is running.  
  429.     ///TRUE表示线程正在运行  
  430.     port->m_bThreadAlive = TRUE;   
  431.   
  432.     // Misc. variables  
  433.     DWORD BytesTransfered = 0;   
  434.     DWORD Event = 0;  
  435.     DWORD CommEvent = 0;  
  436.     DWORD dwError = 0;  
  437.     COMSTAT comstat;  
  438.   
  439.     BOOL  bResult = TRUE;  
  440.   
  441.     // Clear comm buffers at startup  
  442.     ///开始时清除串口缓冲  
  443.     if (port->m_hComm)       // check if the port is opened  
  444.         PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
  445.   
  446.     // begin forever loop.  This loop will run as long as the thread is alive.  
  447.     ///只要线程存在就不断读取数据  
  448.     for (;;)   
  449.     {   
  450.   
  451.         // Make a call to WaitCommEvent().  This call will return immediatly  
  452.         // because our port was created as an async port (FILE_FLAG_OVERLAPPED  
  453.         // and an m_OverlappedStructerlapped structure specified).  This call will cause the   
  454.         // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to   
  455.         // be placed in a non-signeled state if there are no bytes available to be read,  
  456.         // or to a signeled state if there are bytes available.  If this event handle   
  457.         // is set to the non-signeled state, it will be set to signeled when a   
  458.         // character arrives at the port.  
  459.   
  460.         // we do this for each port!  
  461.   
  462.         /* 
  463.         WaitCommEvent函数第3个参数1pOverlapped可以是一个OVERLAPPED结构的变量指针 
  464.         ,也可以是NULL,当用NULL时,表示该函数是同步的,否则表示该函数是异步的。 
  465.         调用WaitCommEvent时,如果异步操作不能立即完成,会立即返回FALSE,系统在 
  466.         WaitCommEvent返回前将OVERLAPPED结构成员hEvent设为无信号状态,等到产生通信 
  467.         事件时,系统将其置有信号 
  468.         */  
  469.   
  470.         bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);///表示该函数是异步的  
  471.   
  472.         if (!bResult)    
  473.         {   
  474.             // If WaitCommEvent() returns FALSE, process the last error to determin  
  475.             // the reason..  
  476.             ///如果WaitCommEvent返回Error为FALSE,则查询错误信息  
  477.             switch (dwError = GetLastError())   
  478.             {   
  479.             case ERROR_IO_PENDING:  ///正常情况,没有字符可读 erroe code:997  
  480.                 {   
  481.                     // This is a normal return value if there are no bytes  
  482.                     // to read at the port.  
  483.                     // Do nothing and continue  
  484.                     break;  
  485.                 }  
  486.             case ERROR_INVALID_PARAMETER:///系统错误 erroe code:87  
  487.                 {  
  488.                     // Under Windows NT, this value is returned for some reason.  
  489.                     // I have not investigated why, but it is also a valid reply  
  490.                     // Also do nothing and continue.  
  491.                     break;  
  492.                 }  
  493.             case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5  
  494.                 {  
  495.                     port->m_hComm = INVALID_HANDLE_VALUE;  
  496.                     CString str;  
  497.                     str.Format("COM%d ERROR_ACCESS_DENIED,WaitCommEvent() Error Code:%d",port->m_nPortNr,GetLastError());  
  498.                     AfxMessageBox(str);  
  499.                     break;  
  500.                 }  
  501.             case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6  
  502.                 {  
  503.                     port->m_hComm = INVALID_HANDLE_VALUE;  
  504.                     break;  
  505.                 }  
  506.             case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22  
  507.                 {  
  508.                     port->m_hComm = INVALID_HANDLE_VALUE;  
  509.                     CString str;  
  510.                     str.Format("COM%d ERROR_BAD_COMMAND,WaitCommEvent() Error Code:%d",port->m_nPortNr,GetLastError());  
  511.                     AfxMessageBox(str);  
  512.                     break;  
  513.                 }  
  514.             default:///发生其他错误,其中有串口读写中断开串口连接的错误(错误22)  
  515.                 {  
  516.                     // All other error codes indicate a serious error has  
  517.                     //发生错误时,将串口句柄置为无效句柄  
  518.                     port->m_hComm = INVALID_HANDLE_VALUE;  
  519.                     // occured.  Process this error.  
  520.                     port->ProcessErrorMessage("WaitCommEvent()");  
  521.                     break;  
  522.                 }  
  523.             }  
  524.         }  
  525.         else    ///WaitCommEvent()能正确返回  
  526.         {  
  527.             // If WaitCommEvent() returns TRUE, check to be sure there are  
  528.             // actually bytes in the buffer to read.    
  529.             //  
  530.             // If you are reading more than one byte at a time from the buffer   
  531.             // (which this program does not do) you will have the situation occur   
  532.             // where the first byte to arrive will cause the WaitForMultipleObjects()   
  533.             // function to stop waiting.  The WaitForMultipleObjects() function   
  534.             // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state  
  535.             // as it returns.    
  536.             //  
  537.             // If in the time between the reset of this event and the call to   
  538.             // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again  
  539.             // to the signeled state. When the call to ReadFile() occurs, it will   
  540.             // read all of the bytes from the buffer, and the program will  
  541.             // loop back around to WaitCommEvent().  
  542.             //   
  543.             // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,  
  544.             // but there are no bytes available to read.  If you proceed and call  
  545.             // ReadFile(), it will return immediatly due to the async port setup, but  
  546.             // GetOverlappedResults() will not return until the next character arrives.  
  547.             //  
  548.             // It is not desirable for the GetOverlappedResults() function to be in   
  549.             // this state.  The thread shutdown event (event 0) and the WriteFile()  
  550.             // event (Event2) will not work if the thread is blocked by GetOverlappedResults().  
  551.             //  
  552.             // The solution to this is to check the buffer with a call to ClearCommError().  
  553.             // This call will reset the event handle, and if there are no bytes to read  
  554.             // we can loop back through WaitCommEvent() again, then proceed.  
  555.             // If there are really bytes to read, do nothing and proceed.  
  556.   
  557.             bResult = ClearCommError(port->m_hComm, &dwError, &comstat);  
  558.   
  559.             if (comstat.cbInQue == 0)  
  560.                 continue;  
  561.         }   // end if bResult  
  562.   
  563.         ///主等待函数,会阻塞线程  
  564.         // Main wait function.  This function will normally block the thread  
  565.         // until one of nine events occur that require action.  
  566.         ///等待3个事件:关断/读/写,有一个事件发生就返回  
  567.         Event = WaitForMultipleObjects(3, ///3个事件  
  568.             port->m_hEventArray, ///事件数组  
  569.             FALSE, ///有一个事件发生就返回  
  570.             INFINITE);///超时时间  
  571.   
  572.         switch (Event)  
  573.         {  
  574.         case 0:  
  575.             {  
  576.                 // Shutdown event.  This is event zero so it will be  
  577.                 // the higest priority and be serviced first.  
  578.                 ///关断事件,关闭串口  
  579.                 CloseHandle(port->m_hComm);  
  580.                 port->m_hComm=NULL;  
  581.                 port->m_bThreadAlive = FALSE;  
  582.   
  583.                 // Kill this thread.  break is not needed, but makes me feel better.  
  584.                 //AfxEndThread(100);  
  585.                 ::ExitThread(100);  
  586.   
  587.                 break;  
  588.             }  
  589.         case 1: // write event 发送数据  
  590.             {  
  591.                 // Write character event from port  
  592.                 WriteChar(port);  
  593.                 break;  
  594.             }  
  595.         case 2: // read event 将定义的各种消息发送出去  
  596.             {  
  597.                 GetCommMask(port->m_hComm, &CommEvent);  
  598.                 if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中  
  599.                 {  
  600.                     if (IsReceiveString == 1)  
  601.                     {  
  602.                         ReceiveStr(port);//多字符接收  
  603.                     }  
  604.                     else if (IsReceiveString == 0)  
  605.                     {  
  606.                         ReceiveChar(port);//单字符接收  
  607.                     }  
  608.                     else  
  609.                     {  
  610.                         //默认多字符接收  
  611.                         ReceiveStr(port);//多字符接收  
  612.                     }  
  613.                 }  
  614.   
  615.                 if (CommEvent & EV_CTS) //CTS信号状态发生变化  
  616.                     ::SendMessage(port->m_pOwner, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  617.                 if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中   
  618.                     ::SendMessage(port->m_pOwner, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  619.                 if (CommEvent & EV_BREAK)  //输入中发生中断  
  620.                     ::SendMessage(port->m_pOwner, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  621.                 if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY   
  622.                     ::SendMessage(port->m_pOwner, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  623.                 if (CommEvent & EV_RING) //检测到振铃指示  
  624.                     ::SendMessage(port->m_pOwner, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);  
  625.   
  626.                 break;  
  627.             }    
  628.         default:  
  629.             {  
  630.                 AfxMessageBox("接收有问题!");  
  631.                 break;  
  632.             }  
  633.   
  634.         } // end switch  
  635.   
  636.     } // close forever loop  
  637.   
  638.     return 0;  
  639. }  
  640.   
  641. //  
  642. // start comm watching  
  643. ///开启监视线程  
  644. //  
  645. BOOL CSerialPort::StartMonitoring()  
  646. {  
  647.     //if (!(m_Thread = AfxBeginThread(CommThread, this)))  
  648.     if (!(m_Thread = ::CreateThread (NULL, 0, CommThread, this, 0, NULL )))  
  649.         return FALSE;  
  650.     //TRACE("Thread started\n");  
  651.     return TRUE;  
  652. }  
  653.   
  654. //  
  655. // Restart the comm thread  
  656. ///从挂起恢复监视线程  
  657. //  
  658. BOOL CSerialPort::ResumeMonitoring()  
  659. {  
  660.     //TRACE("Thread resumed\n");  
  661.     //m_Thread->ResumeThread();  
  662.     ::ResumeThread(m_Thread);  
  663.     return TRUE;  
  664. }  
  665.   
  666. //  
  667. // Suspend the comm thread  
  668. ///挂起监视线程  
  669. //  
  670. BOOL CSerialPort::SuspendMonitoring()  
  671. {  
  672.     //TRACE("Thread suspended\n");  
  673.     //m_Thread->SuspendThread();  
  674.     ::SuspendThread(m_Thread);  
  675.     return TRUE;  
  676. }  
  677.   
  678. BOOL CSerialPort::IsThreadSuspend(HANDLE hThread)  
  679. {  
  680.     DWORD   count = SuspendThread(hThread);  
  681.     if (count == -1)  
  682.     {  
  683.         return FALSE;  
  684.     }  
  685.     ResumeThread(hThread);  
  686.     return (count != 0);  
  687. }  
  688.   
  689. //  
  690. // If there is a error, give the right message  
  691. ///如果有错误,给出提示  
  692. //  
  693. void CSerialPort::ProcessErrorMessage(char* ErrorText)  
  694. {  
  695.     char *Temp = new char[200];  
  696.   
  697.     LPVOID lpMsgBuf;  
  698.   
  699.     FormatMessage(   
  700.         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,  
  701.         NULL,  
  702.         GetLastError(),  
  703.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language  
  704.         (LPTSTR) &lpMsgBuf,  
  705.         0,  
  706.         NULL   
  707.         );  
  708.   
  709.     sprintf_s(Temp,200,"WARNING:  %s Failed with the following error: \n%s\nPort: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr);   
  710.     MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);  
  711.   
  712.     LocalFree(lpMsgBuf);  
  713.     delete[] Temp;  
  714. }  
  715.   
  716. //  
  717. // Write a character.  
  718. //  
  719. void CSerialPort::WriteChar(CSerialPort* port)  
  720. {  
  721.     BOOL bWrite = TRUE;  
  722.     BOOL bResult = TRUE;  
  723.   
  724.     DWORD BytesSent = 0;  
  725.     DWORD SendLen   = port->m_nWriteSize;  
  726.     ResetEvent(port->m_hWriteEvent);  
  727.   
  728.   
  729.     // Gain ownership of the critical section  
  730.     EnterCriticalSection(&port->m_csCommunicationSync);  
  731.   
  732.     if (bWrite)  
  733.     {  
  734.         // Initailize variables  
  735.         port->m_ov.Offset = 0;  
  736.         port->m_ov.OffsetHigh = 0;  
  737.   
  738.         // Clear buffer  
  739.         PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  
  740.   
  741.         bResult = WriteFile(port->m_hComm,                           // Handle to COMM Port  
  742.             port->m_szWriteBuffer,                   // Pointer to message buffer in calling finction  
  743.             SendLen,    // add by mrlong  
  744.             //strlen((char*)port->m_szWriteBuffer),  // Length of message to send  
  745.             &BytesSent,                             // Where to store the number of bytes sent  
  746.             &port->m_ov);                            // Overlapped structure  
  747.   
  748.         // deal with any error codes  
  749.         if (!bResult)    
  750.         {  
  751.             DWORD dwError = GetLastError();  
  752.             switch (dwError)  
  753.             {  
  754.             case ERROR_IO_PENDING:  
  755.                 {  
  756.                     // continue to GetOverlappedResults()  
  757.                     BytesSent = 0;  
  758.                     bWrite = FALSE;  
  759.                     break;  
  760.                 }  
  761.             case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5  
  762.                 {  
  763.                     port->m_hComm = INVALID_HANDLE_VALUE;  
  764.                     CString str;  
  765.                     str.Format("COM%d ERROR_ACCESS_DENIED,WriteFile() Error Code:%d",port->m_nPortNr,GetLastError());  
  766.                     AfxMessageBox(str);  
  767.                     break;  
  768.                 }  
  769.             case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6  
  770.                 {  
  771.                     port->m_hComm = INVALID_HANDLE_VALUE;  
  772.                     break;  
  773.                 }  
  774.             case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22  
  775.                 {  
  776.                     port->m_hComm = INVALID_HANDLE_VALUE;  
  777.                     CString str;  
  778.                     str.Format("COM%d ERROR_BAD_COMMAND,WriteFile() Error Code:%d",port->m_nPortNr,GetLastError());  
  779.                     AfxMessageBox(str);  
  780.                     break;  
  781.                 }  
  782.             default:  
  783.                 {  
  784.                     // all other error codes  
  785.                     port->ProcessErrorMessage("WriteFile()");  
  786.                 }  
  787.             }  
  788.         }   
  789.         else  
  790.         {  
  791.             LeaveCriticalSection(&port->m_csCommunicationSync);  
  792.         }  
  793.     } // end if(bWrite)  
  794.   
  795.     if (!bWrite)  
  796.     {  
  797.         bWrite = TRUE;  
  798.   
  799.         bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port   
  800.             &port->m_ov,     // Overlapped structure  
  801.             &BytesSent,     // Stores number of bytes sent  
  802.             TRUE);          // Wait flag  
  803.   
  804.         LeaveCriticalSection(&port->m_csCommunicationSync);  
  805.   
  806.         // deal with the error code   
  807.         if (!bResult)    
  808.         {  
  809.             port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");  
  810.         }     
  811.     } // end if (!bWrite)  
  812.   
  813.     // Verify that the data size send equals what we tried to send  
  814.     if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/)  // add by   
  815.     {  
  816.         //TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));  
  817.     }  
  818. }  
  819.   
  820. //  
  821. // Character received. Inform the owner  
  822. //  
  823. void CSerialPort::ReceiveChar(CSerialPort* port)  
  824. {  
  825.     BOOL  bRead = TRUE;   
  826.     BOOL  bResult = TRUE;  
  827.     DWORD dwError = 0;  
  828.     DWORD BytesRead = 0;  
  829.     COMSTAT comstat;  
  830.     unsigned char RXBuff;  
  831.   
  832.     for (;;)   
  833.     {   
  834.         //add by liquanhai 2011-11-06  防止死锁  
  835.         if(WaitForSingleObject(port->m_hShutdownEvent,0)==WAIT_OBJECT_0)  
  836.             return;  
  837.   
  838.         // Gain ownership of the comm port critical section.  
  839.         // This process guarantees no other part of this program   
  840.         // is using the port object.   
  841.   
  842.         EnterCriticalSection(&port->m_csCommunicationSync);  
  843.   
  844.         // ClearCommError() will update the COMSTAT structure and  
  845.         // clear any other errors.  
  846.         ///更新COMSTAT  
  847.   
  848.         bResult = ClearCommError(port->m_hComm, &dwError, &comstat);  
  849.   
  850.         LeaveCriticalSection(&port->m_csCommunicationSync);  
  851.   
  852.         // start forever loop.  I use this type of loop because I  
  853.         // do not know at runtime how many loops this will have to  
  854.         // run. My solution is to start a forever loop and to  
  855.         // break out of it when I have processed all of the  
  856.         // data available.  Be careful with this approach and  
  857.         // be sure your loop will exit.  
  858.         // My reasons for this are not as clear in this sample   
  859.         // as it is in my production code, but I have found this   
  860.         // solutiion to be the most efficient way to do this.  
  861.   
  862.         ///所有字符均被读出,中断循环  
  863.         if (comstat.cbInQue == 0)  
  864.         {  
  865.             // break out when all bytes have been read  
  866.             break;  
  867.         }  
  868.   
  869.         EnterCriticalSection(&port->m_csCommunicationSync);  
  870.   
  871.         if (bRead)  
  872.         {  
  873.             ///串口读出,读出缓冲区中字节  
  874.             bResult = ReadFile(port->m_hComm,        // Handle to COMM port   
  875.                 &RXBuff,                // RX Buffer Pointer  
  876.                 1,                  // Read one byte  
  877.                 &BytesRead,         // Stores number of bytes read  
  878.                 &port->m_ov);        // pointer to the m_ov structure  
  879.             // deal with the error code   
  880.             ///若返回错误,错误处理  
  881.             if (!bResult)    
  882.             {   
  883.                 switch (dwError = GetLastError())   
  884.                 {   
  885.                 case ERROR_IO_PENDING:    
  886.                     {   
  887.                         // asynchronous i/o is still in progress   
  888.                         // Proceed on to GetOverlappedResults();  
  889.                         ///异步IO仍在进行  
  890.                         bRead = FALSE;  
  891.                         break;  
  892.                     }  
  893.                 case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5  
  894.                     {  
  895.                         port->m_hComm = INVALID_HANDLE_VALUE;  
  896.                         CString str;  
  897.                         str.Format("COM%d ERROR_ACCESS_DENIED,ReadFile() Error Code:%d",port->m_nPortNr,GetLastError());  
  898.                         AfxMessageBox(str);  
  899.                         break;  
  900.                     }  
  901.                 case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6  
  902.                     {  
  903.                         port->m_hComm = INVALID_HANDLE_VALUE;  
  904.                         break;  
  905.                     }  
  906.                 case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22  
  907.                     {  
  908.                         port->m_hComm = INVALID_HANDLE_VALUE;  
  909.                         CString str;  
  910.                         str.Format("COM%d ERROR_BAD_COMMAND,ReadFile() Error Code:%d",port->m_nPortNr,GetLastError());  
  911.                         AfxMessageBox(str);  
  912.                         break;  
  913.                     }  
  914.                 default:  
  915.                     {  
  916.                         // Another error has occured.  Process this error.  
  917.                         port->ProcessErrorMessage("ReadFile()");  
  918.                         break;  
  919.                         //return;///防止读写数据时,串口非正常断开导致死循环一直执行。add by itas109 2014-01-09 与上面liquanhai添加防死锁的代码差不多  
  920.                     }   
  921.                 }  
  922.             }  
  923.             else///ReadFile返回TRUE  
  924.             {  
  925.                 // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()  
  926.                 bRead = TRUE;  
  927.             }  
  928.         }  // close if (bRead)  
  929.   
  930.         ///异步IO操作仍在进行,需要调用GetOverlappedResult查询  
  931.         if (!bRead)  
  932.         {  
  933.             bRead = TRUE;  
  934.             bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port   
  935.                 &port->m_ov,     // Overlapped structure  
  936.                 &BytesRead,     // Stores number of bytes read  
  937.                 TRUE);          // Wait flag  
  938.   
  939.             // deal with the error code   
  940.             if (!bResult)    
  941.             {  
  942.                 port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");  
  943.             }     
  944.         }  // close if (!bRead)  
  945.   
  946.         LeaveCriticalSection(&port->m_csCommunicationSync);  
  947.   
  948.         // notify parent that a byte was received  
  949.         //避免线程互相等待,产生死锁,使用PostMessage()代替SendMessage()  
  950.         PostMessage(port->m_pOwner, WM_COMM_RXCHAR, (WPARAM)RXBuff, (LPARAM) port->m_nPortNr);  
  951.         //::SendMessage((port->m_pOwner), WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);  
  952.     } // end forever loop  
  953.   
  954. }  
  955.   
  956. //  
  957. // str received. Inform the owner  
  958. //  
  959. void CSerialPort::ReceiveStr(CSerialPort* port)  
  960. {  
  961.     BOOL  bRead = TRUE;   
  962.     BOOL  bResult = TRUE;  
  963.     DWORD dwError = 0;  
  964.     DWORD BytesRead = 0;  
  965.     COMSTAT comstat;  
  966.   
  967.     for (;;)   
  968.     {   
  969.         //add by liquanhai 2011-11-06  防止死锁  
  970.         if(WaitForSingleObject(port->m_hShutdownEvent,0)==WAIT_OBJECT_0)  
  971.             return;  
  972.   
  973.         // Gain ownership of the comm port critical section.  
  974.         // This process guarantees no other part of this program   
  975.         // is using the port object.   
  976.   
  977.         EnterCriticalSection(&port->m_csCommunicationSync);  
  978.   
  979.         // ClearCommError() will update the COMSTAT structure and  
  980.         // clear any other errors.  
  981.         ///更新COMSTAT  
  982.   
  983.         bResult = ClearCommError(port->m_hComm, &dwError, &comstat);  
  984.   
  985.         LeaveCriticalSection(&port->m_csCommunicationSync);  
  986.   
  987.         // start forever loop.  I use this type of loop because I  
  988.         // do not know at runtime how many loops this will have to  
  989.         // run. My solution is to start a forever loop and to  
  990.         // break out of it when I have processed all of the  
  991.         // data available.  Be careful with this approach and  
  992.         // be sure your loop will exit.  
  993.         // My reasons for this are not as clear in this sample   
  994.         // as it is in my production code, but I have found this   
  995.         // solutiion to be the most efficient way to do this.  
  996.   
  997.         ///所有字符均被读出,中断循环  
  998.         if (comstat.cbInQue == 0)  
  999.         {  
  1000.             // break out when all bytes have been read  
  1001.             break;  
  1002.         }  
  1003.   
  1004.         //如果遇到'\0',那么数据会被截断,实际数据全部读取只是没有显示完全,这个时候使用memcpy才能全部获取  
  1005.         unsigned char* RXBuff = new unsigned char[comstat.cbInQue+1];  
  1006.         if(RXBuff == NULL)  
  1007.         {  
  1008.             return;  
  1009.         }  
  1010.         RXBuff[comstat.cbInQue] = '\0';//附加字符串结束符  
  1011.   
  1012.         EnterCriticalSection(&port->m_csCommunicationSync);  
  1013.   
  1014.         if (bRead)  
  1015.         {  
  1016.             ///串口读出,读出缓冲区中字节  
  1017.             bResult = ReadFile(port->m_hComm,        // Handle to COMM port   
  1018.                 RXBuff,             // RX Buffer Pointer  
  1019.                 comstat.cbInQue,                    // Read cbInQue len byte  
  1020.                 &BytesRead,         // Stores number of bytes read  
  1021.                 &port->m_ov);        // pointer to the m_ov structure  
  1022.             // deal with the error code   
  1023.             ///若返回错误,错误处理  
  1024.             if (!bResult)    
  1025.             {   
  1026.                 switch (dwError = GetLastError())   
  1027.                 {   
  1028.                 case ERROR_IO_PENDING:    
  1029.                     {   
  1030.                         // asynchronous i/o is still in progress   
  1031.                         // Proceed on to GetOverlappedResults();  
  1032.                         ///异步IO仍在进行  
  1033.                         bRead = FALSE;  
  1034.                         break;  
  1035.                     }  
  1036.                 case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5  
  1037.                     {  
  1038.                         port->m_hComm = INVALID_HANDLE_VALUE;  
  1039.                         CString str;  
  1040.                         str.Format("COM%d ERROR_ACCESS_DENIED,ReadFile() Error Code:%d",port->m_nPortNr,GetLastError());  
  1041.                         AfxMessageBox(str);  
  1042.                         break;  
  1043.                     }  
  1044.                 case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6  
  1045.                     {  
  1046.                         port->m_hComm = INVALID_HANDLE_VALUE;  
  1047.                         break;  
  1048.                     }  
  1049.                 case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22  
  1050.                     {  
  1051.                         port->m_hComm = INVALID_HANDLE_VALUE;  
  1052.                         CString str;  
  1053.                         str.Format("COM%d ERROR_BAD_COMMAND,ReadFile() Error Code:%d",port->m_nPortNr,GetLastError());  
  1054.                         AfxMessageBox(str);  
  1055.                         break;  
  1056.                     }  
  1057.                 default:  
  1058.                     {  
  1059.                         // Another error has occured.  Process this error.  
  1060.                         port->ProcessErrorMessage("ReadFile()");  
  1061.                         break;  
  1062.                         //return;///防止读写数据时,串口非正常断开导致死循环一直执行。add by itas109 2014-01-09 与上面liquanhai添加防死锁的代码差不多  
  1063.                     }   
  1064.                 }  
  1065.             }  
  1066.             else///ReadFile返回TRUE  
  1067.             {  
  1068.                 // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()  
  1069.                 bRead = TRUE;  
  1070.             }  
  1071.         }  // close if (bRead)  
  1072.   
  1073.         ///异步IO操作仍在进行,需要调用GetOverlappedResult查询  
  1074.         if (!bRead)  
  1075.         {  
  1076.             bRead = TRUE;  
  1077.             bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port   
  1078.                 &port->m_ov,     // Overlapped structure  
  1079.                 &BytesRead,     // Stores number of bytes read  
  1080.                 TRUE);          // Wait flag  
  1081.   
  1082.             // deal with the error code   
  1083.             if (!bResult)    
  1084.             {  
  1085.                 port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");  
  1086.             }     
  1087.         }  // close if (!bRead)  
  1088.   
  1089.         LeaveCriticalSection(&port->m_csCommunicationSync);  
  1090.   
  1091.         // notify parent that some byte was received  
  1092.         ::SendMessage((port->m_pOwner), WM_COMM_RXSTR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);  
  1093.         //如果只有一个串口收发数据,可以传输读取长度,因为RXBuff中可能包含'\0',ASCII为00  
  1094.         //::SendMessage((port->m_pOwner), WM_COMM_RXSTR, (WPARAM) RXBuff, BytesRead);  
  1095.   
  1096.         //释放  
  1097.         delete[] RXBuff;  
  1098.         RXBuff = NULL;  
  1099.   
  1100.     } // end forever loop  
  1101.   
  1102. }  
  1103.   
  1104. //  
  1105. // Write a string to the port  
  1106. //  
  1107. void CSerialPort::WriteToPort(char* string)  
  1108. {         
  1109.     assert(m_hComm != 0);  
  1110.   
  1111.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
  1112.     strcpy_s(m_szWriteBuffer,m_nWriteBufferSize,string);  
  1113.     m_nWriteSize=strlen(string); // add by mrlong  
  1114.     // set event for write  
  1115.     SetEvent(m_hWriteEvent);  
  1116. }  
  1117.   
  1118. //  
  1119. // Return the device control block  
  1120. //  
  1121. DCB CSerialPort::GetDCB()  
  1122. {  
  1123.     return m_dcb;  
  1124. }  
  1125.   
  1126. //  
  1127. // Return the communication event masks  
  1128. //  
  1129. DWORD CSerialPort::GetCommEvents()  
  1130. {  
  1131.     return m_dwCommEvents;  
  1132. }  
  1133.   
  1134. //  
  1135. // Return the output buffer size  
  1136. //  
  1137. DWORD CSerialPort::GetWriteBufferSize()  
  1138. {  
  1139.     return m_nWriteBufferSize;  
  1140. }  
  1141.   
  1142. BOOL CSerialPort::IsOpen()  
  1143. {  
  1144.     return m_hComm != NULL && m_hComm!= INVALID_HANDLE_VALUE;//m_hComm增加INVALID_HANDLE_VALUE的情况 add by itas109 2016-07-29  
  1145. }  
  1146.   
  1147. void CSerialPort::ClosePort()  
  1148. {  
  1149.     MSG message;  
  1150.   
  1151.     //增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29  
  1152.     if(IsThreadSuspend(m_Thread))  
  1153.     {  
  1154.         ResumeThread(m_Thread);  
  1155.     }  
  1156.   
  1157.     //串口句柄无效  add by itas109 2016-07-29  
  1158.     if(m_hComm == INVALID_HANDLE_VALUE)  
  1159.     {  
  1160.         CloseHandle(m_hComm);  
  1161.         m_hComm = NULL;  
  1162.         return;  
  1163.     }  
  1164.   
  1165.     do  
  1166.     {  
  1167.         SetEvent(m_hShutdownEvent);  
  1168.         //add by liquanhai  防止死锁  2011-11-06  
  1169.         if(::PeekMessage(&message,m_pOwner,0,0,PM_REMOVE))  
  1170.         {  
  1171.             ::TranslateMessage(&message);  
  1172.             ::DispatchMessage(&message);  
  1173.         }  
  1174.     } while (m_bThreadAlive);  
  1175.   
  1176.     // if the port is still opened: close it   
  1177.     if (m_hComm != NULL)  
  1178.     {  
  1179.         CloseHandle(m_hComm);  
  1180.         m_hComm = NULL;  
  1181.     }  
  1182.   
  1183.     // Close Handles    
  1184.     if(m_hShutdownEvent!=NULL)  
  1185.     {  
  1186.         ResetEvent(m_hShutdownEvent);  
  1187.     }  
  1188.     if(m_ov.hEvent!=NULL)  
  1189.     {  
  1190.         ResetEvent(m_ov.hEvent);  
  1191.     }  
  1192.     if(m_hWriteEvent!=NULL)  
  1193.     {  
  1194.         ResetEvent(m_hWriteEvent);  
  1195.         //CloseHandle(m_hWriteEvent);//开发者反映,这里会导致多个串口工作时,重新打开串口异常  
  1196.     }  
  1197.   
  1198.     if(m_szWriteBuffer != NULL)  
  1199.     {  
  1200.         delete [] m_szWriteBuffer;  
  1201.         m_szWriteBuffer = NULL;  
  1202.     }  
  1203. }  
  1204.   
  1205. void CSerialPort::WriteToPort(char* string,int n)  
  1206. {  
  1207.     assert(m_hComm != 0);  
  1208.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
  1209.     memcpy(m_szWriteBuffer, string, n);  
  1210.     m_nWriteSize = n;  
  1211.   
  1212.     // set event for write  
  1213.     SetEvent(m_hWriteEvent);  
  1214. }  
  1215.   
  1216. void CSerialPort::WriteToPort(LPCTSTR string)  
  1217. {  
  1218.     assert(m_hComm != 0);  
  1219.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
  1220.     strcpy_s(m_szWriteBuffer,m_nWriteBufferSize,string);  
  1221.     m_nWriteSize=strlen(string);  
  1222.     // set event for write  
  1223.     SetEvent(m_hWriteEvent);  
  1224. }  
  1225.   
  1226. void CSerialPort::WriteToPort(BYTE* Buffer, int n)  
  1227. {  
  1228.     assert(m_hComm != 0);  
  1229.     memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  
  1230.     int i;  
  1231.     for(i=0; i
  1232.     {  
  1233.         m_szWriteBuffer[i] = Buffer[i];  
  1234.     }  
  1235.     m_nWriteSize=n;  
  1236.   
  1237.     // set event for write  
  1238.     SetEvent(m_hWriteEvent);  
  1239. }  
  1240.   
  1241.   
  1242. //void CSerialPort::SendData(LPCTSTR lpszData, const int nLength)  
  1243. //{  
  1244. //  assert(m_hComm != 0);  
  1245. //  memset(m_szWriteBuffer, 0, nLength);  
  1246. //  strcpy_s(m_szWriteBuffer,m_nWriteBufferSize,lpszData);  
  1247. //  m_nWriteSize=nLength;  
  1248. //  // set event for write  
  1249. //  SetEvent(m_hWriteEvent);  
  1250. //}  
  1251.   
  1252. //BOOL CSerialPort::RecvData(LPTSTR lpszData, const int nSize)  
  1253. //{  
  1254. //  //  
  1255. //  //接收数据  
  1256. //  //  
  1257. //  assert(m_hComm!=0);  
  1258. //  memset(lpszData,0,nSize);  
  1259. //  DWORD mylen  = 0;  
  1260. //  DWORD mylen2 = 0;  
  1261. //  while (mylen < (DWORD)nSize) {  
  1262. //      if(!ReadFile(m_hComm,lpszData,nSize,&mylen2,NULL))   
  1263. //          return FALSE;  
  1264. //      mylen += mylen2;  
  1265. //  }  
  1266. //  
  1267. //  return TRUE;  
  1268. //}  
  1269. //  
  1270. ///查询注册表的串口号,将值存于数组中  
  1271. ///本代码参考于mingojiang的获取串口逻辑名代码  
  1272. //  
  1273. void CSerialPort::QueryKey(HKEY hKey)   
  1274. {   
  1275. #define MAX_KEY_LENGTH 255  
  1276. #define MAX_VALUE_NAME 16383  
  1277.     //  TCHAR    achKey[MAX_KEY_LENGTH];   // buffer for subkey name  
  1278.     //  DWORD    cbName;                   // size of name string   
  1279.     TCHAR    achClass[MAX_PATH] = TEXT("");  // buffer for class name   
  1280.     DWORD    cchClassName = MAX_PATH;  // size of class string   
  1281.     DWORD    cSubKeys=0;               // number of subkeys   
  1282.     DWORD    cbMaxSubKey;              // longest subkey size   
  1283.     DWORD    cchMaxClass;              // longest class string   
  1284.     DWORD    cValues;              // number of values for key   
  1285.     DWORD    cchMaxValue;          // longest value name   
  1286.     DWORD    cbMaxValueData;       // longest value data   
  1287.     DWORD    cbSecurityDescriptor; // size of security descriptor   
  1288.     FILETIME ftLastWriteTime;      // last write time   
  1289.   
  1290.     DWORD i, retCode;   
  1291.   
  1292.     TCHAR  achValue[MAX_VALUE_NAME];   
  1293.     DWORD cchValue = MAX_VALUE_NAME;   
  1294.   
  1295.     // Get the class name and the value count.   
  1296.     retCode = RegQueryInfoKey(  
  1297.         hKey,                    // key handle   
  1298.         achClass,                // buffer for class name   
  1299.         &cchClassName,           // size of class string   
  1300.         NULL,                    // reserved   
  1301.         &cSubKeys,               // number of subkeys   
  1302.         &cbMaxSubKey,            // longest subkey size   
  1303.         &cchMaxClass,            // longest class string   
  1304.         &cValues,                // number of values for this key   
  1305.         &cchMaxValue,            // longest value name   
  1306.         &cbMaxValueData,         // longest value data   
  1307.         &cbSecurityDescriptor,   // security descriptor   
  1308.         &ftLastWriteTime);       // last write time   
  1309.   
  1310.     for (i=0;i<20;i++)///存放串口号的数组初始化  
  1311.     {  
  1312.         m_nComArray[i] = -1;  
  1313.     }  
  1314.   
  1315.     // Enumerate the key values.   
  1316.     if (cValues > 0) {  
  1317.         for (i=0, retCode=ERROR_SUCCESS; i

你可能感兴趣的:(MFC)