通过编写串口助手工具学习MFC过程——(九)自动识别串口的方法
通过编写串口助手工具学习MFC过程
因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉。这次通过做一个串口助手再次熟悉一下MFC,并做了一下记录,以便方便以后查阅。做的过程中多是遇到问题直接百度和谷歌搜索来的,所以很多都是不求甚解,知其然不知其所以然。另外做此工具只是为了熟悉了解,许多功能还没有完善!(开发工具VS2008)
(九)自动识别串口的方法
网上找了一下,找到两个介绍的较详细的,可用的方法,分别用的是“查找windows注册表”和“枚举设备”
一、windows注册表实现 自动识别串口
此方法只能获得端口号不能获取详细信息,和手动打开注册表查看是一样的,获得的名字就是COM1和COM2没有具体详细的信息。

实现Windows系统下自动识别串口需要调用三个Windows API函数,它们是:
1. 主要用于打开注册表串口项
LONG RegOpenKeyEx(
HKEY hKey, //主键,即串口信息存放的文件夹
LPCTSTR lpSubKey, //子键,串口所在的具体文件夹
DWORD ulOptions, //保留值,不用管,必须设置为0
REGSAM samDesired, //访问权限
PHKEY phkResult //返回的串口句柄,以下两个函数使用
);
2、主要用于获得在当前串口注册表中有多少个串口
LONG RegQueryInfoKey(
HKEY hKey, //RegOpenKeyEx的五个参数返回的子键句柄
LPTSTR lpClass, //NULL
LPDWORD lpcClass, //NULL
LPDWORD lpReserved, //NULL
LPDWORD lpcSubKeys, //子键的数量
LPDWORD lpcMaxSubKeyLen, //最大子键的长度
LPDWORD lpcMaxClassLen, //NULL
LPDWORD lpcValues, //串口的数量
LPDWORD lpcMaxValueNameLen, //最大值名的长度
LPDWORD lpcMaxValueLen, //最大串口的长度
LPDWORD lpcbSecurityDescriptor, //NULL
PFILETIME lpftLastWriteTime //NULL
);
3、主要用于获得串口名,如"COM3"等
LONG RegEnumValue(
HKEY hKey, //串口子键句柄
DWORD dwIndex, //在注册表中的索引
LPTSTR lpValueName, //值名
LPDWORD lpcValueName, //值名的长度
LPDWORD lpReserved, //NULL
LPDWORD lpType, //串口的数据类型
LPBYTE lpData, //串口名
LPDWORD lpcbData //串口名的长度
);
自动识别串口的实现:
struct UartInfo
{
DWORD UartNum;
WCHAR UartName[];
};
//获取串口列表
BOOL EnumComs(struct UartInfo **UartCom, LPDWORD UartComNumber, CnuprogDlg *pMainDlg)
{
//LPCTSTR 即const char * *UartComNumber = ;
HKEY hNewKey;
LONG lResult=RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", , KEY_ALL_ACCESS, &hNewKey); if(lResult != ERROR_SUCCESS)
{
pMainDlg->AddToInfOut(_T("打开COM注册表失败!!!"),,);
return FALSE;
}
else
{
pMainDlg->AddToInfOut(_T("打开COM注册表成功!!!"),,);
} //DWORD即unsigned long
DWORD ValuesNumber;
DWORD MaxValueNameLen;
DWORD MaxValueLen;
CString str;
//检索指定的子键下有多少个值项
lResult = RegQueryInfoKey(
hNewKey,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&ValuesNumber,
&MaxValueNameLen,
&MaxValueLen,
NULL,
NULL
);
if(lResult != ERROR_SUCCESS)
{
RegCloseKey(hNewKey);
//pMainDlg->AddToInfOut(_T("检索连接在PC上的串口数量失败!!!"),1,1);
return FALSE;
}
else
{
// str.Format(_T("连接在PC上的串口数量是:%ld"), ValuesNumber);
// pMainDlg->AddToInfOut(str,1,1);
*UartCom =(struct UartInfo *)malloc( ValuesNumber * sizeof(struct UartInfo));
}
DWORD index;
DWORD uartindex = ;
//CHAR ValueName[MAX_VALUE_NAME];
WCHAR ValueName[];
//DWORD ValueNameSize = MAX_VALUE_NAME;
DWORD ValueNameSize;
DWORD DataType;
BYTE DataBuffer[];
DWORD DataLen = ;
//LPTSTR 即 char *, LPBYTE即 char *
//检索每个值项,获取值名,数据类型,数据
for(index = ; index < ValuesNumber; index++)
{
memset(ValueName, , sizeof(ValueName));
memset(DataBuffer, , sizeof(DataBuffer));
ValueNameSize = ;
DataLen = ;
lResult = RegEnumValue(hNewKey,index,ValueName,&ValueNameSize,NULL, &DataType, DataBuffer, &DataLen);
if (lResult == ERROR_SUCCESS )
{
switch(DataType)
{
case REG_NONE:
// No value type (0)
break;
case REG_SZ:
//Unicode nul terminated string (1)
break;
case REG_EXPAND_SZ:
// Unicode nul terminated string (2)
break;
case REG_BINARY:
// Free form binary (3)
break;
case REG_DWORD:
// 32-bit number (4)
break;
case REG_MULTI_SZ:
// Multiple Unicode strings (7)
break;
default:
break;
}
memcpy((*UartCom)[uartindex].UartName, DataBuffer, DataLen);
(*UartCom)[uartindex].UartNum = ValuesNumber;
uartindex++;
}
else if(lResult == ERROR_NO_MORE_ITEMS)
{
//pMainDlg->AddToInfOut(_T("检索串口完毕!!!"),1,1);
}
else
{
DWORD dw = GetLastError();
// str.Format(_T("检索串口出错: 0x%08x"), dw);
// pMainDlg->AddToInfOut(str,1,1);
return FALSE;
}
}
*UartComNumber = uartindex;
return TRUE;
}
结构体和函数
在主函数中的调用:
DWORD UartComNumber = ;
struct UartInfo *pUartCom;
BOOL bResult;
bResult = EnumComs(&pUartCom, &UartComNumber, pMainDlg);
DWORD index;
if(bResult)
{
pMainDlg->AddToInfOut(_T("获取串口列表成功"),,);
}
else
{
pMainDlg->AddToInfOut(_T("获取串口列表失败"),,);
}
for( index= ; index < UartComNumber; index++)
{
pMainDlg->m_ComboBox.AddString(pUartCom[index].UartName);
}
主函数调用
二、使用玫举方法,自动识别串口
这个方法可以得到更详细的信息

