Click here to Skip to main content
15,867,834 members
Articles / Programming Languages / C++
Article

Enumerating windows device

Rate me:
Please Sign up or sign in to vote.
4.66/5 (48 votes)
16 Jun 20068 min read 298.7K   20.2K   133   38
Enumerating the device using the SetupDi* API provided with WinXP

Sample screenshot

Introduction

Windows has a rich collection of APIs to get useful information about installed devices. In this article, I will show how you can enumerate devices on a machine using SetupDiXXX API and CM_XXXX API.

The program has a simple GUI: only a a treeview control that shows all of the installed devices. All the information about a device (such as name and its icon) is grabbed from Windows SetupDiXXX API.

SetupDiXXX API

The Setup application programming interface (API) provides a set of functions that your setup application can call to perform installation operations or get several information about installed devices, their class and also their GUID (a unique identifier for every device).

WINSETUPAPI BOOL WINAPI
  SetupDiGetClassImageList(
    OUT PSP_CLASSIMAGELIST_DATA  ClassImageListData);

The SetupDiGetClassImageList function builds an image list that contains bitmaps for every installed class and returns the list in a data structure.

WINSETUPAPI BOOL WINAPI
  SetupDiDestroyClassImageList(
    IN PSP_CLASSIMAGELIST_DATA  ClassImageListData);

The SetupDiDestroyClassImageList function destroys a class image list that was built by a call to SetupDiGetClassImageList.

WINSETUPAPI BOOL WINAPI
  SetupDiGetClassImageIndex(
    IN PSP_CLASSIMAGELIST_DATA  ClassImageListData,
    IN LPGUID  ClassGuid,
    OUT PINT  ImageIndex);

The SetupDiGetClassImageIndex function retrieves the index within the class image list of a specified class.

*           Get device info set for device class (SetupDiGetClassDevs function) , when first call , the first and second parameters for this function should be set to “<st1:chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="0" unitname="”" w:st="on">0”, the last parameter should be set property DIGCF_ALLCLASSES constant to get all class device.

<o:p> 

HDEVINFO
  SetupDiGetClassDevs(
    IN LPGUID  ClassGuid,  OPTIONAL
    IN PCTSTR  Enumerator,  OPTIONAL
    IN HWND  hwndParent,  OPTIONAL
    IN DWORD  Flags);
 

 

  

hDevInfo = SetupDiGetClassDevs(0L,   //Retrieve all classes
                               0L,  // no enumerator
                               _hDlg, // Parent Windows, usually set to “0”

                        //control options used in building the device information set
                               DIGCF_PRESENT |  
                               DIGCF_ALLCLASSES |
                               DIGCF_PROFILE);

 

Getting the Info<o:p>

 

*           Using (SetupDiEnumDeviceInfo function) to enumeration all class devices.

<o:p> 

WINSETUPAPI BOOL WINAPI
  SetupDiEnumDeviceInfo(
    IN HDEVINFO  DeviceInfoSet,
    IN DWORD  MemberIndex,
    OUT PSP_DEVINFO_DATA  DeviceInfoData);

<o:p> 

<o:p>The function returns TRUE if it is successful. Otherwise, it returns FALSE and the logged error can be retrieved with a call to GetLastError.

 

wIndex = 0;
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
while (1)
{
     if (SetupDiEnumDeviceInfo(hDevInfo,
                               wIndex,
                               &spDevInfoData))
                      .
                      .
};

 

 

The second parameter is Supplies the zero-based index of the device information element to retrieve.

 

The Last parameter is Supplies a pointer to an SP_DEVINFO_DATA structure to receive information about this element. The caller must set cbSize to sizeof(SP_DEVINFO_DATA). 

 

*           Get device name from Registry via SetupDiGetDeviceRegistryPropertyA function

<o:p> 

WINSETUPAPI BOOL WINAPI
  SetupDiGetDeviceRegistryProperty(
    IN HDEVINFO  DeviceInfoSet,
    IN PSP_DEVINFO_DATA  DeviceInfoData,
    IN DWORD  Property,
    OUT PDWORD  PropertyRegDataType,  OPTIONAL
    OUT PBYTE  PropertyBuffer,
    IN DWORD  PropertyBufferSize,
    OUT PDWORD  RequiredSize  OPTIONAL);

 

 

if (!SetupDiGetDeviceRegistryProperty(hDevInfo,
                                      &spDevInfoData,
                                      SPDRP_CLASS, //Retrieve property type,
                                      0L,
                                      (PBYTE)szBuf,
                                      2048,
                                      0)) {
    wIndex++;
    continue;
};

//retrieves the index within the class image list of a specified class

