通过编写串口助手工具学习MFC过程——自动识别串口的方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过编写串口助手工具学习MFC过程——自动识别串口的方法相关的知识,希望对你有一定的参考价值。

通过编写串口助手工具学习MFC过程

因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉。这次通过做一个串口助手再次熟悉一下MFC,并做了一下记录,以便方便以后查阅。做的过程中多是遇到问题直接百度和谷歌搜索来的,所以很多都是不求甚解,知其然不知其所以然。另外做此工具只是为了熟悉了解,许多功能还没有完善!(开发工具VS2008)

(九)自动识别串口的方法

网上找了一下,找到两个介绍的较详细的,可用的方法,分别用的是“查找windows注册表”和“枚举设备”

1、windows注册表实现 自动识别串口

此方法只能获得端口号不能获取详细信息,和手动打开注册表查看是一样的,获得的名字就是COM1和COM2没有具体详细的信息。

技术分享

实现Windows系统下自动识别串口需要调用三个Windows API函数,它们是:

view sourceprint?

1.//主要用于打开注册表串口项

view sourceprint?

1.1. LONG RegOpenKeyEx(

2.HKEY hKey,         //主键,即串口信息存放的文件夹

3.LPCTSTR lpSubKey,      //子键,串口所在的具体文件夹

4.DWORD ulOptions,       //保留值,不用管,必须设置为0

5.REGSAM samDesired,     //访问权限

6.PHKEY phkResult        //返回的串口句柄,以下两个函数使用

7.);

view sourceprint?

1.//主要用于获得在当前串口注册表中有多少个串口

view sourceprint?

01.2. LONG RegQueryInfoKey(

02.HKEY hKey,          //RegOpenKeyEx的五个参数返回的子键句柄

03.LPTSTR lpClass,         //NULL

04.LPDWORD lpcClass,       //NULL

05.LPDWORD lpReserved,     //NULL

06.LPDWORD lpcSubKeys,     //子键的数量

07.LPDWORD lpcMaxSubKeyLen,    //最大子键的长度

08.LPDWORD lpcMaxClassLen,     //NULL

09.LPDWORD lpcValues,      //串口的数量

10.LPDWORD lpcMaxValueNameLen, //最大值名的长度

11.LPDWORD lpcMaxValueLen,     //最大串口的长度

12.LPDWORD lpcbSecurityDescriptor, //NULL

13.PFILETIME lpftLastWriteTime //NULL

14.);

view sourceprint?

1.//主要用于获得串口名,如"COM3"等

view sourceprint?

01.3. LONG RegEnumValue(

02.HKEY hKey,             //串口子键句柄

03.DWORD dwIndex,         //在注册表中的索引

04.LPTSTR lpValueName,        //值名

05.LPDWORD lpcValueName,      //值名的长度

06.LPDWORD lpReserved,        //NULL

07.LPDWORD lpType,            //串口的数据类型

08.LPBYTE lpData,         //串口名

09.LPDWORD lpcbData           //串口名的长度

10.);

自动识别串口的实现:

view sourceprint?

001.struct UartInfo

002.{

003.DWORD UartNum;

004.WCHAR UartName[20];

005.};

006.

007.//获取串口列表

008.BOOL EnumComs(struct UartInfo **UartCom, LPDWORD UartComNumber, CnuprogDlg *pMainDlg)

009.{

010.//LPCTSTR 即const char *

011.

012.*UartComNumber = 0;

013.HKEY hNewKey;

014.LONG lResult=RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"HARDWARE\\\\DEVICEMAP\\\\SERIALCOMM", 0, KEY_ALL_ACCESS, &hNewKey);  

015.if(lResult != ERROR_SUCCESS) 

016.{  

017.pMainDlg->AddToInfOut(_T("打开COM注册表失败!!!"),1,1);

018.return FALSE;  

019.}

020.else

021.{

022.pMainDlg->AddToInfOut(_T("打开COM注册表成功!!!"),1,1);

023.}

024.

025.//DWORD即unsigned long

026.DWORD ValuesNumber;

027.DWORD MaxValueNameLen;

028.DWORD MaxValueLen;

029.CString str;

030.//检索指定的子键下有多少个值项

031.lResult = RegQueryInfoKey(

032.hNewKey,

033.NULL,

034.NULL,

035.NULL,

036.NULL,

037.NULL,

038.NULL,

039.&ValuesNumber,

040.&MaxValueNameLen,

041.&MaxValueLen,

042.NULL,

043.NULL

044.);

