Crash when releasing LPDIRECTINPUTDEVICE8

Started by
9 comments, last by Sc4Freak 16 years, 3 months ago
Here's my code:

#include <windows.h>
#pragma comment( lib, "Winmm.lib" )
#define DIRECTINPUT_VERSION 0x0800
#include <Dinput.h>
#pragma comment( lib, "dinput8.lib" )
#pragma comment( lib, "dxguid.lib" )

LPDIRECTINPUT8 dinput = NULL;
LPDIRECTINPUTDEVICE8 keyboard = NULL;
bool keydown = false;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
void RunGame(HWND);

//Globals
HWND g_hwnd;
HDC g_hdc;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("GameLoop") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass(&wndclass))
     {
          MessageBox (NULL, TEXT ("Could not register window!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }

     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("BETA Game Loop"),    // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
	 //GAME INIT here
	/////////////////////////////////DirectInput/////////////////////////////////
	 //LPDIRECTINPUT8 dinput  = NULL;    //DirectInput object
	 //LPDIRECTINPUTDEVICE8 keyboard;     //DirecInput keyboard device
	 DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&dinput, NULL);  //Create DirectInput object

	 dinput->CreateDevice(GUID_SysKeyboard, &keyboard, NULL);   //Create the device for the keyboard

	 keyboard->SetDataFormat(&c_dfDIKeyboard);      //Setting the data of the keyboard
	 keyboard->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);  //Setting cooperative level for the keyboard
	 keyboard->Acquire();				//Acquire keyboard before use
	
	 
	 //char keys[256];                    //Used for polling the keyboard
	 //keyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys);        //Poll the keyboard so we can retrieve key strokes
	////////////////////////////////////////////////////////////////////////////


	bool looping = true;
    while (looping == true)
     {
		 if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			//Check for WM_QUIT call
			if(msg.message == WM_QUIT)
				looping = false;
			//Decode msgs
			TranslateMessage (&msg) ;
			DispatchMessage (&msg) ;			
		}
		 //Call game function here
		 RunGame(hwnd);
     }

	 //Clean up game here
     return (int)msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{     
	 //g_hwnd = hwnd;
	 //g_hdc = GetDC(hwnd);
		
     switch (message)
     {              
     case WM_DESTROY:
		 if(keyboard != NULL)
		 {
		     keyboard->Unacquire();
			 //keyboard->Release();			 
		 }

		 if(dinput != NULL)
		 {dinput->Release();}
         
		 PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}



void RunGame(HWND hwnd)
{
	
	 char keys[256];                    //Used for polling the keyboard
	keyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys);        //Poll the keyboard so we can retrieve key strokes
	
	 

	if(keys[DIK_UPARROW] & 0x80 && keys[DIK_LEFTARROW] & 0x80)
	 {
		 SetWindowText(hwnd, TEXT("UP LEFT"));
		 keydown = true;
	 }
	 else if(keys[DIK_UPARROW] & 0x80 && keys[DIK_RIGHTARROW] & 0x80)
	 {
		 SetWindowText(hwnd, TEXT("UP RIGHT"));
		 keydown = true;
	 }
	 else if(keys[DIK_DOWNARROW] & 0x80 && keys[DIK_LEFTARROW] & 0x80)
	 {
		 SetWindowText(hwnd, TEXT("DOWN LEFT"));
		 keydown = true;
	 }
	 else if(keys[DIK_DOWNARROW] & 0x80 && keys[DIK_RIGHTARROW] & 0x80)
	 {
		 SetWindowText(hwnd, TEXT("DOWN RIGHT"));
		 keydown = true;
	 }	 
	 else if(keys[DIK_UPARROW] & 0x80)
	 {
		 SetWindowText(hwnd, TEXT("UP"));
		 keydown = true;
	 }
	 else if(keys[DIK_DOWNARROW] & 0x80)
	 {
		 SetWindowText(hwnd, TEXT("DOWN"));
		 keydown = true;
	 }
	 else if(keys[DIK_LEFTARROW] & 0x80)
	 {
		 SetWindowText(hwnd, TEXT("LEFT"));
		 keydown = true;
	 }
	 else if(keys[DIK_RIGHTARROW] & 0x80)
	 {
		 SetWindowText(hwnd, TEXT("RIGHT"));
		 keydown = true;
	 }
	 else if((VK_UP) && (keydown == true))
	 {
		 SetWindowText(hwnd, TEXT("BETA Game Loop"));
		 keydown = false;
	 }

	

	
	
}

