Creating custom data structures for HID devices via DirectInput

Hi all,

I'm working on accessing generic HID devices on Win32 via DirectInput.  It's generally going well, but I've hit a snag.

The devices I want to send and receive data to are non-standard devices - that is, they are not keyboards, mice or joysticks.  So, I think I need to use SetDataFormat with a custom DIDATAFORMAT structure to define a custom structure for these non-standard devices.  I'd expect to iterate through all available objects for the device, get data about each object, and then create a matching data format.

I'm doing just this, but I'm not 100% sure how I should safely create the DIOBJECTDATAFORMAT structures for any potential device object.  This is a problem because of the data offsets I am receiving and using.

When iterating through a device's objects with EnumObjects using a dwFlags value of DIDFT_ALL, at least one device I have tested with returns some objects with an offset of 0.  Now, I would expect the first object to have an offset of 0, but in this case the 16th and 17th objects did too.

Also, I'm not sure how I'm supposed to tell (for a given object) what the data size of that object is, to use with the dwOfs member of the DIOBJECTDATAFORMAT struct.  I though the approach would be to create a DIOBJECTDATAFORMAT struct for every object reported by EnumObjects, but these objects with zero offset have thrown me, as I can't calculate their size by comparing their offset to the offset of the previous object in the enumeration.

So, my question is: what is the recommended way to convert all objects on a HID device into a DIDATAFORMAT for use with SetDataFormat

A couple of extra follow-on questions...

Is the maximum data size I'll ever get back from a call to GetDeviceData a DWORD

Is there a way for me to tell which object has data available for it when I call GetDeviceData   I'd assumed that the dwOfs member of the returned DIDEVICEOBJECTDATA would enable me to identify the object in question via its offset in the data format, but the problem mentioned above (multiple objects with an offset of zero) makes me think not.

Any help much appreciated!



Answer this question