if (SetupDiGetClassImageIndex(&_spImageData,
                              &spDevInfoData.ClassGuid,
                              (int*)&wImageIdx)){

      .
      .

      //retrieves the class description associated with the specified setup class GUID
      if (!SetupDiGetClassDescription(&spDevInfoData.ClassGuid,
                                      szBuf,
                                      MAX_PATH,
                                      &dwRequireSize)){
          .
          .
      };
};

 

 

Get Device Resource<o:p>

 

All devices in the system join in the device classes. As you can see in the below picture, the class has name and GUID (so it can be found in Registry). The class can also have a description. For example, for class "Ports" the description is "Ports (COM & LPT)". Class also has devices that are present in the configuration.

 

Get information about current configuration (CM_Get_First_Log_Conf function).

 

CMAPI CONFIGRET WINAPI
  CM_Get_First_Log_Conf(
    OUT PLOG_CONF  plcLogConf,  OPTIONAL
    IN DEVINST  dnDevInst,
    IN ULONG  ulFlags);

 

Get resource descriptor from current configuration (CM_Get_Next_Res_Des function, do this and follow steps for every resource till they exist)

 

CMAPI CONFIGRET WINAPI
  CM_Get_Next_Res_Des(
    OUT PRES_DES  prdResDes,
    IN RES_DES  rdResDes,
    IN RESOURCEID  ForResource,
    OUT PRESOURCEID  pResourceID,
    IN ULONG  ulFlags);

 

Get information about size of resource data (CM_Get_Res_Des_Data_Size function)

 

CMAPI CONFIGRET WINAPI
  CM_Get_Res_Des_Data_Size(
    OUT PULONG  pulSize,
    IN RES_DES  rdResDes,
    IN ULONG  ulFlags);

 

Get resource data (CM_Get_Res_Des_Data function)

 

CMAPI CONFIGRET WINAPI
  CM_Get_Res_Des_Data(
    IN RES_DES  rdResDes,
    OUT PVOID  Buffer,
    IN ULONG  BufferLen,
    IN ULONG  ulFlags);

 

cmRet = CM_Get_First_Log_Conf(&firstLogConf, DevInst, ALLOC_LOG_CONF);
if (cmRet != CR_SUCCESS) {
    cmRet = CM_Get_First_Log_Conf(&firstLogConf, DevInst, BOOT_LOG_CONF);
    if (cmRet != CR_SUCCESS)  return;
};
cmRet = CM_Get_Next_Res_Des(&nextLogConf,
                            firstLogConf,
                            dwResType,
                            0L,
                            0L);
if (cmRet != CR_SUCCESS){
    CM_Free_Res_Des_Handle(firstLogConf);
    return;
};
while (1) {
    ulSize = 0;
    cmRet = CM_Get_Res_Des_Data_Size(&ulSize, nextLogConf, 0L);
    if (cmRet != CR_SUCCESS) {
        CM_Free_Res_Des_Handle(nextLogConf);
        break;
    };
    pBuf = (char*)LocalAlloc(LPTR, 2048);
    if (!pBuf)  {
        ShowErrorMsg(_hDlg, GetLastError(), "LocalAlloc");
        CM_Free_Res_Des_Handle(nextLogConf);
        break;
    };
    cmRet = CM_Get_Res_Des_Data(nextLogConf, pBuf, ulSize, 0L);
    if (cmRet != CR_SUCCESS) {
       .
       . };
};

 

Load/Unload Non-PNP Driver<o:p>

 

Windows allows loading drivers at runtime using the Service Control Manager. Yes, the Service Control Manager in Windows not only can be used to load and manage services. You can use it with device drivers as you use it to load/unload/start/stop windows services.

 

The OpenSCManager function establishes a connection to the service control manager on the specified computer and opens the specified service control manager database.

 

SC_HANDLE OpenSCManager(
  IN LPCTSTR lpMachineName,
  IN LPCTSTR lpDatabaseName,
  IN DWORD dwDesiredAccess);

<o:p> 

<o:p>The CreateService function creates a service object and installs it in the service control manager database by creating a key with the same name as the service under the following registry key:

<o:p>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services

<o:p> 

<o:p>

SC_HANDLE CreateService(
    IN SC_HANDLE hSCManager,
    IN LPCTSTR lpServiceName,
    IN LPCTSTR lpDisplayName,
    IN DWORD dwDesiredAccess,
    IN DWORD dwServiceType,
    IN DWORD dwStartType,
    IN DWORD dwErrorControl,
    IN LPCTSTR lpBinaryPathName,
    IN LPCTSTR lpLoadOrderGroup,
    IN LPDWORD lpdwTagId,
    IN LPCTSTR lpDependencies,
    IN LPCTSTR lpServiceStartName,
    IN LPCTSTR lpPassword);

<o:p> 

<o:p>The OpenService function opens an existing service.

<o:p> 

<o:p>

