Issues using DirectInput

Post questions, comments and feedback to our 3Dconnexion Windows Development Team.

Moderator: Moderators

simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Issues using DirectInput

Post by simplyRubbish »

First off, I am using the SpaceExplorer and attempting to get this device to work with our software using DirectInput: http://www.3dconnexion.com/3dmouse/spaceexplorer.php

The problem that I'm getting stumped at is in EnumAxesCB function. *pDIDeviceInfo is NULL so my application crashes. Here's what my code looks like for there:

Code: Select all

/*---------------------------------------------------------------------------*/
BOOL CALLBACK EnumAxesCB( const DIDEVICEOBJECTINSTANCE* pdidoi,
                         VOID* pContext )
                         /*
                         Callback function for enumerating the axes on a UsbHidDevice
                         -----------------------------------------------------------------------------*/
{
    HWND hDlg = (HWND)pContext;
    DIDeviceInfo *pDIDeviceInfo = (DIDeviceInfo *)GetWindowLongPtr( hDlg, GWLP_HINSTANCE );

#if 0 // this has no effect
    // Set the range for the axis
    DIPROPRANGE diprg; 
    diprg.diph.dwSize       = sizeof(DIPROPRANGE); 
    diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
    diprg.diph.dwHow        = DIPH_BYID; 
    diprg.diph.dwObj        = pdidoi->dwType; // Specify the enumerated axis
    diprg.lMin              = -512; // +/- 512 matches the hardware 1:1 
    diprg.lMax              =  511; 

    if( FAILED( pDIDeviceInfo->pUsbHidDevice->SetProperty( DIPROP_RANGE, &diprg.diph ) ) )
        return DIENUM_STOP;
#endif

    /*
    * Recent 3Dx USB device descriptors indicate that the device axes return relative data
    * even though the device is really an absolute device.
    * HID ignores this, but apparently DI uses it.
    * Changing the axis handling to return RELATIVE values gives us back what we want (displacement
    * values rather than accumulated values).  If older USB devices were to be supported, this handling
    * would have to be changed to DIPROPAXISMODE_ABS.  
    *
    * In this mode, DI sets the notification event even though the values haven't changed.
    */
    DIPROPDWORD dipdw;
    dipdw.diph.dwSize       = sizeof(DIPROPDWORD); 
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
    dipdw.diph.dwHow        = DIPH_DEVICE; 
    dipdw.diph.dwObj        = 0; // set for whole device not an axis (really only needs to be done once)
    dipdw.dwData            = DIPROPAXISMODE_REL;

    if( FAILED( pDIDeviceInfo->pUsbHidDevice->SetProperty( DIPROP_AXISMODE, &dipdw.diph ) ) )
        return DIENUM_STOP;

    return DIENUM_CONTINUE;
}
The reason I'm using GLWP_HINSTANCE instead of GWLP_USERDATA is due to the fact I'm not actually creating the window the HWND is attached to in EnumDevicesCB function. Here's my code for EnumDevicesCB to show what I mean:

Code: Select all

BOOL CALLBACK EnumDevicesCB( const DIDEVICEINSTANCE* pdidInstance,
                            VOID* pContext )
                            /*
                            Called once for each enumerated UsbHidDevice. If we find one, create a
                            device interface on it so we can play with it.
                            -----------------------------------------------------------------------------*/
{
    int newIndex;
    HRESULT hr;
    GUID guid = pdidInstance->guidProduct;
#   define LOGITECH_3DX_VID    0x046d  // Vendor ID for Logitech/3Dx

    /* Check to see if this is a 3Dx device.  Look at the VendorID before stopping the enumeration. */
    if ( (guid.Data1 & 0x0000ffff) != LOGITECH_3DX_VID)
        return DIENUM_CONTINUE;

    /* Filter out LOGI mice and such */
    else if (pdidInstance->wUsagePage != 0x0001 || pdidInstance->wUsage != 0x0008)
        return DIENUM_CONTINUE;

    /* Make sure haven't found too many 3Dx devices for our array */
    if (g_nDevices >= MAX_DEVICES)
        return DIENUM_CONTINUE;


    newIndex = g_nDevices;
    g_pDevices[g_nDevices++] = (DIDeviceInfo*)malloc( sizeof(DIDeviceInfo) );

    /* Set state variables to known values */
    ZeroMemory( &g_pDevices[newIndex]->lastJoyState, sizeof(DIJOYSTATE) );
    g_pDevices[newIndex]->lastVectorType = BothZeroVectors;

    /* 
    * Save the DIDEVICEINSTANCE struct for later display.
    * This isn't necessary.  The demo just does it to display the info in the dlg box.
    */
    g_pDevices[newIndex]->DIDevInstance = *pdidInstance;

    /* Obtain an interface to the enumerated device. */
    hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pDevices[newIndex]->pUsbHidDevice, NULL );

    /* 
    * If it failed, then we can't use this UsbHidDevice. (Maybe the user unplugged
    * it while we were in the middle of enumerating it.)
    */
    if( FAILED(hr) ) 
    {
        free (g_pDevices[newIndex]);
        g_nDevices--;
        return DIENUM_CONTINUE;
    }

    /* Create a info dialog box for this device */
    //g_pDevices[newIndex]->hDlg = CreateDialogParam( hInst, MAKEINTRESOURCE(_APS_NEXT_RESOURCE_VALUE), NULL, DlgProc,
    //    (LPARAM)g_pDevices[newIndex] );
    g_pDevices[newIndex]->hDlg = GetForegroundWindow();

    return DIENUM_CONTINUE;
}
As you can see, the original "CreateDialogParam" is removed due to the fact this console application was already created so the code to handle that is done. I guess the question I'm trying to get at is, how can I handle the EnumAxesCB() function properly while getting the correct pDIDeviceInfo if I didn't do the creation of the window in EnumDevicesCB?

