Jump to content
  • Advertisement
Sign in to follow this  
HeavyBlade

DInput Function Pointer Woes

This topic is 4817 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ok, I've got another problem, this time regarding the use of enumerating devices. The function pointer in parameter 3 points to a member of class CDx_Input (this code exert was taken from a different function of said class) to handle the necessary enumdevice callback. For some reason, the compiler continues to reject it - despite the fact that there is no syntactical basis for the error. m_lpDI8->EnumDevicesBySemantics(NULL, &m_diAF, CDx_Input::DIEnumDevicesBySemanticsCallback, this, DIEDBSFL_ATTACHEDONLY); "c:\DarkBlade_Ent\DotS\DotS\Source\CDx_Input.cpp(100): error C2664: 'IDirectInput8W::EnumDevicesBySemantics' : cannot convert parameter 3 from 'BOOL (LPCDIDEVICEINSTANCEW,LPDIRECTINPUTDEVICE8W,DWORD,DWORD,LPVOID)' to 'LPDIENUMDEVICESBYSEMANTICSCBW'" The function declaration is: BOOL CALLBACK CDx_Input::DIEnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCEW lpddi, LPDIRECTINPUTDEVICE8W lpDevice, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef) So, why would this be a problem? Much thanks in advance. BTW: If anyone knows of a good tutorial related to default mapping keyboard and mice, that would be grand too. The ones I've found - including MSDNs, don't cover it. -HeavyBlade

Share this post


Link to post
Share on other sites
Advertisement
Just an idea...I'm not sure if you can use function pointers that point to class members like that. Try creating a global function with the same function signature, like:

BOOL CALLBACK DIEnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCEW lpddi, LPDIRECTINPUTDEVICE8W lpDevice, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)

If that works, you could always call CDx_Input::DIEnumDevicesBySemanticsCallback(), provided you have access to the engine instance. Like this:


BOOL CALLBACK DIEnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCEW lpddi, LPDIRECTINPUTDEVICE8W lpDevice, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
{
return engineInstance->inputInstance->DI...Callback( lpddi, lpDevice, dwFlags, dwRemaining, pvRef );
}


This is the approach I take when setting up a Windows MsgProc() - a global function just passes through to the actual engine member procedure.

Share this post


Link to post
Share on other sites
Quote:
Original post by HeavyBlade
...That's weird, using a global function didn't work either - it gives the same error.

-HeavyBlade

Can you show us the exact code and the exact error?

Share this post


Link to post
Share on other sites
The compiler seems to be ignoring your CALLBACK statement for some reason. CALLBACK is just a #define for __stdcall.
You don't #define CALLBACK as anything else in your app, do you?

Share this post


Link to post
Share on other sites
Nope, CALLBACK isn't redefined anywhere in the app. The exact code is:

[source="cpp"]
bool CDx_Input::Init_DIAF(HWND hWnd)
bool CDx_Input::Init_DIAF(HWND hWnd)
{
bool bSuccess = false;
LPCTSTR szActionMapName = _T("User");

ZeroMemory(&m_diAF, sizeof(DIACTIONFORMAT));
m_diAF.dwSize = sizeof(DIACTIONFORMAT);
m_diAF.dwActionSize = sizeof(DIACTION);
m_diAF.dwDataSize = GetNumActions() * sizeof(DWORD);
m_diAF.dwNumActions = GetNumActions();
m_diAF.guidActionMap = g_guidApp;
//diaf.dwGenre = DIVIRTUAL_SPACESIM;
m_diAF.rgoAction = g_rgActions;
m_diAF.dwBufferSize = 16;
m_diAF.lAxisMin = -100;
m_diAF.lAxisMax = 100;
_tcscpy(m_diAF.tszActionMap, szActionMapName);

// Store the hWnd away for later usage:
m_hWnd = hWnd;

// Time to enumerate them thar devices!
//LPDIENUMDEVICESBYSEMANTICSCBW
m_lpDI8->EnumDevicesBySemantics(NULL, &m_diAF, CDx_Input::DIEnumDevicesBySemanticsCallback, this, DIEDBSFL_ATTACHEDONLY);
bSuccess = true;

// Function return.
return bSuccess;
}



