7

I am interested to find out if I am doing this right:

//DeviceManager.h
#include <windows.h>
//#include <hidsdi.h>
#include <setupapi.h>
#include <iostream>
#include <cfgmgr32.h>
#include <tchar.h>
#include <devpkey.h>

extern "C"{
    #include <hidsdi.h>
}

//#pragma comment (lib, "setupapi.lib")

class DeviceManager
{
public:
    DeviceManager();
    ~DeviceManager();

    void ListAllDevices();
    void GetDevice(std::string vid, std::string pid);

    HANDLE PSMove;
    byte reportBuffer;
private:
    HDEVINFO deviceInfoSet;             //A list of all the devices
    SP_DEVINFO_DATA deviceInfoData;     //A device from deviceInfoSet

    SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
    SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailedData;

};

//DeviceManager.cpp
#include"DeviceManager.h"

DeviceManager::DeviceManager()
{
    deviceInfoSet = SetupDiGetClassDevs(NULL, TEXT("USB"), NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); //Gets all Devices
}

DeviceManager::~DeviceManager()
{
}

void DeviceManager::ListAllDevices()
{
    DWORD deviceIndex = 0;

    deviceInfoData.cbSize = sizeof(deviceInfoData);

    while(SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, &deviceInfoData))
    {
        deviceInfoData.cbSize = sizeof(deviceInfoData);

        ULONG tcharSize;
        CM_Get_Device_ID_Size(&tcharSize, deviceInfoData.DevInst, 0);
        TCHAR* deviceIDBuffer = new TCHAR[tcharSize];   //the device ID will be stored in this array, so the tcharSize needs to be big enough to hold all the info.
                                                        //Or we can use MAX_DEVICE_ID_LEN, which is 200

        CM_Get_Device_ID(deviceInfoData.DevInst, deviceIDBuffer, MAX_PATH, 0); //gets the devices ID - a long string that looks like a file path.

        /*
        //SetupDiGetDevicePropertyKeys(deviceInfoSet, &deviceInfoData, &devicePropertyKey, NULL, 0, 0);
        if( deviceIDBuffer[8]=='8' && deviceIDBuffer[9]=='8' && deviceIDBuffer[10]=='8' && deviceIDBuffer[11]=='8' && //VID
            deviceIDBuffer[17]=='0' && deviceIDBuffer[18]=='3' && deviceIDBuffer[19]=='0' && deviceIDBuffer[20]=='8') //PID
        {
            std::cout << deviceIDBuffer << "\t<-- Playstation Move" << std::endl;
        }
        else
        {
            std::cout << deviceIDBuffer << std::endl;
        }*/

        std::cout << deviceIDBuffer << std::endl;

        deviceIndex++;
    }
}

void DeviceManager::GetDevice(std::string vid, std::string pid)
{

    DWORD deviceIndex = 0;
    deviceInfoData.cbSize = sizeof(deviceInfoData);

    while(SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, &deviceInfoData))
    {
        deviceInfoData.cbSize = sizeof(deviceInfoData);

        ULONG IDSize;
        CM_Get_Device_ID_Size(&IDSize, deviceInfoData.DevInst, 0);

        TCHAR* deviceID = new TCHAR[IDSize];

        CM_Get_Device_ID(deviceInfoData.DevInst, deviceID, MAX_PATH, 0);

        if( deviceID[8]==vid.at(0) && deviceID[9]==vid.at(1) && deviceID[10]==vid.at(2) && deviceID[11]==vid.at(3) && //VID
            deviceID[17]==pid.at(0) && deviceID[18]==pid.at(1) && deviceID[19]==pid.at(2) && deviceID[20]==pid.at(3)) //PID
        {
            //DWORD requiredBufferSize;
            //SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, NULL, 0, &requiredBufferSize, 


            HDEVINFO deviceInterfaceSet = SetupDiGetClassDevs(&deviceInfoData.ClassGuid, NULL, NULL, DIGCF_ALLCLASSES);

            DWORD deviceInterfaceIndex = 0;
            deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
            while(SetupDiEnumDeviceInterfaces(deviceInterfaceSet, NULL, &deviceInterfaceData.InterfaceClassGuid, deviceInterfaceIndex, &deviceInterfaceData))
            {
                deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
                std::cout << deviceInterfaceIndex << std::endl;

                deviceInterfaceIndex++;
            }

            //std::cout << deviceInterfaceData.cbSize << std::endl;

            break;
        }

        deviceIndex++;
    }
}

My SetupDiEnumDeviceInterfaces (in the GetDevice() function) is not doing anything. It is not even entering the while loop. What is it that I'm doing wrong?

Edit

