Почему некоторые устройства не перечисляются с помощью SetupDiGetDeviceInterfaceDetail ()? - PullRequest
1 голос
/ 21 февраля 2009

Я поддерживаю приложение, которое использует SetupDiGetDeviceInterfaceDetail () для поиска информации об установленных последовательных портах на компьютере. Во время тестирования я заметил, что есть некоторые устройства, такие как мой Lucent WinModem, которые не отображаются в этом перечислении. Оказывается, у меня похожая проблема с набором устройств, изготовленных моей компанией, которые реализуют интерфейс последовательного порта. Я предполагаю, что в файле INF для устройства чего-то не хватает. Кто-нибудь знает, какие условия могут привести к такому упущению?

Edit: вот пример кода, который я использую для перечисления последовательных портов. Я пробовал различные комбинации флагов, но не видел существенных различий в поведении.

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;
      }
   }
}

Ответы [ 4 ]

4 голосов
/ 24 февраля 2009

Это больше вопрос о проблеме. Когда вы вызываете функцию, первый передаваемый аргумент должен быть DeviceInfoSet, который вы, вероятно, получили от функции SetupDiGetClassDevs . Когда вы вызвали функцию SetupDiGetClassDevs, что вы указали для флагов (последний аргумент), цитируя страницу Microsoft в функции:

DIGCF_ALLCLASSES Возврат списка установленных устройств для всех классов настройки устройства или всех классы интерфейса устройства.

DIGCF_DEVICEINTERFACE Возврат устройств, которые поддерживают интерфейсы устройств для указанного устройства интерфейсные классы. Этот флаг должен быть установить в параметре Flags, если Параметр Enumerator указывает идентификатор экземпляра устройства.

DIGCF_DEFAULT Возврат только устройства, связанного с системой по умолчанию интерфейс устройства, если он установлен, для указанный интерфейс устройства классы.

DIGCF_PRESENT Возврат только тех устройств, которые в данный момент присутствуют в системе.

DIGCF_PROFILE Возврат только тех устройств, которые являются частью текущего профиля оборудования.

В зависимости от вашего выбора список устройств меняется. Например, флаг Present будет отображать только устройства, подключенные активно.


ОБНОВЛЕНИЕ: Спасибо за пример кода.

Мой вопрос сейчас: если вы хотите знать понятное имя модема, почему бы не использовать тот же вызов, но указать модемный гид вместо COM-порта? У меня есть GUID модема 4D36E96D-E325-11CE-BFC1-08002BE10318

В реестре я вижу значение AttachedTo, которое указывает COM-порт. Я должен исследовать, к какому свойству это привязано в API. Раздел реестра находится на

HKLM \ SYSTEM \ CurrentControlSet \ Control \ Class {4D36E96D-E325-11CE-BFC1-08002BE10318} \


ДРУГОЕ ОБНОВЛЕНИЕ:

Присмотритесь к примеру кода. Исходя из этого, если вы пытаетесь получить класс интерфейса устройства, который должен вернуть структуру SP_DEVICE_INTERFACE_DETAIL_DATA . Это не обеспечит способ получить понятное название устройства. Я считаю, что вместо этого вы хотели бы экземпляр устройства.

Из того, что я прочитал, интерфейс устройства используется как способ получения пути к устройству, который можно использовать для записи в него.

Одна вещь, которую я сделал, чтобы проверить ваш код, - это повторить попытку Disk Device Interface. Я сделал несколько изменений, чтобы заставить его работать в моей системе, и это все еще не совсем сделано. Я думаю, что единственная проблема (возможно, больше) заключается в том, что мне нужно изменить размер переменной DevicePath между вызовами SetupDiGetDeviceInterfaceDetail.

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 , чтобы связать драйвер с правильным интерфейсом.

1 голос
/ 23 февраля 2009

Я не уверен, что следующее исправление решит вашу проблему, как указано в

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 голосов
/ 28 февраля 2009

Я решил разобраться с этим и покончить с зависимостью от функций 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
0 голосов
/ 25 февраля 2009

Вы говорите, что ваше устройство присутствует и доступно, но обращаетесь к нему напрямую или к порту по имени и номеру COMn:

У меня есть WinModem, который подключен к моему аудиодрайверу. У меня нет последовательного порта, даже имитированного.

...