通過編寫串口助手工具學習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; }