045.if(lResult != ERROR_SUCCESS)

046.{

047.RegCloseKey(hNewKey);

048.//pMainDlg->AddToInfOut(_T("检索连接在PC上的串口数量失败!!!"),1,1);

049.return FALSE;

050.}

051.else

052.{

053.// str.Format(_T("连接在PC上的串口数量是:%ld"), ValuesNumber);

054.// pMainDlg->AddToInfOut(str,1,1);

055.*UartCom =(struct UartInfo *)malloc( ValuesNumber * sizeof(struct UartInfo));

056.}

057.

058.DWORD index;

059.DWORD uartindex = 0;

060.//CHAR  ValueName[MAX_VALUE_NAME];

061.WCHAR  ValueName[100];

062.//DWORD ValueNameSize = MAX_VALUE_NAME;

063.DWORD ValueNameSize;

064.DWORD DataType;

065.BYTE DataBuffer[100];

066.DWORD DataLen = 100;

067.

068.//LPTSTR 即 char *, LPBYTE即 char *

069.//检索每个值项,获取值名,数据类型,数据

070.for(index = 0; index < ValuesNumber; index++)

071.{

072.memset(ValueName, 0, sizeof(ValueName));

073.memset(DataBuffer, 0, sizeof(DataBuffer));

074.ValueNameSize = 100;

075.DataLen = 100;

076.lResult = RegEnumValue(hNewKey,index,ValueName,&ValueNameSize,NULL, &DataType, DataBuffer, &DataLen);

077.if (lResult == ERROR_SUCCESS )

078.{

079.switch(DataType)

080.{

081.case REG_NONE:          // No value type               (0)

082.break;

083.case REG_SZ:           //Unicode nul terminated string (1)

084.break;

085.case REG_EXPAND_SZ:    // Unicode nul terminated string (2)

086.break;

087.case REG_BINARY:       // Free form binary              (3)

088.break;

089.case REG_DWORD:        // 32-bit number                (4)

090.break;

091.case REG_MULTI_SZ:      // Multiple Unicode strings    (7)

092.break;

093.default:

094.break;

095.}

096.memcpy((*UartCom)[uartindex].UartName, DataBuffer, DataLen);

097.(*UartCom)[uartindex].UartNum = ValuesNumber;

098.uartindex++;

099.}

100.else if(lResult == ERROR_NO_MORE_ITEMS)

101.{

102.//pMainDlg->AddToInfOut(_T("检索串口完毕!!!"),1,1);

103.}

104.else

105.{

106.DWORD dw = GetLastError();

107.// str.Format(_T("检索串口出错: 0x%08x"), dw);

108.// pMainDlg->AddToInfOut(str,1,1);

109.return FALSE;

110.}

111.}

112.

113.*UartComNumber = uartindex;

114.

115.return TRUE;

116.}

在主函数中的调用:

view sourceprint?

01.DWORD UartComNumber = 0;

02.struct UartInfo *pUartCom;

03.BOOL bResult;

04.bResult = EnumComs(&pUartCom, &UartComNumber, pMainDlg);

05.DWORD index;

06.

07.if(bResult)

08.{

09.pMainDlg->AddToInfOut(_T("获取串口列表成功"),1,1);

10.}

11.else

12.{

13.pMainDlg->AddToInfOut(_T("获取串口列表失败"),1,1);

14.}

15.

16.for( index= 0;index < UartComNumber; index++)

17.{ 

18.pMainDlg->m_ComboBox.AddString(pUartCom[index].UartName);

19.}

2、使用玫举方法,自动识别串口

这个方法可以得到更详细的信息

技术分享

实现方法如下:

BOOL findCom()

{

HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;

SP_DEVINFO_DATA spdata = {0};

GUID guid = GUID_DEVINTERFACE_COMPORT;

// empty();

// 得到所有设备HDEVINFO   

hDevInfo = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);

if(hDevInfo == INVALID_HANDLE_VALUE){

return FALSE;

}

spdata.cbSize = sizeof(spdata);

// 循环列举

for(int i=0; SetupDiEnumDeviceInfo(hDevInfo, i, &spdata); i++){

char buff[1024] = {0};

// 获取详细信息

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 + 4);

//if(p != buff) *(p-1) = ‘\\0‘;

//add(c_comport(id, buff));

}

}

}

//  //  释放

SetupDiDestroyDeviceInfoList(hDevInfo);

return TRUE;

}

