通过编写串口助手工具学习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[20]; }; //获取串口列表 BOOL EnumComs(struct UartInfo **UartCom, LPDWORD UartComNumber, CnuprogDlg *pMainDlg) { //LPCTSTR 即const char * *UartComNumber = 0; HKEY hNewKey; LONG lResult=RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_ALL_ACCESS, &hNewKey); if(lResult != ERROR_SUCCESS) { pMainDlg->AddToInfOut(_T("打开COM注册表失败!!!"),1,1); return FALSE; } else { pMainDlg->AddToInfOut(_T("打开COM注册表成功!!!"),1,1); } //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 = 0; //CHAR ValueName[MAX_VALUE_NAME]; WCHAR ValueName[100]; //DWORD ValueNameSize = MAX_VALUE_NAME; DWORD ValueNameSize; DWORD DataType; BYTE DataBuffer[100]; DWORD DataLen = 100; //LPTSTR 即 char *, LPBYTE即 char * //检索每个值项,获取值名,数据类型,数据 for(index = 0; index < ValuesNumber; index++) { memset(ValueName, 0, sizeof(ValueName)); memset(DataBuffer, 0, sizeof(DataBuffer)); ValueNameSize = 100; DataLen = 100; 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 = 0; struct UartInfo *pUartCom; BOOL bResult; bResult = EnumComs(&pUartCom, &UartComNumber, pMainDlg); DWORD index; if(bResult) { pMainDlg->AddToInfOut(_T("获取串口列表成功"),1,1); } else { pMainDlg->AddToInfOut(_T("获取串口列表失败"),1,1); } for( index= 0; index < UartComNumber; index++) { pMainDlg->m_ComboBox.AddString(pUartCom[index].UartName); }
二、使用玫举方法,自动识别串口
这个方法可以得到更详细的信息
实现方法如下:

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编程的时候,有些时候需要枚举设备,例如光盘,光驱,硬盘等等,
我们如何实现功能呢,请见代码分析

#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 = 0; DWORD i; // 创建空设备信息列表 DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL); if(DeviceInfoSet == INVALID_HANDLE_VALUE) { printf("CreateDeviceInfoList failed: %d\n", GetLastError()); return 0; } // 根据接口类型获得新的设备信息列表 NewDeviceInfoSet = SetupDiGetClassDevsEx( InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE, DeviceInfoSet,// 之前创建的设备信息列表 NULL, NULL ); if(NewDeviceInfoSet == INVALID_HANDLE_VALUE) { printf( "SetupDiGetClassDevsEx failed: %d\n", GetLastError() ); return 0; } // 设置 SP_DEVICE_INTERFACE_DATA 大小 DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for (i=0; ;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(), 0, 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(), 0, lpDeviceInterfaceDetailData, dwBufferSize); lpDeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); } } // 显示信息 printf("DevicePath: %s\n",lpDeviceInterfaceDetailData->DevicePath); // lpDeviceInterfaceDetailData->DevicePath可作为CreateFile的参数,进行IO控制 // 释放内存 HeapFree(GetProcessHeap(),0,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, 0, // 无类型 0, // 无回调函数 DIGCF_PRESENT | DIGCF_ALLCLASSES ); if (hDevInfo == INVALID_HANDLE_VALUE) { return FALSE; } // 循环列举 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i, &DeviceInfoData);i++) { DWORD DataT; LPTSTR buffer = NULL; DWORD buffersize = 0; // 获取详细信息 while (!SetupDiGetDeviceRegistryProperty( hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC, &DataT, (PBYTE)buffer, buffersize, &buffersize)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // 内存不足 if (buffer) HeapFree(GetProcessHeap(), 0, buffer); buffer = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, 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[0], DeviceInfoData.ClassGuid.Data4[1], DeviceInfoData.ClassGuid.Data4[2], DeviceInfoData.ClassGuid.Data4[3], DeviceInfoData.ClassGuid.Data4[4], DeviceInfoData.ClassGuid.Data4[5], DeviceInfoData.ClassGuid.Data4[6], DeviceInfoData.ClassGuid.Data4[7],buffer); if (buffer) HeapFree(GetProcessHeap(), 0, 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 0; }