VC++ 动态检测串口的热插拔
在串口编程中经常需要知道串口号,用来配置串口,可是没有好的办法,只能进到设备管理器中去看,那么如何能够实现软件的自动检测串口号,并且可支持热插拔检测那?
下面将讲述两种方法来实现这种效果:一种是遍历设备列表中的所有串口0-255,二是通过读去注册表来实现检测
在这篇文章中将只讲述循环遍历方法的实现,在下一篇文章中将讲述利用注册表方法的实现
---------------------------------------------------
第一种方法是一种比较简单也比较笨的方法,对于每一个串口一个一个的去试,试完了也就知道了,计算机一般支持0-255即256个串口,可是利用CreateFile创建串口,只能成功创建COM0-COM9串口设备,而COM10及以上的串口创建CreateFile就会返回-1,因为10及以上已经超出的设备的命名规范,需要使用\$device\COM10方式,作为参数传递给CreateFile。
You
1.遍历所有串口源码:
OnInitDialog初始化窗口
- BOOL
CDetectComDlg::OnInitDialog() - {
-
CDialog::OnInitDialog(); -
-
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); -
ASSERT(IDM_ABOUTBOX < 0xF000); -
-
CMenu* pSysMenu = GetSystemMenu(FALSE); -
if (pSysMenu != NULL) -
{ -
CString strAboutMenu; -
strAboutMenu.LoadString(IDS_ABOUTBOX); -
if (!strAboutMenu.IsEmpty()) -
{ -
pSysMenu->AppendMenu(MF_SEPARATOR); -
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); -
} -
} -
SetIcon(m_hIcon, TRUE); // 设置大图标 -
SetIcon(m_hIcon, FALSE); // 设置小图标 -
-
DWORD sStyle = m_ListShow.GetExtendedStyle(); -
sStyle |= LVS_EX_GRIDLINES; -
sStyle |= LVS_EX_FULLROWSELECT; -
m_ListShow.SetExtendedStyle(sStyle); -
m_ListShow.InsertColumn(0,_T("索引"),LVCFMT_LEFT,100); -
m_ListShow.InsertColumn(1,_T("串口号"),LVCFMT_LEFT,100); -
-
TraversalCom(); //遍历Com口 -
return TRUE; - }
- void
CDetectComDlg::TraversalCom(void) - {
-
EnumerateSerialPorts(ports,portse,portsu); -
unsigned short Counter; -
unsigned short Setcom; -
CString str; -
-
//获取可用串口个数 -
Counter = portse.GetSize(); -
//如果个数大于0 -
if(Counter > 0) -
{ -
//初始化串口列表框 -
for(int i=0; i -
{ -
Setcom = portse.ElementAt(i); -
str.Format("%d",i); -
m_ListShow.InsertItem(i,str); -
str.Format(_T("COM%d "),Setcom); -
m_ListShow.SetItemText(i,1,str); -
} -
} - }
- void
CDetectComDlg::EnumerateSerialPorts(CUIntArray& ports, CUIntArray& portse, CUIntArray& portsu) - {
-
//清除串口数组内容 -
ports.RemoveAll(); -
portse.RemoveAll(); -
portsu.RemoveAll(); -
//因为至多有255个串口,所以依次检查各串口是否存在 -
//如果能打开某一串口,或打开串口不成功,但返回的是 ERROR_ACCESS_DENIED错误信息, -
//都认为串口存在,只不过后者表明串口已经被占用,否则串口不存在 -
for (int i=1; i<256; i++) -
{ -
//Form the Raw device name -
CString sPort; -
sPort.Format(_T("\\\\.\\COM%d"), i); -
-
//Try to open the port -
BOOL bSuccess = FALSE; -
HANDLE hPort = ::CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); -
if (hPort == INVALID_HANDLE_VALUE) -
{ -
DWORD dwError = GetLastError(); -
-
if (dwError == ERROR_ACCESS_DENIED) -
{ -
bSuccess = TRUE; -
portsu.Add(i); //已占用的串口 -
} -
} -
else -
{ -
//The port was opened successfully -
bSuccess = TRUE; -
portse.Add(i); 可用的串口 -
//Don't forget to close the port, since we are going to do nothing with it anyway -
CloseHandle(hPort); -
} -
-
//Add the port number to the array which will be returned -
if (bSuccess) -
ports.Add(i); //所有存在的串口 -
} - }
这时再刚登陆窗口时便可罗列出可用串口。
2.检测串口的热插拔
这里主要利用Cwnd的ON_WM_DEVICECHANGE消息来处理。
ON_WM_DEVICECHANGE消息在VS是通过手动添加的
注意:此消息只有顶层窗口可以捕获到
因此,首先得手动添加ON_WM_DEVICECHANGE消息:
第一步:在消息映射BEGIN_MESSAGE_MAP(Ctbox_debug_viewDlg, CDialogEx)中添加:
- afx_msg
BOOL OnDeviceChange(UINT nEventType, DWORD dwData);
第二步:在cpp文件中添加函数声明
- afx_msg
BOOL OnDeviceChange(UINT nEventType, DWORD dwData);
第三步:实现
- BOOL
CDetectComDlg::OnDeviceChange(UINT nEventType,DWORD dwData) - {
-
//DEV_BROADCAST_DEVICEINTERFACE* dbd = (DEV_BROADCAST_DEVICEINTERFACE*) dwData; -
switch (nEventType) -
{ -
case DBT_DEVICEREMOVECOMPLETE://移除设备 -
case DBT_DEVICEARRIVAL://添加设备 -
ReDetectCom();//刷新列表框的内容 -
break; -
-
default: -
break; -
} -
return TRUE; - }
- void
CDetectComDlg::ReDetectCom(void) - {
-
m_ListShow.DeleteAllItems(); -
TraversalCom(); - }
第四步包含头文件
由于DEV_BROADCAST_DEVICEINTERFACE,DBT_DEVICEREMOVECOMPLETE,DBT_DEVICEARRIVAL这几个东东在头文件Dbt.h中定义的,所以包含Dbt.h头文件
- #include
至此遍历方法实现检测串口的方法已经实现,在下一篇中将介绍如何读取注册表实现串口的自动检测
源码:遍历方法的实现:遍历方法的实现
在上一篇文章中讲述了如何通过循环遍历的方法获取可用串口,可是这样的方法过于暴力,难免会想有没有其他的办法那,嘿嘿,那是肯定会有的,不管什么问题,解决问题的方法永远都不止一种。下面讲述如何通过注册表来获取可用串口。
大家都知道,通过设备管理器我们可以看到可用串口号的列表,windows肯定有自己管理各种设备的方法,那就是大家所熟悉的注册表,注册表中记录各种设备信息以及其他重要信息。在HKEY_LOCAL_MACHINE下逐级展开到Hardware\\DeviceMap\\SerialComm,这里记录的就是串口信息。只要通过简单的注册表读取操作我们就可以得到串口列表。
这里将展示给大家,通过注册表读取,并将结果展示到列表框中的例子。
1遍历串口源码如下:
OnInitDialog中如下:
- BOOL
CDetectComDlg::OnInitDialog() - {
-
CDialog::OnInitDialog(); -
-
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); -
ASSERT(IDM_ABOUTBOX < 0xF000); -
-
CMenu* pSysMenu = GetSystemMenu(FALSE); -
if (pSysMenu != NULL) -
{ -
CString strAboutMenu; -
strAboutMenu.LoadString(IDS_ABOUTBOX); -
if (!strAboutMenu.IsEmpty()) -
{ -
pSysMenu->AppendMenu(MF_SEPARATOR); -
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); -
} -
} -
SetIcon(m_hIcon, TRUE); // 设置大图标 -
SetIcon(m_hIcon, FALSE); // 设置小图标 -
-
DWORD sStyle = m_ListShow.GetExtendedStyle(); -
sStyle |= LVS_EX_GRIDLINES; -
sStyle |= LVS_EX_FULLROWSELECT; -
m_ListShow.SetExtendedStyle(sStyle); -
m_ListShow.InsertColumn(0,_T("索引"),LVCFMT_LEFT,100); -
m_ListShow.InsertColumn(1,_T("串口号"),LVCFMT_LEFT,100); -
TraversalCom(); -
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE - }
TraversalCom()函数如下:
- void
CDetectComDlg::TraversalCom() - {
-
HKEY hKey; -
-
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Hardware\\DeviceMap\\SerialComm"), NULL, KEY_READ, &hKey)==ERROR_SUCCESS) -
{ -
TCHAR szPortName[256], szComName[256]; -
DWORD dwLong, dwSize; -
int nCount = 0; -
while(true) -
{ -
dwLong = dwSize = 256; -
if(RegEnumValue(hKey, nCount, szPortName, &dwLong, NULL, NULL, (PUCHAR)szComName, &dwSize)==ERROR_NO_MORE_ITEMS) -
break; -
CString str; -
str.Format("%d",nCount); -
m_ListShow.InsertItem(nCount,str); -
str.Format(_T("%s "),szComName); -
m_ListShow.SetItemText(nCount,1,str); -
nCount++; -
} -
RegCloseKey(hKey); -
-
} - }
这时再刚登陆窗口时便可罗列出可用串口。
2.检测串口的热插拔
这里主要利用Cwnd的ON_WM_DEVICECHANGE消息来处理。
ON_WM_DEVICECHANGE消息在VS是通过手动添加的
注意:此消息只有顶层窗口可以捕获到
因此,首先得手动添加ON_WM_DEVICECHANGE消息:
第一步:在消息映射BEGIN_MESSAGE_MAP(Ctbox_debug_viewDlg, CDialogEx)中添加:
- afx_msg
BOOL OnDeviceChange(UINT nEventType, DWORD dwData);
第二步:在cpp文件中添加函数声明
- afx_msg
BOOL OnDeviceChange(UINT nEventType, DWORD dwData);
- BOOL
CDetectComDlg::OnDeviceChange(UINT nEventType,DWORD dwData) - {
-
//DEV_BROADCAST_DEVICEINTERFACE* dbd = (DEV_BROADCAST_DEVICEINTERFACE*) dwData; -
switch (nEventType) -
{ -
case DBT_DEVICEREMOVECOMPLETE://移除设备 -
case DBT_DEVICEARRIVAL://添加设备 -
ReDetectCom();//刷新列表框的内容 -
break; -
-
default: -
break; -
} -
return TRUE; - }
RefreshCom的内容
- void
CDetectComDlg::RefreshCom(void) - {
-
m_ListShow.DeleteAllItems(); -
TraversalCom(); - }
源码:通过注册表实现