SC_HANDLE OpenService(
  IN SC_HANDLE hSCManager,
  IN LPCTSTR lpServiceName,
  IN DWORD dwDesiredAccess);

<o:p> 

<o:p>

hSCManager = OpenSCManager(0L,
                           0L,
                           SC_MANAGER_ALL_ACCESS);
if (hSCManager){  //Create new device service
    hService = CreateService(hSCManager,
                             lpszServiceName,  //DosDeviceName
                             lpszServiceName, //DosDeviceName
                             SERVICE_ALL_ACCESS,
                             SERVICE_KERNEL_DRIVER,
                             wStart,
                             wError,
                             lpszDriverPath,   // Image path
                             0L,
                             0L,
                             0L,
                             0L,
                             0L);
    if (!hService) {
            DWORD dwErrorCode = GetLastError();
            if (dwErrorCode == ERROR_SERVICE_EXISTS) {
                //If service already existed? Open it.
                hService = OpenService(hSCManager, lpszServiceName, SERVICE_ALL_ACCESS);
            }
            else {
                ShowErrorMsg(_hDlg, dwErrorCode, "CreateService");
                return 0;
            };
    };
    if (hService) { // Start new service
        if (!StartService(hService, 0, 0L)) {
            CloseServiceHandle(hService);
            ShowErrorMsg(_hDlg, GetLastError(), "StartService");
            return 0;
        };
    };
    CloseServiceHandle(hSCManager);
};

 

If you want to send/receive data to the device driver loaded, to allow this, you have to get a handle to the driver. For this purpose you can use the function CreateFile, and pass the driver name as the file name. You can see Windows is so abstract in this aspect.

Sample screenshot

Load/Unload WDM Driver<o:p>

Windows also allows loading drivers at runtime using the SetupDiXXX API. The first we need to call SetuiDiGetINFClass to get the class of a specified device INF file. 

Sample screenshot

WINSETUPAPI BOOL WINAPI
  SetupDiGetINFClass(
    IN PCTSTR  InfName,
    OUT LPGUID  ClassGuid,
    OUT PTSTR  ClassName,
    IN DWORD  ClassNameSize,
    OUT PDWORD  RequiredSize  OPTIONAL);

The second parameter, Receives the class GUID for the specified INF file. If the INF file does not specify a class name, this variable is set to GUID_NULL. Call SetupDiClassGuidsFromName to determine if one or more classes with this name are already installed.

When function return TRUE, NOW we need create an empty device information set and optionally associates the set with a device setup class and a top-level window. Use SetupDiCreateDeviceInfo function creates a new device information element and adds it as a new member to the specified device information set.

HDEVINFO
  SetupDiCreateDeviceInfoList(
    IN LPGUID  ClassGuid,  OPTIONAL
    IN HWND  hwndParent  OPTIONAL);

The caller of this function must delete the returned device information set when it is no longer needed by calling SetupDiDestroyDeviceInfoList.
To create a device information list for a remote machine use SetupDiCreateDeviceInfoListEx.

WINSETUPAPI BOOL WINAPI
  SetupDiCreateDeviceInfo(
    IN HDEVINFO  DeviceInfoSet,
    IN PCTSTR  DeviceName,
    IN LPGUID  ClassGuid,
    IN PCTSTR  DeviceDescription,  OPTIONAL
    IN HWND  hwndParent,  OPTIONAL
    IN DWORD  CreationFlags,
    OUT PSP_DEVINFO_DATA  DeviceInfoData  OPTIONAL);

The sixth parameter. Controls how the device information element is created. Can be a combination of the following values:

DICD_GENERATE_ID 
If this flag is specified, DeviceName contains only a Root-enumerated device ID and the system creates a unique device instance key for it. This unique device instance key is generated as:
Enum\Root\DeviceName\InstanceID
where InstanceID is a four-digit, base-10 number that is unique among all subkeys under Enum\Root\DeviceName. Call SetupDiGetDeviceInstanceId to find out what ID was generated for this device information element.

DICD_INHERIT_CLASSDRVS
If this flag is specified, the resulting device information element inherits the class driver list, if any, associated with the device information set. In addition, if there is a selected driver for the device information set, that same driver is selected for the new device information element.

if (!SetupDiGetINFClass(szINFName, &guid, className, MAX_CLASS_NAME_LEN, 0)) {
        return 0;
};
hDevInfo = SetupDiCreateDeviceInfoList(&guid, 0);
if (hDevInfo == (void*)-1) {
        return 0;
};
spDevData.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiCreateDeviceInfo(hDevInfo,
                             className,
                             &guid,
                             0L,
                             0L,
                             DICD_GENERATE_ID,
                             &spDevData)) {
        SetupDiDestroyDeviceInfoList(hDevInfo);
        return 0;
};

