Monday, August 10, 2009

SetupDi: How To Enumerate Devices Using SetupDiGetClassDevs

I am back to work from my month off for paternity leave with a fresh new post. This time I going to write a about a topic that is directly related to my job, devices. In particular, we will look at how to use SetupDi to enumerate present devices and print out a few properties. There are a lot of other APIs available in Windows to do device enumeration. Perhaps I will cover them in later posts. As you will see in this post, SetupDi’s interfaces aren’t the most conducive to sexy code, but for better or worse, SetupAPI is the main way to work with devices in Windows. If you have any opinion on what a good device API should look like in Windows, please leave a comment and let me know.


Windows Vista on there is another API that is maybe easier to use for this kind of task, Function Discovery. If you are interested, check it out at:

Function Discovery PnP Enumeration Example

To the code…


This code is pretty basic. We create an HDEVINFO set of all present dev nodes, and step through each dev node printing out a few properties. I haven’t really looked at this sample code recently, so let me know if you see any problems.


#include <windows.h>
#include <setupapi.h>
#include <stdio.h>

void print_property
(
__in HDEVINFO hDevInfo,
__in SP_DEVINFO_DATA DeviceInfoData,
__in PCWSTR Label,
__in DWORD Property
)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;

//
// Call function with null to begin with,
// then use the returned buffer size (doubled)
// to Alloc the buffer. Keep calling until
// success or an unknown failure.
//
// Double the returned buffersize to correct
// for underlying legacy CM functions that
// return an incorrect buffersize value on
// DBCS/MBCS systems.
//
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
Property,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
// Change the buffer size.
if (buffer)
{
LocalFree(buffer);
}
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (LPTSTR)LocalAlloc(LPTR, buffersize * 2);
}
else
{
break;
}
}

wprintf(L"%s %s\n",Label, buffer);

if (buffer)
{
LocalFree(buffer);
}
}

//int main(int argc, char *argv[], char *envp[])
int setupdi_version()
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;

// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(
NULL,
0, // Enumerator
0,
DIGCF_PRESENT | DIGCF_ALLCLASSES);

if (INVALID_HANDLE_VALUE == hDevInfo)
{
// Insert error handling here.
return 1;
}

// Enumerate through all devices in Set.

DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++)
{
LPTSTR buffer = NULL;
DWORD buffersize = 0;

print_property(hDevInfo, DeviceInfoData, L"Friendly name :", SPDRP_FRIENDLYNAME);

while (!SetupDiGetDeviceInstanceId(
hDevInfo,
&DeviceInfoData,
buffer,
buffersize,
&buffersize))
{
if (buffer)
{
LocalFree(buffer);
}

if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
// Change the buffer size.
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (LPTSTR)LocalAlloc(LPTR, buffersize * 2);
}
else
{
wprintf(L"error: could not get device instance id (0x%x)\n", GetLastError());
break;
}
}

if (buffer)
{
wprintf(L"\tDeviceInstanceId : %s\n", buffer);
}

print_property(hDevInfo, DeviceInfoData, L"\tClass :", SPDRP_CLASS);
print_property(hDevInfo, DeviceInfoData, L"\tClass GUID :", SPDRP_CLASSGUID);
}


if (NO_ERROR != GetLastError() && ERROR_NO_MORE_ITEMS != GetLastError())
{
// Insert error handling here.
return 1;
}

// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);

return 0;
}

3 comments:

  1. Thanks Sam this was a big help!

    ReplyDelete
  2. Sam, is there a way to determine which devices a process (or application) is using?

    ReplyDelete
    Replies
    1. Here, I wrote a post about it for you:

      http://samscode.blogspot.com/2015/08/how-can-you-find-who-is-using-what.html

      Delete