4
votes

I'm looking for a programmatic way to get the version number of a driver. I want the same number that device manager shows in the driver properties for a device.

Background: I have an application that talks to some custom hardware. The device driver for the custom hardware has known bugs before a certain version number. I want the application to check the driver version and warn the user if they need to update it. The application runs on Windows XP and 7 and is written in C++.

A previous hack I used was to read the .sys file directly from system32/drivers and search for "FileVersion" directly. This is bad for many reasons. In particular it seems to need admin privileges on Windows 7.

I know the class GUID and the hardware ID (ie "USB\VID_1234&PID_5678").

The application currently uses SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces and then SetupDiGetDeviceInterfaceDetail to get the "DevicePath". It then calls CreateFile with that path to talk to the driver.

It looks like I need to get a SP_DRVINFO_DATA structure from somewhere. I've tried various functions from setupapi.h, such as SetupDiGetDeviceInterfaceDetail. Here's some code I've tried that fails:

int main(void)
{
    HDEVINFO DeviceInfoSet = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_USBSPI, NULL, NULL,
        DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

    SP_INTERFACE_DEVICE_DATA InterfaceDeviceData;
    InterfaceDeviceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);

    // Cycle through all devices.
    for (int i = 0; i < 32; i++)
    {
        if (!SetupDiEnumDeviceInterfaces(DeviceInfoSet, 0, (LPGUID)&GUID_DEVINTERFACE_USBSPI, i, &InterfaceDeviceData))
            break;

        PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData;
        DWORD RequiredSize;

        SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, &InterfaceDeviceData, NULL, 0, &RequiredSize, NULL);
        DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, RequiredSize); 
        try
        {
            DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
            SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, &InterfaceDeviceData, DeviceInterfaceDetailData, RequiredSize, NULL, NULL);

            // Try to get the driver info. This part always fails with code 
            // 259 (ERROR_NO_MORE_ITEMS).
            SP_DRVINFO_DATA drvInfo;
            drvInfo.cbSize = sizeof(SP_DRVINFO_DATA);
            if (!SetupDiEnumDriverInfo(DeviceInfoSet, NULL, SPDIT_CLASSDRIVER, i, &drvInfo))
                printf("error = %d\n", GetLastError());

            printf("Driver version is %08x %08x\n", drvInfo.DriverVersion >> 32, drvInfo.DriverVersion & 0xffffffff);
        }
        catch(...)
        {
            HeapFree(GetProcessHeap(), 0, DeviceInterfaceDetailData);
            throw;
        }
        HeapFree(GetProcessHeap(), 0, DeviceInterfaceDetailData);
    }

    SetupDiDestroyDeviceInfoList(DeviceInfoSet);

    return 0;
}

Edit - My updated code now looks like this:

HDEVINFO devInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USBSPI, NULL, NULL,
                                          DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

// Cycle through all devices.
for (int i = 0; ; i++)
{
    // Get the device info for this device
    SP_DEVINFO_DATA devInfo;
    devInfo.cbSize = sizeof(SP_DEVINFO_DATA);
    if (!SetupDiEnumDeviceInfo(devInfoSet, i, &devInfo))
        break;

    // Get the first info item for this driver
    SP_DRVINFO_DATA drvInfo;
    drvInfo.cbSize = sizeof(SP_DRVINFO_DATA);
    if (!SetupDiEnumDriverInfo(devInfoSet, &devInfo, SPDIT_COMPATDRIVER, 0, &drvInfo))
        printf("err - %d\n", GetLastError()); // Still fails with "no more items"
}

SetupDiDestroyDeviceInfoList(devInfoSet);
2
Have you tried checking the .INF file in `\Windows\INF` ?MSalters
I don't know how to find the filename. It will be oemXXX.inf, where XXX is a three digit number.Andrew Bainbridge

2 Answers

3
votes

You're incorrectly reusing i as index in SetupDiEnumDriverInfo. That should be an inner loop for each driver info element per driver. As a result, you fail to retrieve driver info #0 for device #1.

Still, that doesn't explain why info #0 for device #0 fails. For that, you have to look at the second parameter of SetupDiEnumDriverInfo. That is a SP_DEVINFO_DATA structure for your device, but you leave it set to NULL. That gets you the list of drivers associated with the device class, not the device. I.e. that works for mice and USB sticks, which have class drivers. Your device probably has a vendor-specific driver, so you need the driver for that specific device.

0
votes

As you asked a nearly identical question I post only the link to my answer here:

Why does SetupDiEnumDriverInfo give two version numbers for my driver