分享

在串口通信开发中实现自动查找串口端口的方法

 落水成花 2015-05-02

开发工具:visual studio 2010
本机可用串口信息如下:

在串口通信开发中实现自动查找串口端口的方法 - danshiming - danshiming的博客

 1 、查询注册表
查询注册表的方法是比较常见的方法,通过查看“ HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM ”项来获取串口信息。该方法就是使用编程方法读取注册表内信息,相当于用户通过在运行框内输入 ”regedit” (或 regedit32 )直接打开注册表。源代码如下:
/************************************************************************\
* 函数说明:获取本机所有的串口。多个串口以“|”连接。
* 参数说明:
* 返 回 值:
* 注意事项:本方法通过从注册表中搜索串口设备。该方法在 1ms内即可完成查找;
                    同时也可解决 usb 转串口设备的问题,比较实用,唯一缺点是,如果
                    用户在装某些软硬件时在注册表中注册了虚拟串口之类的,用此法枚举
                   得到的该类串口实际上是不能当串口用的。 比如:经测试,本机注册
                   表有串口COM3、COM5、COM6、COM12,但通过设备管理器查看只有COM5、
                   COM6、COM12串口,没有COM3,COM3可能是装某些软硬件时在注册表中注
                   册了虚拟串口之类的,引起的误判。


* 编辑日期:2013-08-26 16:22:57
* 作    者:DanSir

\************************************************************************/
wstring CSerialTools::GetLocalSerial()
{
 wstring szTotalSerial;
 HKEY hKey;
 wstring szData_Set = _T("HARDWARE\\DEVICEMAP\\SERIALCOMM\\");
 long ret0 = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szData_Set.c_str(), 0, KEY_READ, &hKey); // 打开一个制定的注册表键,成功返回ERROR_SUCCESS即“0”值
 if(ret0 != ERROR_SUCCESS)
  return szTotalSerial;
 
 WCHAR szName[25] = {0};
 UCHAR szPortName[25] = {0};
 DWORD dwIndex = 0;
 DWORD dwName;
 DWORD dwSizeofPortName;
 DWORD dwType;
 LONG lStatus = -1;
 wchar_t szSerialName[256] = {0};
 dwName = sizeof(szName);
 dwSizeofPortName = sizeof(szPortName);
 do
 {
  memset(szName, 0, sizeof(szName));
        memset(szPortName, 0, sizeof(szPortName));
  lStatus = RegEnumValue(hKey, dwIndex++, szName, &dwName, NULL, &dwType, szPortName, &dwSizeofPortName);// 读取键值
  DWORD dwError = GetLastError();
  if((lStatus == ERROR_SUCCESS)||(lStatus == ERROR_MORE_DATA))
  {
   memset(szSerialName, 0, sizeof(szSerialName));
   memcpy(szSerialName, szPortName, dwSizeofPortName);
   szTotalSerial += szSerialName;       // 串口字符串保存
   szTotalSerial.append(_T("|"));
  }
  // 每读取一次dwName和dwSizeofPortName都会被修改
  // 注意一定要重置,否则会出现很离奇的错误,本人就试过因没有重置,出现先插入串口号大的(如COM4),再插入串口号小的(如COM3),此时虽能发现两个串口,但都是同一串口号(COM4)的问题,同时也读不了COM大于10以上的串口
  dwName = sizeof(szName);
  dwSizeofPortName = sizeof(szPortName);
 } while((lStatus != ERROR_NO_MORE_ITEMS));

 RegCloseKey(hKey);
 wstring::size_type nIndex = szTotalSerial.rfind(_T("|"));
 if(wstring::npos != nIndex)
 {
  szTotalSerial = szTotalSerial.substr(0, nIndex);
 }
  

 return szTotalSerial;
}
界面显示信息如下:

