项目中一直在用串口通讯,但没有实现可用串口的自动检测和热插拔检测。今天通过查找资料实现了这些功能,所以在这里坐下记录。
参考资料:http://blog.csdn.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
添加打开串口按钮,并实现打开串口/关闭串口功能,实现代码如下:
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()
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
最后界面如下图所示
另外在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()