I've just called the GetLastError() function and it has returned a 259 - ERROR_NO_MORE_ITEMS. Is it even possible for a device to contain no interfaces?

halfer
  • 19,824
  • 17
  • 99
  • 186
Danny
  • 9,199
  • 16
  • 53
  • 75
  • 2
    `SetupDiEnumDeviceInfo` is returning false then. Look at the documentation to determine why this might be and then use your debugger. If you still can't figure it out then ask a more specific question about where you got stuck then. – AJG85 May 14 '12 at 17:09
  • Then May I please ask if the deviceInfoData.ClassGuid been passed correct? I mean, I am suppost to be passing that guid to get the deviceInterfaceSet? Or am I suppost to pass it a different GUID? – Danny May 14 '12 at 17:18
  • 1
    If you never enter the while loop then you never get to that line of code which means you are using the `deviceInfoSet` assigned in the constructor. Hint: Use `FormatMessage` with `GetLastError` to determine specifically what went wrong after the call to `SetupDiEnumDeviceInfo` It could simply be that there are no present active USB devices available on the system. – AJG85 May 14 '12 at 17:31
  • have compiled & run your code without a hitch; eg: `DeviceManager::ListAllDevices` returns for me: `003796F8 00379800` ~i think you have no devices ;) ~best to bung in a check for the return value when you call `SetupDiGetClassDevs` – violet313 May 14 '12 at 18:12
  • @violet313 I've created a DeviceManager object and called the ListAllDevices function and it displayed a list of USB devices' IDs :S... – Danny May 14 '12 at 18:20
  • hmm, well fwiw i was also making it inside the `while` of your `DeviceManager::GetDevice` ~out of interest, what do you think the difference in the the two respective calls to `SetupDiEnumDeviceInfo` may be? are your calling these methods on the same instance? ~is some *other* code *doing something* to your `deviceInfoSet` value? – violet313 May 14 '12 at 18:26
  • @violet313 I am not even using deviceInfoSet for that function :S. I have created another HDEVINFO called deviceInterfaceSet in the GetDevice function. SetupDiEnumDeviceInfo enumerates the devices, SetupDiEnumDeviceInterfaces enumerates the interfaces of a device. Also, there are no other code doing something to my deviceInfoSet. DeviceManager.h, DeviceManager.cpp and Mainframe.cpp is the only files I have, but Mainframe.cpp only contains my int main function that calls GetDevice(). – Danny May 14 '12 at 18:32
  • ah yes. wrong while ~i is eejit lol... – violet313 May 14 '12 at 18:36

2 Answers2

9

Have a go with this.

I have tried to hack your original code about as little as possible; the following codes (for me at least) get through to the inner while(SetupDiEnumDeviceInterfaces..):

void DeviceManager::GetDeviceUSB(std::string vid, std::string pid)
{
    DWORD deviceIndex = 0;
    deviceInfoData.cbSize = sizeof(deviceInfoData);

    //buried somewhere deep in the ddk
    static GUID GUID_DEVINTERFACE_USB_HUB={ 0xf18a0e88, 0xc30c, 0x11d0, {0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8} };
    static GUID GUID_DEVINTERFACE_USB_DEVICE ={ 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
    static GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER={ 0x3abf6f2d, 0x71c4, 0x462a, {0x8a, 0x92, 0x1e, 0x68, 0x61, 0xe6, 0xaf, 0x27}};

    //get usb device interfaces
    HDEVINFO deviceInterfaceSet=SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_DEVICEINTERFACE);


    while(SetupDiEnumDeviceInfo(deviceInterfaceSet, deviceIndex, &deviceInfoData))
    {
        deviceInfoData.cbSize = sizeof(deviceInfoData);

        ULONG IDSize;
        CM_Get_Device_ID_Size(&IDSize, deviceInfoData.DevInst, 0);

        TCHAR* deviceID = new TCHAR[IDSize];

        CM_Get_Device_ID(deviceInfoData.DevInst, deviceID, MAX_PATH, 0);

        if( deviceID[8]==vid.at(0) && deviceID[9]==vid.at(1) && deviceID[10]==vid.at(2) && deviceID[11]==vid.at(3) && //VID
            deviceID[17]==pid.at(0) && deviceID[18]==pid.at(1) && deviceID[19]==pid.at(2) && deviceID[20]==pid.at(3)) //PID
        {
            DWORD deviceInterfaceIndex = 0;
            deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);

            while(SetupDiEnumDeviceInterfaces(deviceInterfaceSet, &deviceInfoData, &GUID_DEVINTERFACE_USB_DEVICE, deviceInterfaceIndex, &deviceInterfaceData))
            {
                deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
                std::cout << deviceInterfaceIndex << std::endl;

                //get some more details etc
                //DWORD requiredBufferSize;
                //SetupDiGetDeviceInterfaceDetail(deviceInterfaceSet, &deviceInterfaceData, NULL, 0, &requiredBufferSize, 

                deviceInterfaceIndex++;
            }
        }

        deviceIndex++;
    }
}