The program runs and compiles perfectly with no errors or warnings until I exit and the code hits:
          keyboard->Release();
The program then crashes. I came to the conclusion that
	keyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys); 
is causing the problem. If I empty the RunGame() function, the program exits fine without crashing. In the meanwhile, I am forced to run the program without releasing the keyboard. What is the problem? [Edited by - french_hustler on January 8, 2008 3:28:11 PM]
Advertisement
A couple of notes:

1) Put long code snippets in source tags. It makes them easier to read (see the FAQ if you don't know how)

2) You're not checking the function's return value, which might tell you more about the problem.

3) Usually the way to release COM interfaces is :

template <class T>void safeRelease(T *&p) {    if (p) {        p->Release();        p = NULL;    }}


This makes sure that it's okay to call safeRelease() on a NULL pointer, or call safeRelease() on a pointer more than once.

4) You should only use DirectInput to read input from a joystick, not from the keyboard and mouse. More.

EDIT: Fixed stupid typos.

[Edited by - Gage64 on January 8, 2008 4:01:01 AM]
Quote:Original post by Gage64
4) You should only use DirectInput to read input from a joystick, not from the keyboard and mouse. More.
Several more reasons.

Using DirectInput for keyboard or mouse input is about as good an idea as using BIOS interrupts for file IO.
I appreciate you guys teaching me why DirectInput is not a good idea when getting input from the keyboard, but this is in no way a full blow-out application. I wrote this program merely to learn about DirectX. I will from now on use the Win32 API, but the problem is still there.

I have tried to do a "safe release" as mentioned above and in the Directx documentation, yet the program still crashes on exit.

Why?

Thanks in advance.
You have only commented on my last two suggestions, but have you applied the other two (well I can see that you have not applied the first [smile])?
Quote:Original post by Gage64
You have only commented on my last two suggestions, but have you applied the other two (well I can see that you have not applied the first [smile])?


Ok. I fixed my post :)


Alright.
So I used hr = keyboard->GetDeviceState();

hr returns S_OK.

When debugging, it seems that the program jumps back into RunGame() after WM_DESTROY. So it's releasing the device, but by jumping back in RunGame(), it goes through the GetDeviceState once more which causes a crash.

Why?

[Edited by - french_hustler on January 8, 2008 3:30:30 PM]
What's the exact error you get? If it's an access violation reading location 0x00000000 (Or a close address), it's probably dereferencing a null pointer, meaning your keyboard is null. If the address looks valid, it's probably been Release()d already.

If you look at the value of the keyboard pointer, and the 3 vtable entries in the debugger, what are they? (0xfeeefeee = already released)
Error when debugger breaks at the GetDeviceState line in RunGame after having exited the application is:

Unhandled exception at 0x00411924 in GameLoop.exe: 0xC0000005: Access violation reading location 0xfeeeff12.


Value for keyboard is: 0x0015dd84 at the time of the error.

I don't quite understand what you mean by "3 vtable entries".
It seems that I have found a plausible solution.

Instead of cleaning up Directx in WndProc(), I have created an other function to take care of the job which I call right before
 return (int)msg.wParam ;
in WinMain().

This seems to do the trick.

Thanks.
Yes, you had an ordering issue. When the window handler gets the "destroyed" msg, your app continues to process another frame. Since keyboard has been released, you're getting a crash.

Using the "safe release" makes these bugs easier to track, that's all. Won't solve a crash, but it would make it obvious that you were accessing a bad pointer, instead of one that looks like it could be valid (0x0015dd84).

Cheers.

This topic is closed to new replies.

Advertisement