tags:

views:

1321

answers:

4

I am maintaining an application that uses SetupDiGetDeviceInterfaceDetail() to find out information on the installed serial ports on the computer. I have noticed while testing this that there are some devices, such as my Lucent WinModem, that do not show up in that enumeration. It turns out that I am having a similar issue with a set of devices manufactured by my company that implement the serial port interface. My assumption is that there is something that is missing from the INF file for the device. Does anyone know what kinds of conditions can result in this kind of omission?

Edit: Here is a sample of the code that I am using to enumerate the serial ports. I have tried various combinations of flags but have not seen any significant difference in behaviour.

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  A: 

I am not sure whether following hotfix will solve your problem as mentioned in

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

One more intersting point: GUID_CLASS_COMPORT is obsolete from Win2000 onwards..

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

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

Another site I find having 9 different ways of enumeration. Best of luck.

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

lakshmanaraj
This is a very good lead but, unfortunately does not describe the behaviour that I have encountered. In my case, the potr can be accessed. I simply can't see it in the enumeration.
Jon Trauntvein
I saw the same reference to the GUID and have updated it to match the one currently used to identify serial ports. Since I declared the GUID object myself, I simply kept the same name.
Jon Trauntvein
Ok, added another interesting site
lakshmanaraj
+2  A: 

This is more of a question about the issue. When you call the function the first arg you pass in should be DeviceInfoSet which you likely got from the SetupDiGetClassDevs function. When you called the SetupDiGetClassDevs function what did you specify for the flags (Last Argument) Quoting Microsoft's Page on the function:

DIGCF_ALLCLASSES Return a list of installed devices for all device setup classes or all device interface classes.

DIGCF_DEVICEINTERFACE Return devices that support device interfaces for the specified device interface classes. This flag must be set in the Flags parameter if the Enumerator parameter specifies a device instance ID.

DIGCF_DEFAULT Return only the device that is associated with the system default device interface, if one is set, for the specified device interface classes.

DIGCF_PRESENT Return only devices that are currently present in a system.

DIGCF_PROFILE Return only devices that are a part of the current hardware profile.

Depending on your choice the list of devices changes. For example The Present flag will only show devices plugged in actively.


UPDATE: Thanks for the sample code.

My question now, is if you want to know the friendly name of the modem why not use the same call but specify the Modem Guid instead of the COM Port? I have the Modem GUID being 4D36E96D-E325-11CE-BFC1-08002BE10318

In the registry I can see a value called 'AttachedTo' which specifies a COM Port. I'll have to research which property thats is tied to in the API. The registry key is at

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


ANOTHER UPDATE:

Looking closer at the sample code. Based on this, if you are trying to get the device interface class that should return a SP_DEVICE_INTERFACE_DETAIL_DATA Structure. That wouldn't provide a way of getting the friendly name of the device. I believe instead you would want the device instance.

From what I've read, the Device Interface is used to as a way to get the device path which can be used to write to it.

One thing I did to test your code was try it again the Disk Device Interface. I made a few changes to get it to work on my system and it still isn't quite done. I think the one problem (probably more) is that I need to resize the DevicePath variable inbetween the SetupDiGetDeviceInterfaceDetail calls.

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

Also, in the INF, you may have to add the AddInterface directive to associate your driver with the correct interface.

Rob Haupt
I have added a code sample to show how I am calling the functions. Note that the device that I am trying to detect is "present" when I was conducting these tests.
Jon Trauntvein
Updated based on sample code.
Rob Haupt
The problem is that I expect the device to implement the serial port interface. Its USB port is more of a configuration port than it is an operational port used for modem communications (it is designed as a peripheral to some of our other equipment).
Jon Trauntvein
I should also mention that I have tried to use the DIGCF_ALLCLASSES option and still did not see the device in the resulting enumeration.
Jon Trauntvein
So, in the INF what did you specify for Class and Class Guid? Modem or Ports
Rob Haupt
The INF specifies ports as in the following:Class=PortsClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
Jon Trauntvein
Updated my answer on this.
Rob Haupt
I looked up the AddInterface directive and found that it is not supposed to be needed for "PlugAndPlay" drivers. It further doesn't seem to apply because the device is using an existing interface.
Jon Trauntvein
A: 

You say your device is present and accessible, but are you accessing your device directly or are you accessing a port by name and number COMn:

I have a WinModem that is connected to my audio driver. I have no serial port, not even a simulated one.

Windows programmer
I can open the device as comxx just fine. My code is attempting to find the "friendly name" for the serial port.
Jon Trauntvein
A: 

I decided to punt on this and to do away with the dependency on the SetupDi() functions. Instead, I have written code that traverses the subkeys in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum to find any drivers that support the serial port GUID. I have the feeling that this is what the device manager does. In case anyone is interested, my code fragment can be seen below:

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
Jon Trauntvein
This code is relying on the undocumented implementation of Microsoft's Device Manager. If you want the friendly name of a device you should use the SetupDiEnumDeviceInterfaces
Rob Haupt
Problem is that it doesn't work and I can lay you odds that the device manager doesn't use it either.
Jon Trauntvein