2009-02-20 93 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。我没有串口,甚至没有模拟的。