[source="cpp"]
BOOL CALLBACK CDx_Input::DIEnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCEW lpddi, LPDIRECTINPUTDEVICE8W lpDevice, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
{
HRESULT hr = S_OK;
DWORD dwIndex = 0;

// Devices of type DI8DEVTYPE_DEVICECTRL are specialized devices not generally
// considered appropriate to control game actions. We just ignore these.
if(GET_DIDEVICE_TYPE(lpddi->dwDevType) != DI8DEVTYPE_DEVICECTRL)
{
// Exclusive control = debugging hell, so fuck it.
hr = lpDevice->SetCooperativeLevel(m_hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
if(SUCCEEDED(hr))
{
Write_Log(E_NONE, "CDx_Input: Successfully set device's co-operative level(DISCL_NONEXCLUSIVE|DISCL_BACKGROUND)");

// Build the action map for the device. This will map each action to
// the most appropriate function on the device.
hr = lpDevice->BuildActionMap(&m_diAF, NULL, DIDBAM_DEFAULT);
if(SUCCEEDED(hr))
{
Write_Log(E_NONE, "CDx_Input: Successfully built action map for device iteration.");

for(dwIndex = 0; dwIndex < m_diAF.dwNumActions; dwIndex++)
{
if(m_diAF.rgoAction[dwIndex].dwHow != DIAH_UNMAPPED)
break;
}

if(dwIndex < m_diAF.dwNumActions)
{
// If any controls were mapped, we will be using this device,
// so set the action map on this device.
hr = lpDevice->SetActionMap(&m_diAF, NULL, DIDSAM_DEFAULT);
if(SUCCEEDED(hr))
{
Write_Log(E_NONE, "CDx_Input: Successfully set action map for device iteration.");

// Check to see if we have a valid m_pDeviceAr instance.
if(!m_pDeviceAr)
m_pDeviceAr = GCC_NEW LPDIRECTINPUTDEVICE8[dwRemaining + 1];

// Add the new device iteration into the array of accepted devices for input usage.
m_pDeviceAr[m_iDevices] = lpDevice;
m_iDevices++;

// Add a reference to this device, since DirectInput will
// release the device when we return.
m_lpDI8->AddRef();
}
else
Write_Log(E_ERROR, "CDx_Input: Failed to set action map for device iteration.");
}
}
else
Write_Log(E_ERROR, "CDx_Input: Failed to build action map for device iteration.");
}
else
Write_Log(E_ERROR, "CDx_Input: Failed to set device's co-operative level(DISCL_NONEXCLUSIVE|DISCL_BACKGROUND).");
}
else
Write_Log(E_WARNING, "CDx_Input: CDx_Input was passed a DI8DEVTYPE_DEVICECTRL - not acceptable for gaming and subsequently ignored.");

// Function return. Always this value.
return DIENUM_CONTINUE;
}

Share this post


Link to post
Share on other sites
Well, that's not a global function. Either make it a static member function, or use a global function. (And if either fails, post the code + the error).

Edit - To clarify why member functions don't work:
Member functions have an implicit first parameter, the 'this' pointer. The compiler inserts this parameter behind your back in order for the function to access instance data/functions. When you mark a function as static, you're saying it's not associated with an instance (object), but with the class itself - so there's no implicit 'this' parameter.

Share this post


Link to post
Share on other sites
Quote:
Original post by Coder
Well, that's not a global function. Either make it a static member function, or use a global function. (And if either fails, post the code + the error).

Edit - To clarify why member functions don't work:
Member functions have an implicit first parameter, the 'this' pointer. The compiler inserts this parameter behind your back in order for the function to access instance data/functions. When you mark a function as static, you're saying it's not associated with an instance (object), but with the class itself - so there's no implicit 'this' parameter.
Actually, I think it is a static function.
He's passing in a this pointer, which makes me think he knows he should be using a static function, and the static modifier can only be used where the function is declared - in the class declaration, which we can't see.
Assuming this is the same code as in his first post, then the compiler isn't interpreting it as a member function, or else the error would mention that it's trying to cast from a member function (It'd mention the CDx_Input:: class).

However, having said that - I don't know what the problem actually is, except the compiler isn't mentioning that it's a __stdcall function in the error, which it should be.

Share this post


Link to post
Share on other sites
Ok, so what do I do to... correct the code inside the function exactly? If I static the member function as you say, the usage of the member variables inside the static function becomes invalid? Forgive me, but the usage of static is still something I'm vague on.

..Sorry, I'm being a bit of an a** here. I shoulda given you view of the header before ><.

CDx_Input.h (remember that? :P)
[source="cpp"]
#ifndef _DIRECTXMANAGERS_CDXINPUT_H
#define _DIRECTXMANAGERS_CDXINPUT_H

#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

// Define button is down and button is released. Button released is just the opposite of button down.
#define KeyDown(name, key) ((name[key] & 0x80) ? true : false)
#define KeyUp(name, key)((name[key] & 0x80) ? false : true)

// Our action-map identifier list:
enum ACTION_KEYS
{
ACTION_LEFT, // The key for walking left.
ACTION_RIGHT, // The key for walking right.
ACTION_UP, // The key for walking up.
ACTION_DOWN, // The key for walking down.
ACTION_OK, // The key for interact/confirm.
ACTION_CANCEL, // The key for dashing/cancelling.
ACTION_MENU, // The key for the in-game menu.
ACTION_FQUIT, // Our emergency escape key.
};

// Key mappings
static DIACTION g_rgActions[] =
{
// Actions mapped to keys as well as to virtual controls
{ACTION_LEFT, DIKEYBOARD_LEFT, 0, _T("Walk Left") },
{ACTION_RIGHT, DIKEYBOARD_RIGHT, 0, _T("Walk Right") },
{ACTION_UP, DIKEYBOARD_UP, 0, _T("Walk Up") },
{ACTION_DOWN, DIKEYBOARD_DOWN, 0, _T("Walk Down") },
{ACTION_OK, DIKEYBOARD_RETURN, 0, _T("Interact//Confirm") },
{ACTION_CANCEL, DIKEYBOARD_RSHIFT, 0, _T("Dash//Cancel") },
{ACTION_MENU, DIKEYBOARD_TAB, 0, _T("Open in-game menu") },
{ACTION_FQUIT, DIKEYBOARD_ESCAPE, 0, _T("Fast Exit(dev)") },
};

class CDx_Input
{
public:
CDx_Input(void);
~CDx_Input(void);

HRESULT Init_DirectInput(void);
bool Init_DIAF(HWND hWnd);
static BOOL CALLBACK DIEnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCEW lpddi, LPDIRECTINPUTDEVICE8W lpDevice, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef);
void AcquireDevices(void);
void UnacquireDevices(void);
void Update(void);
void Close(void);

DWORD GetNumActions(void) { return m_dwActions; }
void SetNumActions(DWORD dwActions) { m_dwActions = dwActions; return; }

// Handle the current action.
void HandleAction(unsigned int uiAction, DWORD dwData);

HWND m_hWnd; // Stores a copy of the hWnd pointer typedef, used for initializing.

private:
LPDIRECTINPUT8 m_lpDI8;
LPDIRECTINPUTDEVICE8* m_pDeviceAr; // Array of devices that we will be using for our action mapping.

DIACTIONFORMAT m_diAF; // Holds the g_rbAction key-mapping structure defined above this class.
DWORD m_dwActions; // Holds the number of actions we have enumerated for action-mapping.
int m_iDevices; // Holds the total number of devices we have to deal with. Stupid user and his 99 bloody controllers :P XD
};

#endif // _DIRECTXMANAGERS_CDXINPUT_H



The now static function was NOT static before, btw.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Actually, I think it is a static function.
He's passing in a this pointer, which makes me think he knows he should be using a static function, and the static modifier can only be used where the function is declared - in the class declaration, which we can't see.

He was passing a 'this' pointer, but he wasn't using it.

Quote:
If I static the member function as you say, the usage of the member variables inside the static function becomes invalid? Forgive me, but the usage of static is still something I'm vague on.

Yes. A static member function doesn't have access to any non-static member data. That's why the designers of the callback put a void pointer for you to pass anything you want. You should pass this through this pointer, and then in the callback, cast it back to the class type. Convert all your m_* to object->m_*.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!