Bootstrap

枚举本机串口

       有过硬件编程经验的朋友对串口操作一定不会陌生,目前绝大多数设备和计算机的通信还是通过串口来实现的。做下位机通信经常需要遍历本机所有串口通过消息确认我们的目标设备到底连在哪个串口上,而一般的方法就是一下遍历比如从com1到com10,因为一个机器上应该不会有这么多个串口,所以这样应该就可以涵盖到所有可用串口。可是这种方法显得过于暴力,有没有什么途径可以获取到本机所有的可用串口呢?答案当然是肯定的。
       我们知道如果我们打开windows的硬件管理器,那么本机有多少可用串口一目了然。串口这种硬件设备系统自然会管理方式,我们的问题是有没有提供相应的接口函数或可以通过某些途径获取到这些信息呢?首先想到的自然是windows的注册表,而注册表也确实能解决这个问题。在HKEY_LOCAL_MACHINE下我们可以逐级找到这样一个表项Hardware\\DeviceMap\\SerialComm,显然这里记录的就是串口信息。只要通过一些基本的操作注册表的函数我们就可以获得本机串口的全部信息,这里给出获取串口名并添加到一个ComboBox中的一段代码
void CEnumComDlg::FindComPort()
{
	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;
		CComboBox*	pCombo	= (CComboBox*)GetDlgItem(IDC_COMBO_COM);

		pCombo->ResetContent();
		while(true)
		{
			dwLong	= dwSize	= 256;
			if(RegEnumValue(hKey, nCount, szPortName, &dwLong, NULL, NULL, (PUCHAR)szComName, &dwSize)==ERROR_NO_MORE_ITEMS)
				break;

			pCombo->InsertString(nCount, szComName);
			nCount++;
		}
		RegCloseKey(hKey);
		pCombo->SetCurSel(0);
	}
}

       这样有关串口的问题就解决了,不过我们还是继续讨论一下,看看有没有可以完善的地方。我们知道本机串口数量是有限的,目前大都数机器都只有一个串口而笔记本都没有串口。那么如果我们需要通过多个串口来进行仪器控制怎么办呢?有这方面经验的朋友应该知道可以通过USB串口转接线来为我们的机器做扩展。现在问题来了,如果我们已经通过上面的函数获取到本机目前的串口信息,而此时我们通过USB串口转接线增加了一个串口,那么这个串口我们岂不是永远无法使用了?有没有什么方法可以监测到系统的硬件变化呢?解决这个问题需要用到一个消息WM_DEVICECHANGE,在系统硬件发生变化的时候系统会向应用程序发送这个消息来告知系统硬件改变的一些相应的信息,而我们通过这个消息就可以及时更新串口信息,示例代码如下
BOOL CEnumComDlg::OnDeviceChange(UINT nEventType, DWORD_PTR dwData)
{
	if(nEventType==DBT_DEVNODES_CHANGED)
		FindComPort();

	return	TRUE;
}

       到这里应该说我们已经很好的解决了枚举串口这个问题,我做了一个简单的小例子,以上代码皆取自这个例子,有兴趣的朋友可以到我的资源里下载。此外,作为问题补充这里再提一下类似问题在windowsCE下如何处理。CE下提供了一组更通用硬件操作接口函数FindFirstDevice、FindNextDevice,这些函数和具有相似名字的文件操作函数的使用方法类似,可以访问所有系统设备,以下代码实现了我上面实现的功能,仅供大家参考。

	DEVMGR_DEVICE_INFORMATION	ddiFind	= {0};
	ddiFind.dwSize	= sizeof(DEVMGR_DEVICE_INFORMATION);

	HANDLE	hDevFind	= FindFirstDevice(DeviceSearchByLegacyName, L"COM*", &ddiFind);
	CString	strCom;

	m_comboComList.ResetContent();
	do
	{
		strCom	= ddiFind.szLegacyName;
		strCom	= strCom.Left(strCom.GetLength()-1);
		m_comboComList.AddString(strCom);
	}while(FindNextDevice(hDevFind, &ddiFind));
	if(hDevFind!=INVALID_HANDLE_VALUE)
		FindClose(hDevFind);

       到这这个问题应该说解决的比较完满了,对于此类问题如果朋友们还有什么更好的解决方案还请不吝赐教。

;