Jump to content
  • Advertisement
Sign in to follow this  
HeavyBlade

DirectInput8 causes HardCrash

This topic is 4808 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

Ow. It hurts. Nothing like a series of hard crashes to ruin your day. Anyway, before the collective genius's here I once again must bring my cursed CDx_Input class. This time, it initializes, boots up and everything, and then (on top of not apparently working) hard-crashes. Alt-f4 doesn't work, windows key doesn't work, ctrl-alt-del doesn't work. I've heard this can happen if you don't unacquire the devices, but I'm doing that, so I don't see the problem. In my main...
[source="cpp"]
	case WM_ACTIVATE:
        if(LOWORD(wParam) == WA_INACTIVE)
        {
            // Set paused to false here.
			if(g_pApp && g_pApp->m_pGame)
				g_pApp->m_pGame->m_pView->m_pCDxInput->UnacquireDevices();
        }
        else
        {
            // Set paused to true here.
			if(g_pApp && g_pApp->m_pGame)
				g_pApp->m_pGame->m_pView->m_pCDxInput->AcquireDevices();
        }
        break;

CDx_Input.cpp
[source="cpp"]
// Use DirectInput 8 to correspond to the rest of our program (uses DirectX 9.0c)
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include "stdafx.h"
#include "CDx_Input.h"
#include "CONSTANTS.h"
#include "DEFINES.h"
#include "Log.h"

CDx_Input::CDx_Input(void)
{
	m_lpDI8 = NULL;
	m_pDeviceAr = NULL;
	m_iDevices = 0;
}

CDx_Input::~CDx_Input(void)
{
	Close();
}

/**********************************************************
Function: HRESULT CDx_Input::Init_DirectInput(void)
Description: 
Parameters: None.
Returns: 
***********************************************************/
HRESULT CDx_Input::Init_DirectInput(void)
{
	HRESULT hr = S_OK;

	hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_lpDI8, NULL);

	if(SUCCEEDED(hr))
		Write_Log(E_NONE, "Succeeded in instantiating DirectInput8 on startup.");
	else
		Write_Log(E_FATAL, "Failed to instantiate DirectInput8 on startup!");

	// Instantiate our array of mapped controls:
	SetNumActions((sizeof(g_rgActions) / sizeof(DIACTION)));

	// Return.
	return hr;
}

/**********************************************************
Function: bool CDx_Input::Init_DIAF(HWND hWnd)
Description: 
Parameters: None.
Returns: 
***********************************************************/
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;
	m_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;
}

/**********************************************************
Function: void CDx_Input::HandleAction(unsigned int uiAction, DWORD dwData)
Description: 
Parameters: 
Returns: None.
***********************************************************/
void CDx_Input::HandleAction(unsigned int uiAction, DWORD dwData)
{

	switch(uiAction)
	{
	case ACTION_LEFT:
		// The key for walking left.
		;
		break;
	case ACTION_RIGHT:
		// The key for walking right.
		;
		break;
	case ACTION_UP:
		// The key for walking up.
		;
		break;
	case ACTION_DOWN:
		// The key for walking down.
		;
		break;
	case ACTION_OK:
		// The key for interact/confirm.
		;
		break;
	case ACTION_CANCEL:
		// The key for dashing/cancelling.
		;
		break;
	case ACTION_MENU:
		// The key for the in-game menu.
		;
		break;
		// enclose this one in an ifdef __DEBUG conditional.
	case ACTION_FQUIT:
		// A special key for a fast exit.

		// shutdown time!
		Write_Log(E_NONE, "Recieved WM_DESTROY case Msg.  Shutting down...");
		g_pApp->m_bQuitting = true;
		PostQuitMessage(0);
		break;
	default:
		// This will ultimately contain error handling, but for now, give us a way out!
		Write_Log(E_NONE, "Recieved WM_DESTROY case Msg.  Shutting down...");
		g_pApp->m_bQuitting = true;
		PostQuitMessage(0);
		break;
	};

	// Added return.
	return;
}

/**********************************************************
Function: void CDx_Input::Update(void)
Description: Checks all polled devices attached to the user's computer
to see if any updated input has been recieved.
Parameters: None.
Returns: None.
***********************************************************/
void CDx_Input::Update(void)
{
	DIDEVICEOBJECTDATA pdiDevData[INPUT_DATA_LIMIT];//= NULL;	//?
	DWORD dwDevices = INPUT_DATA_LIMIT;

	// Prevent possible access violation.
	if(m_pDeviceAr)
	{
		// Iterate through the attached devices.
		for(int iIndex = 0; iIndex < m_iDevices; iIndex++)
		{
			// Poll the device for data. 
			m_pDeviceAr[iIndex]->Poll(); 

			// Retrieve the data.
			m_pDeviceAr[iIndex]->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), pdiDevData, &dwDevices, 0);

			// Iterate through the polled devices and carry out any necessary input
			// processing that needs doing on them.  Not the cleanest format (using all
			// those DWords... but it allows us to save on processing time by avoiding casting.
			for(DWORD dwIndex = 0; dwIndex < dwDevices; dwIndex++)
			{
				// Handle the actions the same regardless of what device returned them.
				HandleAction(pdiDevData[dwIndex].uAppData, pdiDevData[dwIndex].dwData);
			}
		}
	}

	// Added return.
	return;
}

