2009-02-20 96 views
1

我正在維護使用SetupDiGetDeviceInterfaceDetail()查找有關計算機上安裝的串行端口的信息的應用程序。我在測試時注意到有一些設備,比如我的Lucent WinModem,沒有在枚舉中顯示。事實證明,我與我的公司製造的一組實現串行端口接口的設備有類似的問題。我的假設是,該設備的INF文件中缺少某些內容。有誰知道哪種情況會導致這種遺漏?爲什麼有些設備不能用SetupDiGetDeviceInterfaceDetail()枚舉?

編輯:這裏是我用來枚舉串行端口的代碼示例。我嘗試過各種標誌組合,但在行爲方面沒有發現任何顯着差異。

DEFINE_GUID(GUID_CLASS_COMPORT, 0x4d36e978, 0xe325, 0x11ce, 0xbf, 0xc1, \ 
      0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18); 


GUID *serial_port_guid = const_cast<GUID *>(&GUID_CLASS_COMPORT); 
HDEVINFO device_info = INVALID_HANDLE_VALUE; 
SP_DEVICE_INTERFACE_DETAIL_DATA *detail_data = 0; 

device_info = SetupDiGetClassDevs(
    serial_port_guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 
if(device_info != INVALID_HANDLE_VALUE) 
{ 
    uint4 const detail_data_size = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256; 
    detail_data = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA *>(new char[detail_data_size]); 
    SP_DEVICE_INTERFACE_DATA ifc_data; 
    bool more_interfaces = true; 
    int rcd; 
    memset(&ifc_data, 0, sizeof(ifc_data)); 
    memset(detail_data, 0, detail_data_size); 
    ifc_data.cbSize = sizeof(ifc_data); 
    detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 
    for(uint4 index = 0; more_interfaces; ++index) 
    { 
     rcd = SetupDiEnumDeviceInterfaces(device_info, 0, serial_port_guid, index, &ifc_data); 
     if(rcd) 
     { 
     // we need to get the details of this device 
     SP_DEVINFO_DATA device_data = { sizeof(SP_DEVINFO_DATA) }; 
     rcd = SetupDiGetDeviceInterfaceDetail(
      device_info, &ifc_data, detail_data, detail_data_size, 0, &device_data); 
     if(rcd) 
     { 
      StrAsc device_path(detail_data->DevicePath); 
      byte friendly_name[256]; 

      rcd = SetupDiGetDeviceRegistryProperty(
       device_info, &device_data, SPDRP_FRIENDLYNAME, 0, friendly_name, sizeof(friendly_name), 0); 
      if(rcd) 
      { 
       std::for_each(
        port_names.begin(), 
        port_names.end(), 
        update_friendly_name(
        reinterpret_cast<char const *>(friendly_name))); 
      } 
     } 
     else 
      more_interfaces = false; 
     } 
    } 
} 

回答

-1

我決定踢這個,並取消對SetupDi()函數的依賴。相反,我編寫了遍歷HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Enum中子項的代碼,以查找任何支持串口GUID的驅動程序。我有這種感覺,這是設備管理器所做的。如果有人有興趣,我的代碼片段可以看到如下:

typedef std::string StrAsc; 
typedef std::pair<StrAsc, StrAsc> port_name_type; 
typedef std::list<port_name_type> friendly_names_type; 
void SerialPortBase::list_ports_friendly(friendly_names_type &port_names) 
{ 
    // we will first get the list of names. This will ensure that, at the very least, we get 
    // the same list of names as we would have otherwise obtained. 
    port_names_type simple_list; 
    list_ports(simple_list); 
    port_names.clear(); 
    for(port_names_type::iterator pi = simple_list.begin(); pi != simple_list.end(); ++pi) 
     port_names.push_back(friendly_name_type(*pi, *pi)); 

    // we will now need to enumerate the subkeys of the Enum registry key. We will need to 
    // consider many levels of the registry key structure in doing this so we will use a list 
    // of key handles as a stack. 
    HKEY enum_key ; 
    char const enum_key_name[] = "SYSTEM\\CurrentControlSet\\Enum"; 
    StrAsc const com_port_guid("{4d36e978-e325-11ce-bfc1-08002be10318}"); 
    char const class_guid_name[] = "ClassGUID"; 
    char const friendly_name_name[] = "FriendlyName"; 
    char const device_parameters_name[] = "Device Parameters"; 
    char const port_name_name[] = "PortName"; 
    long rcd = ::RegOpenKeyEx(
     HKEY_LOCAL_MACHINE, enum_key_name, 0, KEY_READ, &enum_key); 
    char value_buff[MAX_PATH]; 
    StrAsc port_name, friendly_name; 

    if(!port_names.empty() && rcd == ERROR_SUCCESS) 
    { 
     std::list<HKEY> key_stack; 
     key_stack.push_back(enum_key); 
     while(!key_stack.empty()) 
     { 
     // we need to determine whether this key has a "ClassGUID" value 
     HKEY current = key_stack.front(); 
     uint4 value_buff_len = sizeof(value_buff); 
     key_stack.pop_front(); 
     rcd = ::RegQueryValueEx(
      current, 
      class_guid_name, 
      0, 
      0, 
      reinterpret_cast<byte *>(value_buff), 
      &value_buff_len); 
     if(rcd == ERROR_SUCCESS) 
     { 
      // we will only consider devices that match the com port GUID 
      if(com_port_guid == value_buff) 
      { 
       // this key appears to identify a com port. We will need to get the friendly name 
       // and try to get the 'PortName' from the 'Device Parameters' subkey. Once we 
       // have those things, we can update the friendly name in our original list 
       value_buff_len = sizeof(value_buff); 
       rcd = ::RegQueryValueEx(
        current, 
        friendly_name_name, 
        0, 
        0, 
        reinterpret_cast<byte *>(value_buff), 
        &value_buff_len); 
       if(rcd == ERROR_SUCCESS) 
       { 
        HKEY device_parameters_key; 
        rcd = ::RegOpenKeyEx(
        current, 
        device_parameters_name, 
        0, 
        KEY_READ, 
        &device_parameters_key); 
        if(rcd == ERROR_SUCCESS) 
        { 
        friendly_name = value_buff; 
        value_buff_len = sizeof(value_buff); 
        rcd = ::RegQueryValueEx(
         device_parameters_key, 
         port_name_name, 
         0, 
         0, 
         reinterpret_cast<byte *>(value_buff), 
         &value_buff_len); 
        if(rcd == ERROR_SUCCESS) 
        { 
         friendly_names_type::iterator fi; 
         port_name = value_buff; 
         fi = std::find_if(
          port_names.begin(), port_names.end(), port_has_name(port_name)); 
         if(fi != port_names.end()) 
          fi->second = friendly_name; 
        } 
        ::RegCloseKey(device_parameters_key); 
        } 
       } 
      } 
     } 
     else 
     { 
      // since this key did not have what we expected, we will need to check its 
      // children 
      uint4 index = 0; 
      rcd = ERROR_SUCCESS; 
      while(rcd == ERROR_SUCCESS) 
      { 
       value_buff_len = sizeof(value_buff); 
       rcd = ::RegEnumKeyEx(
        current, index, value_buff, &value_buff_len, 0, 0, 0, 0); 
       if(rcd == ERROR_SUCCESS) 
       { 
        HKEY child; 
        rcd = ::RegOpenKeyEx(current, value_buff, 0, KEY_READ, &child); 
        if(rcd == ERROR_SUCCESS) 
        key_stack.push_back(child); 
       } 
       ++index; 
      } 
     } 
     ::RegCloseKey(current); 
     } 
    } 
} // list_ports_friendly 
1

我不知道是否在

http://support.microsoft.com/kb/327868

還有一個野趣一點提到以下修補程序將解決你的問題:GUID_CLASS_COMPORT是從Win2000的開始過時..

http://msdn.microsoft.com/en-us/library/bb663140.aspx

http://msdn.microsoft.com/en-us/library/bb663174.aspx

我發現另一個網站有9種不同的枚舉方式。祝你好運。

http://www.naughter.com/enumser.html

+0

這是一個很好的領導,但不幸沒有描述我遇到的行爲。在我的情況下,potr可以被訪問。我在枚舉中根本看不到它。 – 2009-02-23 12:29:51

+0

我看到了與GUID相同的參考,並將其更新爲與當前用於識別串行端口的參考相匹配。由於我自己聲明瞭GUID對象,因此我只保留相同的名稱。 – 2009-02-25 14:54:01

+0

好的,增加了另一個有趣的網站 – lakshmanaraj 2009-02-26 04:18:51

4

這更多的是對這個問題問題。當你調用函數時,你傳遞的第一個參數應該是DeviceInfoSet,你可能從SetupDiGetClassDevs函數中獲得了。當被叫SetupDiGetClassDevs功能是什麼?你的標誌指定(最後一個參數)引用微軟的頁面上的功能:

DIGCF_ALLCLASSES 返回所有的設備安裝程序類或已安裝的設備列表中的所有 設備接口類。

DIGCF_DEVICEINTERFACE 支持用於指定設備 接口類設備接口返回裝置。如果 Enumerator參數指定 設備實例ID,則此標誌必須在Flags參數中設置爲 。

DIGCF_DEFAULT 僅返回與該系統默認 設備接口相關聯,如果一個被設定爲 指定的設備接口 類設備。

DIGCF_PRESENT 僅返回當前存在於系統中的設備。

DIGCF_PROFILE 僅返回屬於當前硬件配置文件一部分的設備。

根據您的選擇,設備列表發生變化。例如,「當前」標誌只會顯示主動插入的設備。


更新:感謝您的示例代碼。

我現在的問題是,如果你想知道調制解調器的友好名稱,爲什麼不使用相同的調用,但指定調制解調器Guid而不是COM端口?我有調制解調器GUID爲4D36E96D-E325-11CE-BFC1-08002BE10318

在註冊表中,我可以看到一個名爲'AttachedTo'的值,它指定了一個COM端口。我將不得不研究在API中綁定哪些屬性。該註冊表項是在

HKLM \系統\ CurrentControlSet \控制\類{4D36E96D-E325-11CE-BFC1-08002BE10318} \


另一個更新:

在研究樣本代碼越接近。基於此,如果您嘗試獲取應返回SP_DEVICE_INTERFACE_DETAIL_DATA結構的設備接口類。這不會提供獲取設備友好名稱的方法。我相信你會想要設備實例。

從我讀到的設備接口被用來獲取可用於寫入設備路徑的設備路徑。

我做了一件事來測試你的代碼是再次嘗試它的磁盤設備接口。我做了一些改變,讓它在我的系統上運行,但仍然沒有完成。我認爲一個問題(可能更多)是我需要在SetupDiGetDeviceInterfaceDetail調用之間調整DevicePath變量的大小。

void Test() 
{ 

GUID *serial_port_guid = const_cast<GUID *>(&GUID_DEVINTERFACE_DISK); 
HDEVINFO device_info = INVALID_HANDLE_VALUE; 
SP_DEVICE_INTERFACE_DETAIL_DATA detail_data; 

device_info = SetupDiGetClassDevs(
    serial_port_guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 
if(device_info != INVALID_HANDLE_VALUE) 
{ 
    //uint4 const detail_data_size = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);// + 256; 
    //detail_data = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA *>(new char[detail_data_size]); 
    SP_DEVICE_INTERFACE_DATA ifc_data; 
    bool more_interfaces = true; 
    int rcd; 
    memset(&ifc_data, 0, sizeof(ifc_data)); 
    //memset(detail_data, 0, detail_data_size); 
    ifc_data.cbSize = sizeof(ifc_data); 
    detail_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 
    for(uint4 index = 0; more_interfaces; ++index) 
    { 
     rcd = SetupDiEnumDeviceInterfaces(device_info, 0, serial_port_guid, index, &ifc_data); 
     if(rcd) 
     { 
     // we need to get the details of this device 
     SP_DEVINFO_DATA device_data; 
     device_data.cbSize = sizeof(SP_DEVINFO_DATA); 
     DWORD intReqSize; 
     rcd = SetupDiGetDeviceInterfaceDetail(device_info, &ifc_data, 0, 0, &intReqSize, &device_data); 

     rcd = SetupDiGetDeviceInterfaceDetail(device_info, &ifc_data, &detail_data,intReqSize,&intReqSize,&device_data); 
     if(rcd) 
     { 
      //StrAsc device_path(detail_data->DevicePath); 
      byte friendly_name[256]; 

      rcd = SetupDiGetDeviceRegistryProperty(
       device_info, &device_data, SPDRP_FRIENDLYNAME, 0, friendly_name, sizeof(friendly_name), reinterpret_cast<DWORD *>(sizeof(friendly_name))); 
      if(rcd) 
      { 
       cout<<reinterpret_cast<char const *>(friendly_name); 
      } 
      else 
      { int num = GetLastError(); 
      } 
     } 
     else 
     { 
       int num = GetLastError(); 
      } 
     } 
     else 
      more_interfaces = false; 
    }  
} 
SetupDiDestroyDeviceInfoList(device_info); 
} 

此外,在INF,您可能需要添加AddInterface指令到驅動程序與正確的接口進行關聯。

+0

我已經添加了一個代碼示例來展示我如何調用這些函數。請注意,當我進行這些測試時,我正在嘗試檢測的設備是「存在」的。 – 2009-02-25 05:50:14

0

你說你的設備是存在​​且可訪問,但你直接訪問您的設備或者是你的姓名和電話號碼COMN訪問端口:

我有連接到我的音頻驅動程序的WinModem。我沒有串口,甚至沒有模擬的。