Creating custom data structures for HID devices via DirectInput

  • revati27

    Ross Ridge wrote:

    It's not supposed to be in the same format as the hardware uses natively. Just create a custom data format that has the objects that both the device supports and your application actually uses.


    But is it wrong to use the native dataformat, i.e. just copiing the information retrieved with the EnumObjects-Callback function
    I'm quite confused about this method. Actually it works sometimes and sometimes it does not for no apparent reason.

  • Hrvoje Zlatar

    halebob wrote:
    I also copy the GUIDs, dwType and dwFlags from the DIDEVICEOBJECTINSTANCE structure i get from EnumObjects.

    I think you just need to copy the dwType value, that should uniquely identify the object. Make sure that you're not trying to use objects that have the DIDFT_NODATA bit set. If you do copy the guidType and dwFlags make sure you're copying the guidType value, and that you're not just copying the address of the guidType member. You may need to mask out the non-aspect bits from dwFlags.

    Make sure that the values in your custom data format are of the correct size and are properly aligned. All buttons (DIDFT_TGLBUTTON or DIDFT_PSHBUTTON) use BYTE values, and these can be placed at any offset in the structure. Everything else uses DWORD values and these need to be at offset that's a multiple of 4. Each offset in your structure can be used for at most one object, and each object can have at most one offset in your structure.

    If you still can't figure out what's wrong, try using the DirectInput debug runtime. It should hopefully print the reason why it's failing in your debugger.


  • ExcelinLondon

    Thank you, that gives me some more insight. Anyway, I still get into problems, when creating a custom dataformat dynamically. I thought the obvious way would be calling EnumObjects and checking for available Buttons and Axes. With that information i would create my dataformat (and use my own offsets).
    I also copy the GUIDs, dwType and dwFlags from the DIDEVICEOBJECTINSTANCE structure i get from EnumObjects. Somehow this works on my system here but not on another. What else can be done wrong then I mean, the Objectdataformat structure isn't very complex - there are only 4 fields, so there's not very much choice to change something....

  • BrantCarter

    halebob wrote:
    But is it wrong to use the native dataformat, i.e. just copiing the information retrieved with the EnumObjects-Callback function

    In general, it's impossible to duplicate the native data format that the device sends over the USB wire. You can create a DirectInput data format that contains values for all all the objects you can enumerate, but the data format will be different than the native one. For example, a USB mouse might have a native data format something like this:

    struct example_native_usb_mouse {
    BYTE button0 : 1;
    BYTE button1 : 1;
    BYTE button2 : 1;
    BYTE pad : 5;
    signed char xdelta;
    signed char ydelta;
    };

    Notice how all three buttons are packed into a single byte, and that the relative position data are signed 8-bit values. With DirectInput you can't pack multiple buttons into a single BYTE, each button requires it's own BYTE value, while all other non-boolean values have to be stored in DWORDs. So, a corresponding custom DirectInput data format could be:

    struct example_dinput_usb_mouse {
    BYTE button0;
    BYTE button1;
    BYTE button2;
    BYTE pad;
    LONG xdelta;
    LONG ydelta;

    };

    Note, the standard DirectInput data format for mouse input is different the from the example I gave above.


  • Bill Schulz

    Hi,

    Can anyone help me out with this I've just got my WDDK in the post, and it looks like I need to somehow use HidP_GetData and HidP_SetData to access the Device Object data in more detail by using the lower-level HID API. But I have no idea how.

    This page talks about how the DirectInput wDesignatorIndex can be passed to functions in Hidpi.h:

    http://msdn.microsoft.com/library/default.asp url=/library/en-us/directx9_c/DIDEVICEOBJECTINSTANCE.asp

    ...and this page (from the HID docs) talks about the fact that this can happen:

    http://msdn.microsoft.com/library/default.asp url=/library/en-us/Hid_d/hh/HID_d/hidclass_fa9f249b-4866-481c-9f7f-c96b19442606.xml.asp

    ...but I have no idea how the two might link up. Can anyone help

    Dave.


  • Prashant N M

     maurj wrote:

    I'm doing just this, but I'm not 100% sure how I should safely create the DIOBJECTDATAFORMAT structures for any potential device object.  This is a problem because of the data offsets I am receiving and using.

    The DIOBJECTDATAFORMAT structures define your own custom data format.  It's not supposed to be in the same format as the hardware uses natively.  Just create a custom data format that has the objects that both the device supports and your application actually uses.  For example, you might use code something like this:

    unsigned const BUTTON0 = 1 << 0;
    //...
    unsigned const YAXIS = 1 << 4;

    unsigned const REQUIRED = BUTTON0 | XAXIS | YAXIS;
    unsigned controls;

    struct custom {
        BYTE button0, button1, button2, pad;
        DWORD xaxis, yaxis;
    };

    DIOBJECTDATAFORMAT button0 = { 0, offsetof(custom, button0),
                                   DIDFT_BUTTON | DIDFT_MAKEINSTANCE(0),
                                   0 };
    // ...

    DIOBJECTDATAFORMAT yaxis = { &GUID_YAxis, offsetof(custom, yaxis),
                                 DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 };
    DIOBJECTDATAFORMAT objects[5] = { button0, xaxis, yaxis, button1, button2 };
    DIDATAFORMAT didf = { sizeof didf, sizeof objects[0], 0,
                          sizeof(custom), 3, objects };

    // Set flags on "controls" for the objects the device supports.
    dinput->EnumObjects(callback, &controls, DIDFT_ALL);
    if ((controls & REQUIRED) != REQUIRED)
        return FAIL;  // Device is missing required objects
    if (controls & BUTTON1)
        objects[didf.dwNumObjs++] = button1;
    if (controls & BUTTON2)
        objects[didf.dwNumObjs++] = button2;

    dinput->SetDataFormat(&didf);

    You should also look at the CustomData sample in the DirectX SDK which uses the undocumented DIDFT_OPTIONAL flag to handle optional controls.


  • Creating custom data structures for HID devices via DirectInput