通过编写串口助手工具学习MFC过程——自动识别串口的方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过编写串口助手工具学习MFC过程——自动识别串口的方法相关的知识,希望对你有一定的参考价值。
通过编写串口助手工具学习MFC过程
因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉。这次通过做一个串口助手再次熟悉一下MFC,并做了一下记录,以便方便以后查阅。做的过程中多是遇到问题直接百度和谷歌搜索来的,所以很多都是不求甚解,知其然不知其所以然。另外做此工具只是为了熟悉了解,许多功能还没有完善!(开发工具VS2008)
(九)自动识别串口的方法
网上找了一下,找到两个介绍的较详细的,可用的方法,分别用的是“查找windows注册表”和“枚举设备”
1、windows注册表实现 自动识别串口
此方法只能获得端口号不能获取详细信息,和手动打开注册表查看是一样的,获得的名字就是COM1和COM2没有具体详细的信息。
实现Windows系统下自动识别串口需要调用三个Windows API函数,它们是:
1.//主要用于打开注册表串口项
1.1. LONG RegOpenKeyEx(
2.HKEY hKey, //主键,即串口信息存放的文件夹
3.LPCTSTR lpSubKey, //子键,串口所在的具体文件夹
4.DWORD ulOptions, //保留值,不用管,必须设置为0
5.REGSAM samDesired, //访问权限
6.PHKEY phkResult //返回的串口句柄,以下两个函数使用
7.);
1.//主要用于获得在当前串口注册表中有多少个串口
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.);
1.//主要用于获得串口名,如"COM3"等
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.);
自动识别串口的实现:
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.}
在主函数中的调用:
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复选框