As far as I know, this method picks up the same devices as with your OP constructor call (NB: I included some other useful interface guids):

deviceInfoSet = SetupDiGetClassDevs(NULL, TEXT("USB"), NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); //Gets all Devices

But I am doing this to get the device interfaces:

// /coughs/ you might want to put back the DIGCF_PRESENT flag I removed for testing
HDEVINFO deviceInterfaceSet=SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_DEVICEINTERFACE); 

I also pass the deviceInfoData to SetupDiEnumDeviceInterfaces since according to the documentation:

A pointer to an SP_DEVINFO_DATA structure that specifies a device information element in DeviceInfoSet. This parameter is optional and can be NULL. If this parameter is specified, SetupDiEnumDeviceInterfaces constrains the enumeration to the interfaces that are supported by the specified device. If this parameter is NULL, repeated calls to SetupDiEnumDeviceInterfaces return information about the interfaces that are associated with all the device information elements in DeviceInfoSet. This pointer is typically returned by SetupDiEnumDeviceInfo.

Edit

Additional note as requested. Your USB device has an associated setup class and interface class(es):

From the device setup classes documentation:

The device setup class defines the class installer and class co-installers that are involved in installing the device

From the device interface classes documentation:

A device interface class is a way of exporting device and driver functionality to other system components, including other drivers, as well as user-mode applications

So:

deviceInfoSet = SetupDiGetClassDevs(NULL, TEXT("USB"), NULL,DIGCF_PRESENT|DIGCF_ALLCLASSES);

This is retrieving all setup class information sets and filtering on "USB"

You could do this:

deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES|DIGCF_DEVICEINTERFACE);`

This retrieves class information sets for devices that support a device interface of any class. (Applying an emumerator ID s/a "USB" seems to have no affect).

Crucially however: The function adds to the device information set a device information element that represents such a device and then adds to the device information element a device interface list that contains all the device interfaces that the device supports.

(And note: SP_DEVINFO_DATA.ClassGuid is always the GUID of the device's setup class)

As far as I know, you then still need to provide an InterfaceClassGuid when invoking SetupDiEnumDeviceInterfaces(). To be honest, I don't really understand why this would be necessary if the caller is providing the optional DeviceInfoData but since it's all closed source, how would I know? :)

And here is info regarding GUID_DEVINTERFACE_USB_DEVICE

Disclaimer: I do not work for M$; do treat the above information with suspicion, and of course do your own research.

halfer
  • 19,824
  • 17
  • 99
  • 186
violet313
  • 1,912
  • 1
  • 15
  • 19
  • that works and it is exactly what I need! Thanks alot :D. If you don't mind, can I also ask you to explain a bit more about the GUID that you've declared at the top?? Why do we need them when I've used TEXT("USB")? What is the difference? Or can you atleast provide me a link where I can look up more info about the GUIDs that you've declared?? Thanks :D – Danny May 15 '12 at 09:27
  • noP, see revised answer,, btw lol, i am *not an [alot](http://hyperboleandahalf.blogspot.co.uk/2010/04/alot-is-better-than-you-at-everything.html) XD – violet313 May 15 '12 at 16:59
  • 1
    I wish I could up vote this more than once. You must have put a lot of effort into this answer. I have one more piece of information to add, having thrashed about for over a day looking for it: If your device is a WinUSB device, then the GUID you pass to `SetupDiEnumDeviceInterfaces()` will be the "DeviceInterfaceGUIDs" value from the registry/inf file. I very much doubt this is documented anywhere. – Edward Falk Feb 23 '17 at 09:12
4

The problem starts with how SetupDiGetClassDevs is called.

If you are looking to get a device path, use SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE ,,,)

SetupDiEnumDeviceInterfaces fails with error 259 if SetupDiGetClassDevs is given the wrong GUID in ClassGuid which MS Help says is A pointer to the GUID for a device setup class or a device interface class.

Include file devguid.h contains a set of GUID_DEVCLASS values. These are NOT the same as GUID_DEVINTERFACE_* values which are the one you need.

Use #include <uuids.h> which includes ksuuids.h where you'll find GUID_DEVINTERFACE_* values.

There's a more detailed explanation on my website, with some source code that should help in correctly enumerating USB devices.

See http://pixcl.com/SetupDiEnumInterfaces_Fail.htm