整个获取的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编程的时候,有些时候需要枚举设备,例如光盘,光驱,硬盘等等,

我们如何实现功能呢,请见代码分析

1. #include <windows.h>

2. #include <setupapi.h>

3. #include <stdio.h>

4. #include <devguid.h>

5. #include <regstr.h>

6. /* 函数声明 */

7. BOOL EnumPresentDevice( const GUID * InterfaceClassGuid );  

8. BOOL EnumAllDevice();  

9. /*************************************

10. * BOOL EnumClassDevice( const GUID * InterfaceClassGuid )

11. * 功能    根据类型列举当前存在的设备

12. * 参数    InterfaceClassGuid,所需列举设备接口类的GUID

13. **************************************/

14. BOOL EnumClassDevice( const GUID * InterfaceClassGuid )  

15.

16. {  

17.     HDEVINFO DeviceInfoSet;  

18.     HDEVINFO NewDeviceInfoSet;  

19.

20.     SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;  

21.     PSP_DEVICE_INTERFACE_DETAIL_DATA lpDeviceInterfaceDetailData;  

22.

23. DWORD dwBufferSize = 0;  

24. DWORD i;  

25. // 创建空设备信息列表

26.     DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL);  

27.

28. if(DeviceInfoSet == INVALID_HANDLE_VALUE)   

29.     {  

30.         printf("CreateDeviceInfoList failed: %d\\n", GetLastError());  

31. return 0;  

32.     }  

33.

34. // 根据接口类型获得新的设备信息列表

35.

36.     NewDeviceInfoSet = SetupDiGetClassDevsEx(  

37.         InterfaceClassGuid,  

38.         NULL,  

39.         NULL,  

40.         DIGCF_PRESENT | DIGCF_DEVICEINTERFACE,  

41.         DeviceInfoSet,// 之前创建的设备信息列表

42.         NULL,  

43.         NULL  

44.         );  

45. if(NewDeviceInfoSet == INVALID_HANDLE_VALUE)  

46.     {  

47.         printf( "SetupDiGetClassDevsEx failed: %d\\n", GetLastError() );  

48. return 0;  

49.     }  

50. // 设置 SP_DEVICE_INTERFACE_DATA 大小

51.     DeviceInterfaceData.cbSize   

52.         = sizeof(SP_DEVICE_INTERFACE_DATA);  

53.

54. for (i=0; ;i++)  

55.     {  

56. // 列举接口信息

57. BOOL bResult = SetupDiEnumDeviceInterfaces(  

58.             NewDeviceInfoSet,  

59.             NULL,  

60.             InterfaceClassGuid,  

61.             i,  

62.             &DeviceInterfaceData  

63.             );  

64. if(!bResult)  

65.         {  

66. if ( GetLastError()!=NO_ERROR &&  

67.                 GetLastError()!=ERROR_NO_MORE_ITEMS )  

68.             {  

69.                 printf("ERROR: (%d)",GetLastError());  

70. return FALSE;  

71.             }  

72. break;  

73.         }  

74. else

75.         {  

76. // 为PSP_DEVICE_INTERFACE_DETAIL_DATA结构分配内存,填充

77.             lpDeviceInterfaceDetailData = HeapAlloc(  

78.                 GetProcessHeap(), 0,  

79. sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA));  

80.             lpDeviceInterfaceDetailData->cbSize   

81.                 = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);  

82.             dwBufferSize = lpDeviceInterfaceDetailData->cbSize;  

83. // 获得接口详细信息

84. while(!SetupDiGetDeviceInterfaceDetail(  

85.                 NewDeviceInfoSet,  

86.                 &DeviceInterfaceData,  

87.                 lpDeviceInterfaceDetailData,  

88.                 dwBufferSize,  

89.                 &dwBufferSize,  

90.                 NULL))  

91.             {  

92. // 如果内存空间不足,再次分配,直到可以成功调用

93. if(ERROR_INSUFFICIENT_BUFFER==GetLastError())  

94.                 {  

95.                     lpDeviceInterfaceDetailData = HeapReAlloc(  

96.                         GetProcessHeap(), 0,   

97.                         lpDeviceInterfaceDetailData, dwBufferSize);  

98.                     lpDeviceInterfaceDetailData->cbSize   

99.                         = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);  

100.                 }                 

101.             }  

102. // 显示信息

103.             printf("DevicePath: %s\\n",lpDeviceInterfaceDetailData->DevicePath);  

104. // lpDeviceInterfaceDetailData->DevicePath可作为CreateFile的参数,进行IO控制