Any help on this issue is appreciated. Thank you.
jwick
Moderator
Moderator
Posts: 3341
Joined: Wed Dec 20, 2006 2:25 pm
Location: USA
Contact:

Post by jwick »

??
global variable ??
simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Post by simplyRubbish »

jwick wrote:??
global variable ??
What do you mean by 'global variable'? Am I missing a variable?

Am I even able to use DInput if I don't use Win32 windows?
jwick
Moderator
Moderator
Posts: 3341
Joined: Wed Dec 20, 2006 2:25 pm
Location: USA
Contact:

Post by jwick »

I don't know what the scope of "don't use Win32 windows" means. It doesn't run on Linux. I don't think you need a windows event loop if that's what you are asking about. The sample code that you are apparently using only uses windows to display information to the end user. You should read the DirectInput doc pages.

I'd get the sample up and running, then start removing all the parts you don't want to get down to where you want to be, or start adding new parts to build up to where you want to be.
simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Post by simplyRubbish »

jwick wrote:I don't know what the scope of "don't use Win32 windows" means. It doesn't run on Linux. I don't think you need a windows event loop if that's what you are asking about. The sample code that you are apparently using only uses windows to display information to the end user. You should read the DirectInput doc pages.

I'd get the sample up and running, then start removing all the parts you don't want to get down to where you want to be, or start adding new parts to build up to where you want to be.
What I mean is the console application we already wrote and we want to use the spaceball to input values. I guess I said the wrong words with "Win32 windows". Is it possible to use DInput with a console application or would I be better off using an alternative input such as HID?
simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Post by simplyRubbish »

Let me add a bit more detail that can be helpful.

The application I am using the SpaceExplorer device for is a console application that is basically a "wrapper" for a 3d application that uses DirectX. We used to use direct calls to the 3dConnexion software such as SIOpen but those are no longer working and we decided it would be best to use DInput since all we need is raw data.

Is DInput the best method for our needs or should we be using something else?

Thanks again.
jwick
Moderator
Moderator
Posts: 3341
Joined: Wed Dec 20, 2006 2:25 pm
Location: USA
Contact:

Post by jwick »

Six of one, half a dozen of the other, if you don't already have any code to handle either. HID certainly requires a lot more code to write and you have to use threads to handle > 1 device. OTOH, you don't have to drag DirectInput libs around.

Sit down with the code from each of the two samples along with the architects of the original code and see which one will fold into your existing code base easier.

If you don't have a graphical program, another approach could be to have a separate program read the 3Dx device and feed the data into your program via some other convenient IPC mechanism.
jwick
Moderator
Moderator
Posts: 3341
Joined: Wed Dec 20, 2006 2:25 pm
Location: USA
Contact:

Post by jwick »

Oops, those crossed in the mail. I'd say if you are already using DirectX, you should use DirectInput.
simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Post by simplyRubbish »

Thanks for the help jwick. It's much appreciated. One last question I have is, do I have to initialize the windows form to get DInput to work? From the example code that looks like what I have to do but I may be incorrect in this. I think that was the problem I was having earlier is that the HWND I was giving DInput wasn't one it started with.

Am I incorrect in this?

PS. This will be my last question for a while :P

Thanks again.
jwick
Moderator
Moderator
Posts: 3341
Joined: Wed Dec 20, 2006 2:25 pm
Location: USA
Contact:

Post by jwick »

That's just a context pointer to get data into the EnumAxesCB callback. You can get that data in there any way you want, even an evil global variable (eek).
simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Post by simplyRubbish »

jwick wrote:That's just a context pointer to get data into the EnumAxesCB callback. You can get that data in there any way you want, even an evil global variable (eek).
Hmm I wonder why DIDeviceInfo *pDIDeviceInfo would be NULL in EnumAxesCB function...

I'll give it another shot and hopefully it works.

Thanks for the clarification.
jwick
Moderator
Moderator
Posts: 3341
Joined: Wed Dec 20, 2006 2:25 pm
Location: USA
Contact:

Post by jwick »

Make sure you are stashing it where you think you are.
simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Post by simplyRubbish »

jwick wrote:Make sure you are stashing it where you think you are.
Gotcha, will double check for that.
simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Post by simplyRubbish »

Well everything looks good and the device is updating except one problem. On MsgWaitForMultipleObjects it's just staying there and not responding. I read MSDN and it looks like I'm doing everything right. I guess it's not getting any events for the handle? What can I do to fix this?

One thing to note however is in InitDI8 function I added this:

Code: Select all

g_pDevices[i]->hDlg = GetConsoleWindow();
to get the HWND of the window. Is this incorrect? I figured since I was told it doesn't matter which HWND you're using this would work.

The other "quirky" thing I'm noticing is in EnumAxesCB instead of

Code: Select all

DIDeviceInfo *pDIDeviceInfo = (DIDeviceInfo *)GetWindowLongPtr( hDlg, GWLP_USERDATA );
I'm doing

Code: Select all

DIDeviceInfo *pDIDeviceInfo = g_pDevices[0];
I'm guessing this is causing problems but when the first one gives NULL I wasn't sure where to go.
simplyRubbish
Posts: 13
Joined: Fri Oct 02, 2009 7:25 am
Location: Daytona Beach, FL

Post by simplyRubbish »

One thing I'm noticing that may have already been addressed is in the DIExample code as well as mine, the input values from the spaceball don't change when I change the "overall speed" on the 3Dconnexion Control Panel. Is this a known issue with DInput?
Post Reply