实现方法如下:
BOOL findCom()
{
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
SP_DEVINFO_DATA spdata = {};
GUID guid = GUID_DEVINTERFACE_COMPORT; // empty();
// 得到所有设备HDEVINFO
hDevInfo = SetupDiGetClassDevs(&guid, , , DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if(hDevInfo == INVALID_HANDLE_VALUE){
return FALSE;
} spdata.cbSize = sizeof(spdata);
// 循环列举
for(int i=; SetupDiEnumDeviceInfo(hDevInfo, i, &spdata); i++){
char buff[] = {};
// 获取详细信息
if(SetupDiGetDeviceRegistryProperty(hDevInfo, &spdata, SPDRP_FRIENDLYNAME, NULL,
PBYTE(buff), _countof(buff), NULL))
{
printf("buff = %s \n",buff);
// Prolific com port (COMxx)
char* p = strstr(buff, "(COM");
if(p){
int id = atoi(p + );
//if(p != buff) *(p-1) = '\0';
//add(c_comport(id, buff));
}
}
}
// // 释放
SetupDiDestroyDeviceInfoList(hDevInfo); return TRUE;
}
findCom()
整个获取的buff的第一个COM是串口的名字(串口号),整个获取的buff就是COM的详细信息。Buff获取的信息为 ELTIMA Virtual Serial Port (COM1->COM2),所以串口名字是 COM1。
此方法主要是引自下面这个博文:http://blog.csdn.net/itcastcpp/article/details/7078719/
基于Visual C++之Windows核心编程代码分析(1)实现设备管理器枚举设备
我们进行Windows编程的时候,有些时候需要枚举设备,例如光盘,光驱,硬盘等等,
我们如何实现功能呢,请见代码分析
#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#include <devguid.h>
#include <regstr.h>
/* 函数声明 */
BOOL EnumPresentDevice( const GUID * InterfaceClassGuid );
BOOL EnumAllDevice();
/*************************************
* BOOL EnumClassDevice( const GUID * InterfaceClassGuid )
* 功能 根据类型列举当前存在的设备
* 参数 InterfaceClassGuid,所需列举设备接口类的GUID
**************************************/
BOOL EnumClassDevice( const GUID * InterfaceClassGuid )
{
HDEVINFO DeviceInfoSet;
HDEVINFO NewDeviceInfoSet;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA lpDeviceInterfaceDetailData;
DWORD dwBufferSize = ;
DWORD i;
// 创建空设备信息列表
DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL);
if(DeviceInfoSet == INVALID_HANDLE_VALUE)
{
printf("CreateDeviceInfoList failed: %d\n", GetLastError());
return ;
}
// 根据接口类型获得新的设备信息列表
NewDeviceInfoSet = SetupDiGetClassDevsEx(
InterfaceClassGuid,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE,
DeviceInfoSet,// 之前创建的设备信息列表
NULL,
NULL
);
if(NewDeviceInfoSet == INVALID_HANDLE_VALUE)
{
printf( "SetupDiGetClassDevsEx failed: %d\n", GetLastError() );
return ;
}
// 设置 SP_DEVICE_INTERFACE_DATA 大小
DeviceInterfaceData.cbSize
= sizeof(SP_DEVICE_INTERFACE_DATA);
for (i=; ;i++)
{
// 列举接口信息
BOOL bResult = SetupDiEnumDeviceInterfaces(
NewDeviceInfoSet,
NULL,
InterfaceClassGuid,
i,
&DeviceInterfaceData
);
if(!bResult)
{
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
printf("ERROR: (%d)",GetLastError());
return FALSE;
}
break;
}
else
{
// 为PSP_DEVICE_INTERFACE_DETAIL_DATA结构分配内存,填充
lpDeviceInterfaceDetailData = HeapAlloc(
GetProcessHeap(), ,
sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
lpDeviceInterfaceDetailData->cbSize
= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
dwBufferSize = lpDeviceInterfaceDetailData->cbSize;
// 获得接口详细信息
while(!SetupDiGetDeviceInterfaceDetail(
NewDeviceInfoSet,
&DeviceInterfaceData,
lpDeviceInterfaceDetailData,
dwBufferSize,
&dwBufferSize,
NULL))
{
// 如果内存空间不足,再次分配,直到可以成功调用
if(ERROR_INSUFFICIENT_BUFFER==GetLastError())
{
lpDeviceInterfaceDetailData = HeapReAlloc(
GetProcessHeap(), ,
lpDeviceInterfaceDetailData, dwBufferSize);
lpDeviceInterfaceDetailData->cbSize
= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
}
}
// 显示信息
printf("DevicePath: %s\n",lpDeviceInterfaceDetailData->DevicePath);
// lpDeviceInterfaceDetailData->DevicePath可作为CreateFile的参数,进行IO控制
// 释放内存
HeapFree(GetProcessHeap(),,lpDeviceInterfaceDetailData);
}
}
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
return TRUE;
}
/*************************************
* BOOL EnumAllDevice( )
* 功能 列举当前存在的设备
* 返回值 是否成功
**************************************/
BOOL EnumAllDevice()
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
printf("Displaying the Installed Devices\n\n");
// 得到所有设备 HDEVINFO
hDevInfo = SetupDiGetClassDevs(NULL,
, // 无类型
, // 无回调函数
DIGCF_PRESENT | DIGCF_ALLCLASSES );
if (hDevInfo == INVALID_HANDLE_VALUE)
{
return FALSE;
}
// 循环列举
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = ;
// 获取详细信息
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_DEVICEDESC,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// 内存不足
if (buffer) HeapFree(GetProcessHeap(), , buffer);
buffer = (LPTSTR)HeapAlloc(GetProcessHeap(), , buffersize);
}
else
break;
}
// 输出
printf("GUID:{%.8X-%.4X-%.4X--%.2X%.2X-%.2X%.2X%.2X%.2X%.2X%.2X} "
"Device: %s\n",
DeviceInfoData.ClassGuid.Data1,
DeviceInfoData.ClassGuid.Data2,
DeviceInfoData.ClassGuid.Data3,
DeviceInfoData.ClassGuid.Data4[],
DeviceInfoData.ClassGuid.Data4[],
DeviceInfoData.ClassGuid.Data4[],
DeviceInfoData.ClassGuid.Data4[],
DeviceInfoData.ClassGuid.Data4[],
DeviceInfoData.ClassGuid.Data4[],
DeviceInfoData.ClassGuid.Data4[],
DeviceInfoData.ClassGuid.Data4[],buffer);
if (buffer) HeapFree(GetProcessHeap(), , buffer);
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
return FALSE;
}
// 释放
SetupDiDestroyDeviceInfoList(hDevInfo);
return TRUE;
}
int main( int argc, char *argv[ ], char *envp[ ] )
{
// 列举所有设备
printf("Enumerating All Device\n\n");
EnumAllDevice();
// 列举磁盘分卷驱动器设备
printf("\n\nEnumerating Present Volume \n\n");
EnumClassDevice(&GUID_DEVINTERFACE_VOLUME);
return ;
}
通过编写串口助手工具学习MFC过程——(九)自动识别串口的方法的更多相关文章
- 通过编写串口助手工具学习MFC过程--(十一)弹出模态型对话框
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 通过编写串口助手工具学习MFC过程——(十)UpdateData()用法和编辑框的赋值、取值
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 通过编写串口助手工具学习MFC过程——(八)遇到的一些问题
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 通过编写串口助手工具学习MFC过程——(六)添加Edit编辑框控件
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 通过编写串口助手工具学习MFC过程——(七)添加Tab Control控件
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 通过编写串口助手工具学习MFC过程——(五)添加CheckBox复选框
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 通过编写串口助手工具学习MFC过程——(四)添加ComboBox组合框
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 通过编写串口助手工具学习MFC过程——(三)Unicode字符集的宽字符和多字节字符转换
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
- 通过编写串口助手工具学习MFC过程——(二)通过“打开串口”按钮了解基本操作
通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...
随机推荐
- C# 之 数组倒叙排列
//倒叙排列 string temp=""; ; i < strlist.Length / ; i++) { temp = strlist[i]; strlist[i] = ...
- const与#define的区别
1.const (1)为什么需要const成员函数? C中常用:“ #define 变量名 变量值”定义一个值替代,然而却有个致命缺点:缺乏类型检测机制,这样预处理在C++中成为可能引发错误的隐患,于 ...
- 计蒜客 A1607 UVALive 8512 [ACM-ICPC 2017 Asia Xi'an]XOR
ICPC官网题面假的,要下载PDF,点了提交还找不到结果在哪看(我没找到),用VJ交还直接return 0;也能AC 计蒜客题面 这个好 Time limit 3000 ms OS Linux 题目来 ...
- Mybatis学习笔记之---多表查询(2)
Mybatis多表查询(2) (一)举例 用户和角色 一个用户可以有多个角色,一个角色可以赋予多个用户 (二)步骤 1.建立两张表:用户表,角色表,让用户表和角色表具有多对多的关系.需要使用中间表,中 ...
- Shell 变量/echo命令
Shell 教程 Shell 是一个用C语言编写的程序,它是用户使用Linux的桥梁.Shell既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序,这个应用程序提供了一个界面,用户 ...
- Java数据结构之排序---选择排序
简单选择排序的介绍: 从给定的序列中,按照指定的规则选出某一个元素,再根据规定交换位置后达到有序的目的. 简单选择排序的基本思想: 假定我们的数组为int [] arr = new int[n],第一 ...
- 说下Java堆空间结构,及常用的jvm内存分析命令和工具
Java堆空间结构图:http://www.cnblogs.com/SaraMoring/p/5713732.html JVM内存状况查看方法和分析工具: http://blog.csdn.net/n ...
- 微信小程序 API 界面 (2)
由于每个 API 参数:对象的属性都有 success,fail,complete,所以在这个提前介绍,就不再每个API 上写了 success:类型 函数 接口调用成功的回调函数 fail:类型 函 ...
- LeetCode_1116.打印零与奇偶数(多线程)
LeetCode_1116 LeetCode-1116.打印零与奇偶数 假设有这么一个类: class ZeroEvenOdd { public ZeroEvenOdd(int n) { ... } ...
- 阶段3 1.Mybatis_11.Mybatis的缓存_8 mybatis的二级缓存
二级缓存: 它指的是Mybatis中SqlSessionFactory对象的缓存.由同一个SqlSessionFactory对象创建的SqlSession共享其缓存. ...