/**********************************************************
Function: void CDx_Input::AcquireDevices(void)
Description: Acquire(obtain control of) the devices
Parameters: None.
Returns: None.
***********************************************************/
void CDx_Input::AcquireDevices(void)
{
	if(m_pDeviceAr)
	{
		for(int iIndex = 0; iIndex < m_iDevices; iIndex++)
			m_pDeviceAr[iIndex]->Acquire();
	}

	// Added return.
	return;
}

/**********************************************************
Function: void CDx_Input::UnacquireDevices(void)
Description: Unacquire(release control of) the devices
Parameters: None.
Returns: None.
***********************************************************/
void CDx_Input::UnacquireDevices(void)
{
	if(m_pDeviceAr)
	{
		for(int iIndex = 0; iIndex < m_iDevices; iIndex++)
			m_pDeviceAr[iIndex]->Unacquire();
	}

	// Added return.
	return;
}

/**********************************************************
Function: BOOL CALLBACK CDx_Input::DIEnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCE lpddi, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
Description: Callback function to map appropriate devices
Parameters: 
Returns: 
***********************************************************/
BOOL CALLBACK CDx_Input::DIEnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCEW lpddi, LPDIRECTINPUTDEVICE8W lpDevice, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
{
	HRESULT	hr = S_OK;
	CDx_Input* pInput = NULL;
	DWORD dwIndex = 0;
	
	pInput = reinterpret_cast<CDx_Input*>(pvRef);
	if(!pInput)
		return FALSE;

	// 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(pInput->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_FOREGROUND)");

			// Build the action map for the device. This will map each action to
			// the most appropriate function on the device.
			hr = lpDevice->BuildActionMap(&pInput->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 < pInput->m_diAF.dwNumActions; dwIndex++)
				{
					if(pInput->m_diAF.rgoAction[dwIndex].dwHow != DIAH_UNMAPPED)
						break;
				}

				if(dwIndex < pInput->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(&pInput->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(!pInput->m_pDeviceAr)
							pInput->m_pDeviceAr = GCC_NEW LPDIRECTINPUTDEVICE8[dwRemaining + 1];

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

						// Add a reference to this device, since DirectInput will 
						// release the device when we return.
						pInput->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;
}

/**********************************************************
Function: void CDx_Input::Close(void)
Description: 
Parameters: None.
Returns: None.
***********************************************************/
void CDx_Input::Close(void)
{
	{
		for(int iIndex = 0; iIndex < m_iDevices; iIndex++)
		{
			m_pDeviceAr[iIndex]->Unacquire();
			SAFE_RELEASE(m_pDeviceAr[iIndex]);	// experimental.
			//m_pDeviceAr[iIndex]->Release();
		}
		SAFE_DELETE_ARRAY(m_pDeviceAr);
	}

	SAFE_RELEASE(m_lpDI8);

	// Added return.
	return;
}

CDx_Input.h
[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.

protected:
	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 problem continues to elude me, and the hard-crash makes it pretty hard to debug. Please share any ideas you might have so I can resolve this issue as soon as possible. Any help is greatly appreciated. -HeavyBlade

Share this post


Link to post
Share on other sites
Advertisement
What is the app supposed to do? What does it do instead, before it goes down? Is the program flow normal up until the crash? At what exact location does it crash? Use a series of breakpoints to determine the location. Is there any other related info like problems you've had with it in the past?

Share this post


Link to post
Share on other sites
Well, it's a game, so it boots up directinput and checks for input on a per-frame basis. It does everything it's supposed to do - including enumerating the direct input devices properly up until the time of the crash. The crash I would estimate, occurs when user input is taken in for the first time. Not much in the way of related info, sorry.

"At what exact location does it crash? Use a series of breakpoints to determine the location."

Don't know yet. I'll look, but I'd prefer a response before then so I have some possibility of escaping a total system shutdown. Sigh.

Share this post


Link to post
Share on other sites
Quote:
Original post by HeavyBlade
Well, it's a game, so it boots up directinput and checks for input on a per-frame basis. It does everything it's supposed to do - including enumerating the direct input devices properly up until the time of the crash. The crash I would estimate, occurs when user input is taken in for the first time. Not much in the way of related info, sorry.

"At what exact location does it crash? Use a series of breakpoints to determine the location."

Don't know yet. I'll look, but I'd prefer a response before then so I have some possibility of escaping a total system shutdown. Sigh.


You need to add the reference to the device interface, not to DirectInput.

It sounds like you're making a DirectInput poll request to a disposed COM interface.

Share this post


Link to post
Share on other sites
Quote:
Original post by RomSteady
You need to add the reference to the device interface, not to DirectInput.

It sounds like you're making a DirectInput poll request to a disposed COM interface.


Thanks for the response, but can you please expand, and would a desposed COM interface really cause such a problem?

Share this post


Link to post
Share on other sites
Quote:
Original post by HeavyBlade
Quote:
Original post by RomSteady
You need to add the reference to the device interface, not to DirectInput.

It sounds like you're making a DirectInput poll request to a disposed COM interface.


Thanks for the response, but can you please expand, and would a desposed COM interface really cause such a problem?


You need to store lpddi in the callback as well, and AddRef it. (According to the SDK documentation, anyway.) The callback interface creates the object for you and passes it to you, but if you don't add a reference to it, it disposes of the object immediately because the reference count reaches 0.

And yes, a kernel level call to a disposed interface could cause all sorts of pain and/or suffering for your machine. Since it was disposed, COM could be placing an entirely different interface at the location that you're calling, and who knows what the hell would happen.

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!