自动检测可用串口实现串口通讯程序(可实现串口热插拔检测)
项目中一直在用串口通讯,但没有实现可用串口的自动检测和热插拔检测。今天通过查找资料实现了这些功能,所以在这里坐下记录。
参考资料:http://blog.****.net/flydream0/article/details/8086976
实现环境:VC6.0
1、创建基于对话框的工程
2、在工程中添加串口通讯控件,添加方法如下
VC6.0菜单中:工程—>增加到工程—>Components and Controls Gallery双击打开Registered ActiveX Controls在下拉框里选择Microsoft Communications Control, version 6.0,如图所示
在这里碰到问题了,win7下的vc6.0在添加控件的时候出错,所以要在此声明,使用的是XP+VC6.0,估计是两者的问
兼容题,在此就不说了,接着上面的记录
在添加控件成功之后,和添加其他控件一样拖拽至对话框内即可
3、为Comm Control控件添加变量和响应函数,如图所示
至此,控件添加完成
4、下面实现自动检测串口
遍历所有串口代码如下:
- void CCommDlg::foreachPort(CUIntArray& ports,CUIntArray& portse,CUIntArray& portsu)
- {
- //清空数组内容
- ports.RemoveAll();//所有存在串口
- portse.RemoveAll();//可用串口
- portsu.RemoveAll();//已占用串口
- //清空数组内容
- //串口最多有255,所以这里遍历255个串口是否可以使用即可
- for (int i=1;i<=255;i++)
- {
- //形成串口名称
- CString portStr;
- portStr.Format("COM%d",i);
- //形成串口名称
- //尝试打开串口
- BOOL bSuccess=FALSE;
- HANDLE m_hCom=CreateFile(portStr,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
- if (m_hCom==INVALID_HANDLE_VALUE)
- {
- DWORD dwError=GetLastError();
- if (dwError==ERROR_ACCESS_DENIED)
- {
- bSuccess=TRUE;
- portsu.Add(i);//已占用的串口号
- }
- }
- else
- {
- bSuccess=TRUE;
- portse.Add(i);//可用串口号
- CloseHandle(m_hCom);
- }
- if (bSuccess) ports.Add(i);//全部串口号
- //尝试打开串口
- }
- }
在下拉框内显示可用窗口,实现代码如下:
- void CCommDlg::fillPortComm()
- {
- foreachPort(ports,portse,portsu);//所有串口号,可用串口号,已占用串口号
- CString str;
- // unsigned short portNum;
- // unsigned short portSerialNum;
- // portNum=ports.GetSize();
- if (portse.GetSize()>0)
- {
- for (int i=0;i<portse.GetSize();i++)
- {
- str.Format("COM%d",portse.ElementAt(i));
- m_portComBox.AddString(str);
- }
- }
- }
添加打开串口按钮,并实现打开串口/关闭串口功能,实现代码如下:
- void CCommDlg::OnOpenport()
- {
- // TODO: Add your control notification handler code here
- CString strname;
- int comNum=portse.ElementAt(((CComboBox *)GetDlgItem(IDC_PORTCOMBO))->GetCurSel());
- if (GetDlgItemText(IDC_OPENPORT,strname),strname=="OpenPort")
- {
- if(m_Comm.GetPortOpen())//如果串口是打开的,则关闭串口
- {
- m_Comm.SetPortOpen(FALSE);
- }
- try
- {
- m_Comm.SetCommPort(comNum); //串口
- m_Comm.SetInputMode(1); //设置输入方式为二进制方式
- m_Comm.SetInBufferSize(1024); //设置输入缓冲区的大小,Bytes
- m_Comm.SetOutBufferSize(512); //设置输入缓冲区的大小,Bytes
- m_Comm.SetSettings("9600,n,8,1"); //设置波特率等参数
- m_Comm.SetPortOpen(TRUE);
- }
- catch (CException* e)
- {
- e-> Delete(); //防止显示“Invalid Port Number”对话框
- strname.Format("COM%d打开失败!",comNum);
- AfxMessageBox(strname);
- return;
- }
- m_Comm.SetRThreshold(1); //为1表示有一个字符即引发事件
- m_Comm.SetInputLen(0);
- m_Comm.GetInput();
- SetDlgItemText(IDC_OPENPORT,"Close");
- //SetTimer(1,500,NULL);//设置定时器,循环发送时使用
- //display status
- strname.Format("COM%d打开成功",comNum);
- SetDlgItemText(IDC_STATUS,strname);
- //display status
- GetDlgItem(IDC_SENDBtn)->EnableWindow(TRUE);
- }
- else
- {
- m_Comm.SetPortOpen(FALSE);
- SetDlgItemText(IDC_OPENPORT,"OpenPort");
- //KillTimer(1);//设置定时器,循环发送时使用
- //display status
- strname.Format("COM%d关闭成功",comNum);
- SetDlgItemText(IDC_STATUS,strname);
- //display status
- GetDlgItem(IDC_SENDBtn)->EnableWindow(FALSE);
- }
- }
为了实现自动检测串口热插拔,需手动添加消息ON_WM_DEVICECHANGE()
- <pre class="cpp" name="code"><pre class="cpp" name="code">BEGIN_MESSAGE_MAP(CCommDlg, CDialog)
- //{{AFX_MSG_MAP(CCommDlg)
- ON_WM_DEVICECHANGE()
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
在工程的头文件中添加如下代码,对应消息ON_WM_DEVICECHANGE
- afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);//实现串口热插拔
在*.CPP文件下,实现代码如下
- //实现串口热插拔
- BOOL CCommDlg::OnDeviceChange(UINT nEventType, DWORD dwData)
- {
- switch(nEventType)
- {
- case DBT_DEVICEREMOVECOMPLETE:
- //refreshCom();
- //break;
- case DBT_DEVICEARRIVAL:
- refreshCom();
- break;
- default:
- break;
- }
- return TRUE;
- }
- void CCommDlg::refreshCom()
- {
- //清除Combo Box 内容
- int m_Num=m_portComBox.GetCount();
- /*m_portComBox.GetCount()这里得到的数值是实时改变的,
- 所以这里不能用m_portComBox.GetCount()判断循环是否结束*/
- for (int i=0;i<m_Num;i++)
- {
- m_portComBox.DeleteString(m_Num-1-i);
- }
- //清除Combo Box 内容
- //重新填充,并设置第一个串口为默认串口
- fillPortComm();
- m_portComBox.SetCurSel(0);
- //重新填充,并设置第一个串口为默认串口
- }
- //实现串口热插拔
最后界面如下图所示
另外在XP+VC6.0配合的情况下添加ON_WM_DEVICECHANGE之后再打开“建立类向导”会出现如下提示
Parsing error:Undrecognized macro
Input Line:"ON_WM_DEVICECHANGED"
- BEGIN_MESSAGE_MAP(CCommDlg, CDialog)
- //{{AFX_MSG_MAP(CCommDlg)
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
出现这样问题的原因在于上面语句中注释掉的语句具有特殊的作用。注释掉的语句被称为注释宏,注释宏为“建立类向导(class wizad)”服务,“建立类向导(class wizad)”通过注释宏来定位自动添加代码的位置。删除掉注释宏之后“建立类向导(class wizad)”将无法工作。
所以这里的解决办法是把自己定义消息的语句放到注释宏之外,既如下语句
- BEGIN_MESSAGE_MAP(CCommDlg, CDialog)
- //{{AFX_MSG_MAP(CCommDlg)
- //}}AFX_MSG_MAP
- ON_WM_DEVICECHANGE()
- END_MESSAGE_MAP()