在串口通信开发中实现自动查找串口端口的方法 - danshiming - danshiming的博客
可以看到,从注册表中读出了COM3,但是COM3在设备管理器中是不存在的,所以用这种方法有误判可能性。该方法在 1ms内即可完成查找;同时也可解决 usb 转串口设备的问题,比较实用,唯一缺点是,如果用户在装某些软硬件时在注册表中注册了虚拟串口之类的,用此法枚举得到的该类串口实际上是不能当串口用的,如本例中的COM3。
2、使用 SetupAPI 函数集的方法
此种方法是最简单的方法,之所以简单是因为已经有人将复杂的代码封装起来了,只需像傻子一样调用就可以完成工作了,具体的说明请看 http://www./Cpp/W-P/system/hardwareinformation/article.php/c5721/ ,使用该方法要在你的程序中,添加“ EnumSerial.cpp ”和“ EnumSerial.h ”两个文件,并且将 Setupapi.lib 包含进你的工程,方法在项目属性-配置属性-连接器-输入-附加依赖项中添加setupapi.lib,然后在EnumSerial.cpp中添加包含"setupapi.h" ,你的应用程序添加包含EnumSerial.h头文件就行了,
下面给出调用该方法的例子代码:
/************************************************************************\
* 函数说明:获取本机所有的串口。多个串口以“|”连接。
* 参数说明:
* 返 回 值:
* 注意事项:此种方法是最简单的方法,之所以简单是因为已经有人将复杂的代码封装起来了
   ,只需像傻子一样调用就可以完成工作了,具体的说明请看
   http://www./Cpp/W-P/system/hardwareinformation/article.php/c5721/
   使用该方法要在你的程序中,添加“ EnumSerial.cpp ”和“ EnumSerial.h ”两个文件,
   并且将 Setupapi.lib 包含进你的工程,方法在项目属性-配置属性-连接器-输入-附加依赖项
   中添加setupapi.lib,然后在EnumSerial.cpp中添加包含"setupapi.h" ,你的应用程序添
   加包含EnumSerial.h头文件就行了。本方法避免了用户在装某些软硬件时在注册表中注册了
   虚拟串口之类的东西引起的误判。比如::经测试,本机注册表有串口COM3、COM5、COM6、COM12,
   但通过设备管理器查看,只有COM5、COM6、COM12串口,没有COM3,COM3可能是装某些软硬件时在注
   册表中注册了虚拟串口之类的,如果采用读注册表即GetLocalSerial()方法,会将COM3也读取到,
   从而引起的误判,但采用本方法时则不会引起误判,即不会将COM3读取出来。
* 编辑日期:2013-08-27 13:25:41
* 作    者:DanSir
\************************************************************************/
wstring CSerialTools::GetlocalSerialEx()
{
 CArray<SSerInfo,SSerInfo&> asi;
 // Populate the list of serial ports.
 EnumSerialPorts(asi, FALSE/*include all*/);
 wstring szTotalSerial;
 for (int ii=0; ii < asi.GetSize(); ii++)
 {
  // 注意:不能使用asi[ii].strPortName,当串口存在时,其为空(内部代码没实现)
  CString strFriendlyName = asi[ii].strFriendlyName;
  int nIndexFirst = strFriendlyName.Find(_T("("));
  int nIndexLast = strFriendlyName.ReverseFind(')');
  if (-1 == nIndexLast || -1 == nIndexFirst)
  {
   continue;
  }
  CString strPortName = strFriendlyName.Mid(nIndexFirst + 1, nIndexLast - nIndexFirst - 1);
  szTotalSerial += strPortName;       // 串口字符串保存
  szTotalSerial.append(_T("|"));
 }
 wstring::size_type nIndex = szTotalSerial.rfind(_T("|"));
 if(wstring::npos != nIndex)
 {
  szTotalSerial = szTotalSerial.substr(0, nIndex);
 }
 return szTotalSerial;
}
界面显示如下:
在串口通信开发中实现自动查找串口端口的方法 - danshiming - danshiming的博客
 
 
该方法查找一个串口就要 15ms 左右,但可以看到该方法获取的串口完完全全就是硬件设备管理器中的串口。

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多