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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM