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