105.

106. // 释放内存

107.             HeapFree(GetProcessHeap(),0,lpDeviceInterfaceDetailData);  

108.         }  

109.     }  

110.     SetupDiDestroyDeviceInfoList(DeviceInfoSet);  

111. return TRUE;  

112. }  

113. /*************************************

114. * BOOL EnumAllDevice( )

115. * 功能    列举当前存在的设备

116. * 返回值   是否成功

117. **************************************/

118. BOOL EnumAllDevice()  

119. {  

120.     HDEVINFO hDevInfo;  

121.     SP_DEVINFO_DATA DeviceInfoData;  

122. DWORD i;  

123.

124.     printf("Displaying the Installed Devices\\n\\n");  

125.

126. // 得到所有设备 HDEVINFO 

127.     hDevInfo = SetupDiGetClassDevs(NULL,  

128.         0, // 无类型

129.         0, // 无回调函数

130.         DIGCF_PRESENT | DIGCF_ALLCLASSES );  

131. if (hDevInfo == INVALID_HANDLE_VALUE)  

132.     {  

133. return FALSE;  

134.     }  

135. // 循环列举

136.     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);  

137. for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,  

138.         &DeviceInfoData);i++)  

139.     {  

140. DWORD DataT;  

141. LPTSTR buffer = NULL;  

142. DWORD buffersize = 0;  

143.

144. // 获取详细信息

145. while (!SetupDiGetDeviceRegistryProperty(  

146.             hDevInfo,  

147.             &DeviceInfoData,  

148.             SPDRP_DEVICEDESC,  

149.             &DataT,  

150.             (PBYTE)buffer,  

151.             buffersize,  

152.             &buffersize))  

153.         {  

154. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)  

155.             {  

156. // 内存不足

157. if (buffer) HeapFree(GetProcessHeap(), 0, buffer);  

158.                 buffer = (LPTSTR)HeapAlloc(GetProcessHeap(), 0,  buffersize);  

159.             }  

160. else

161. break;  

162.         }  

163. // 输出

164.         printf("GUID:{%.8X-%.4X-%.4X--%.2X%.2X-%.2X%.2X%.2X%.2X%.2X%.2X} "

165. "Device: %s\\n",  

166.             DeviceInfoData.ClassGuid.Data1,  

167.             DeviceInfoData.ClassGuid.Data2,  

168.             DeviceInfoData.ClassGuid.Data3,  

169.             DeviceInfoData.ClassGuid.Data4[0],  

170.             DeviceInfoData.ClassGuid.Data4[1],  

171.             DeviceInfoData.ClassGuid.Data4[2],  

172.             DeviceInfoData.ClassGuid.Data4[3],  

173.             DeviceInfoData.ClassGuid.Data4[4],  

174.             DeviceInfoData.ClassGuid.Data4[5],  

175.             DeviceInfoData.ClassGuid.Data4[6],  

176.             DeviceInfoData.ClassGuid.Data4[7],buffer);  

177.

178. if (buffer) HeapFree(GetProcessHeap(), 0, buffer);  

179.     }  

180.

181. if ( GetLastError()!=NO_ERROR &&  

182.         GetLastError()!=ERROR_NO_MORE_ITEMS )  

183.     {  

184. return FALSE;  

185.     }  

186. //  释放

187.     SetupDiDestroyDeviceInfoList(hDevInfo);  

188. return TRUE;  

189. }  

190.

191. int main( int argc, char *argv[ ], char *envp[ ] )  

192. {  

193. // 列举所有设备

194.     printf("Enumerating All Device\\n\\n");  

195.     EnumAllDevice();  

196. // 列举磁盘分卷驱动器设备

197.     printf("\\n\\nEnumerating Present Volume \\n\\n");  

198.     EnumClassDevice(&GUID_DEVINTERFACE_VOLUME);  

199. return 0;  

200. }  

以上是关于通过编写串口助手工具学习MFC过程——自动识别串口的方法的主要内容,如果未能解决你的问题,请参考以下文章

通过编写串口助手工具学习MFC过程——添加ComboBox组合框

通过编写串口助手工具学习MFC过程——添加Tab Control控件

通过编写串口助手工具学习MFC过程——添加Edit编辑框控件

通过编写串口助手工具学习MFC过程——添加CheckBox复选框

通过编写串口助手工具学习MFC过程——Unicode字符集的宽字符和多字节字符转换

串口助手Python从零开始制作温湿度串口上位机