If this device instance is being added to a set that has an associated class, the device class must be the same or the call fails. In this case, a call to GetLastError returns ERROR_CLASS_MISMATCH.

If the specified device instance is the same as an existing device instance key in the registry, the call fails. In this case, a call to GetLastError returns ERROR_DEVINST_ALREADY_EXISTS. This occurs only if the DICD_GENERATE_ID flag is not set.

If the new device information element was successfully created but the caller-supplied DeviceInfoData buffer is invalid, the function returns FALSE. In this case, a call to GetLastError returns ERROR_INVALID_USER_BUFFER. However, the device information element will have been added as a new member of the set already.

That's it! If you have any suggestions or problems to report, post them here. There are plenty of ways this idea could be extended if you are of an inclination to do so.<o:p>

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) Shan Hon Co., Ltd.
Taiwan Taiwan
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionthank you. I found your code helpful though it is old Pin
Richard Chambers3-Nov-21 6:50
Richard Chambers3-Nov-21 6:50 
QuestionIs there a way to detect the Driverversion of the Networkcard? Pin
Member 1131303227-Sep-17 7:10
Member 1131303227-Sep-17 7:10 
QuestionGet specified device info Pin
Mr.ExA-MaN22-Nov-14 23:49
Mr.ExA-MaN22-Nov-14 23:49 
QuestionAre you able to read the Fields in the "Details" Field of a Property Tab???? Pin
Member 22176003-Feb-14 2:00
Member 22176003-Feb-14 2:00 
QuestionThis programm does not iterate the video devices correctly Pin
Seikilos20-Sep-13 1:55
Seikilos20-Sep-13 1:55 
QuestionEnumerating Devices Used by Each Process? Pin
Darwin Airola29-May-13 7:48
Darwin Airola29-May-13 7:48 
QuestionWindows 8 issue Pin
Hellmaster66697-Oct-12 23:14
Hellmaster66697-Oct-12 23:14 
AnswerRe: Windows 8 issue Pin
arnoudmulder26-Jan-14 4:00
arnoudmulder26-Jan-14 4:00 
see msdn, if running a 64 bit OS you need to create a 64 bit application and vica versa.

http://msdn.microsoft.com/en-us/library/windows/hardware/ff538522(v=vs.85).aspx[^]
QuestionHow can I get information about devices that were not successfully installed? Pin
debby214-Oct-11 6:11
debby214-Oct-11 6:11 
QuestionThanks! Is there any way to modify device orrder?? Pin
tbfisher23-Jul-11 7:02
tbfisher23-Jul-11 7:02 
GeneralThe regedit's information of LPTIO is deleted when restart PC Pin
Member 790053312-May-11 22:18
Member 790053312-May-11 22:18 
GeneralGetting Device Information Pin
gilmarshow21-Aug-10 3:03
gilmarshow21-Aug-10 3:03 
QuestionCan I get the status of the device Pin
jkally12-Feb-09 1:46
jkally12-Feb-09 1:46 
AnswerRe: Can I get the status of the device Pin
Babybaer27-May-10 19:46
Babybaer27-May-10 19:46 
GeneralRe: Can I get the status of the device Pin
Chuan-Liang Teng30-May-10 5:21
Chuan-Liang Teng30-May-10 5:21 
GeneralLink Error, help me, please Pin
fuqiang_634715-Jan-09 21:57
fuqiang_634715-Jan-09 21:57 
GeneralRe: Link Error, help me, please Pin
gonalo16-Feb-16 4:53
gonalo16-Feb-16 4:53 
GeneralSetupDiCreateDeviceInterface and device setup class GUID Pin
t1234567890098765432116-Dec-08 3:59
t1234567890098765432116-Dec-08 3:59 
GeneralDebug error Pin
jenakumarpradyumna7-Aug-08 0:16
jenakumarpradyumna7-Aug-08 0:16 
AnswerRe: Debug error Pin
Tim McCaffrey20-Nov-08 12:24
Tim McCaffrey20-Nov-08 12:24 
GeneralRe: Debug error Pin
Member 1131303227-Sep-17 7:06
Member 1131303227-Sep-17 7:06 
QuestionEnumerating windows device &gt;&gt; Console-based version? Pin
Destinyii9-Jun-08 16:11
Destinyii9-Jun-08 16:11 
AnswerRe: Enumerating windows device &gt;&gt; Console-based version? Pin
baternest10-Jun-08 2:33
baternest10-Jun-08 2:33 
Questionfatal error C1083: Cannot open include file: 'cfg.h': No such file or directory Pin
Thanks for all the fish22-Mar-08 4:14
Thanks for all the fish22-Mar-08 4:14 
GeneralInteract with Human Interface Devices Pin
IScribe15-Jul-07 15:11
IScribe15-Jul-07 15:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.