Sign in to follow this  
chadsxe

How come when I release my Direct3D device and interface my program crashes...

Recommended Posts

Hello, I am just running through some tutorials gathering knowledge about DirectX and I have ran into my first issue. When I go to release any Direct3D object (LPDIRECT3D9, LPDIRECT3DDEVICE9, LPD3DXSPRITE) that I have created my program crashes. Main.h
#include "E_Global.h"

// Main application instances
HINSTANCE g_hInst; // Global instance handle
HWND g_hWnd; // Global window handle

// Function to display an error message
void AppError(BOOL Fatal, const char *Text);

// Message procedure
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

// Functions to register and unregister windows’ classes
bool RegisterWindowClasses(HINSTANCE hInst);
bool UnregisterWindowClasses(HINSTANCE hInst);

// Function to create the application window
HWND CreateMainWindow(HINSTANCE hInst,int nCmdShow);

// Functions to init, shutdown, and handle per-frame functions
bool DoInit();
bool DoShutdown();
bool DoPreFrame();
bool DoFrame();
bool DoPostFrame();

// Test class objects
E_Graphics eGraphics;

// the entry point 
int WINAPI WinMain(HINSTANCE hInst,
				   HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine,
				   int nCmdShow){
	MSG Msg;

	// Save application instance
	g_hInst = hInst;

	// Register window classes - return on FALSE
	if(RegisterWindowClasses(hInst) == false)
		return false;

	// Create window - return on FALSE
	if((g_hWnd = CreateMainWindow(hInst,nCmdShow)) == NULL)
		return true;

	// Do application initialization - return on FALSE
	if(DoInit()) {
		// Enter the message pump
		ZeroMemory(&Msg, sizeof(MSG));
		
		while(Msg.message != WM_QUIT) {

			// find out the starting time of each loop
			DWORD starting_point = GetTickCount();

			// Handle Windows messages (if any)
			if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
				// translate keystroke messages into the right format
				TranslateMessage(&Msg);
				// send the message to the WindowProc function
				DispatchMessage(&Msg);
			}
			/*else {*/
				// Do pre-frame processing, break on FALSE return value
				if(DoPreFrame() == FALSE)
					break;
				// Do per-frame processing, break on FALSE return value
				if(DoFrame() == FALSE)
					break;
				// Do post-frame processing, break on FALSE return value
				if(DoPostFrame() == FALSE)
					break;
			/*}*/

			// wait until 1/40th of a second has passed
			while ((GetTickCount() - starting_point) < 25);
		}
	}
	// Do shutdown functions
	DoShutdown();

	// Unregister window
	UnregisterWindowClasses(hInst);

	return TRUE;
}

//helper function to set up the window properties
bool RegisterWindowClasses(HINSTANCE hInst){

	// this struct holds information for the window class
	WNDCLASSEX wcex;

	ZeroMemory(&wcex, sizeof(WNDCLASSEX));

	// fill in the struct with the needed information
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = (WNDPROC)WindowProc;
	wcex.hInstance = hInst;
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.lpszClassName = CLASS_NAME;

	//registering the window
	RegisterClassEx(&wcex);

	return true;
}

//helper function to unregister class
bool UnregisterWindowClasses(HINSTANCE hInst){
	// Unregister the window class
	UnregisterClass(CLASS_NAME, hInst);
	return true;
}

//helper function to create windows
HWND CreateMainWindow(HINSTANCE hInst,int nCmdShow){
	HWND hWnd;
	// Create the Main Window
	hWnd = CreateWindowEx(NULL,
						CLASS_NAME, 
						WINDOW_TITLE, 
						WNDTYPE, 
						0,0, 
						SCREEN_WIDTH, SCREEN_HEIGHT, 
						NULL, NULL, 
						hInst, NULL);
	//was there an error creating the window?
	if(!hWnd)
		return NULL;
	
	// Show and update the window
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	
	// Return the window handle
	return hWnd;
}

//helper function to display errors
void AppError(BOOL Fatal, const char *Text){
	char CaptionText[12];

	// Build the message box caption based on fatal flag
	if(Fatal == false)
		strcpy_s(CaptionText, "Error");
	else
		strcpy_s(CaptionText, "Fatal Error");

	//Display the message box
	MessageBox(NULL, 
			   Text, 
			   CaptionText, 
			   MB_OK | MB_ICONEXCLAMATION);

	// Post a quit message if error was fatal
	if(Fatal)
		PostQuitMessage(0);
}

// The message procedure - empty except for destroy message
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
	// sort through and find what code to run for the message given
	switch(message) {
		// this message is read when the window is closed
		case WM_DESTROY:{
			// close the application entirely
			PostQuitMessage(0);
			return 0;
		}break;
	}
	// Handle any messages the switch statement didn't
	return DefWindowProc(hWnd, message, wParam, lParam);
}

// Perform application initialization functions here
// such as those that set up the graphics, sound, network, etc.
// Return a value of TRUE for success, FALSE otherwise.
bool DoInit(){
	eGraphics.Init(g_hWnd);
	return true;
}
// Perform application shutdown functions here
// such as those that shut down the graphics, sound, etc.
// Return a value of TRUE for success, FALSE otherwise
bool DoShutdown(){
	eGraphics.Shutdown();
	return true;
}
// Perform pre-frame processing, such as setting up a timer.
// Return TRUE on success, FALSE otherwise.
bool DoPreFrame(){
	return true;
}
// Perform per-frame processing, such as rendering.
// Return TRUE on success, FALSE otherwise.
bool DoFrame(){
	eGraphics.ClearScreen();
	eGraphics.BeginScene();
	eGraphics.EndScene();
	eGraphics.Display();
	return true;
}
// Perform post-frame processing, such as time synching, etc.
// Return TRUE on success, FALSE otherwise.
bool DoPostFrame(){
	return true;
}

E_Graphics.cpp
#include "E_Global.h"

E_Graphics::E_Graphics(void){
	d3d = NULL;    
	d3ddev = NULL; 
	d3dspt = NULL; 
}

E_Graphics::~E_Graphics(void){
	Shutdown();
}

bool E_Graphics::Init(HWND hWnd){
	// make sure everything is clean;
	//Shutdown();

	// create Direct3D interface
	d3d = Direct3DCreate9(D3D_SDK_VERSION);

	// create struct to hold device information
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = true;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferWidth = SCREEN_WIDTH;
	d3dpp.BackBufferHeight = SCREEN_HEIGHT;

	// create a device class using this information and the info from the d3dpp stuct
	d3d->CreateDevice(D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp,
		&d3ddev);

	// Init sprite interface
	D3DXCreateSprite(d3ddev, &d3dspt);    // create the Direct3D Sprite object
	return true;
}

bool E_Graphics::BeginScene(){
	// is device even active?
	if(d3ddev == NULL)
		return false;
	// begin scene
	d3ddev->BeginScene();
	return true;
}
bool E_Graphics::EndScene(){
	// Error checking
	if(d3ddev == NULL)
		return false;
	// End the scene
	d3ddev->EndScene();
	return true;
}
bool E_Graphics::BeginSprite(){
	// Error checking
	if(d3dspt == NULL)
		return false;
	// begin sprite drawing
	d3dspt->Begin(NULL);
	return true;
}
bool E_Graphics::EndSprite(){
	// Error checking
	if(d3dspt == NULL)
		return false;
	// end sprite drawing
	d3dspt->End();
	return true;
}

// clear screen to solid color
bool E_Graphics::ClearScreen(){
	// Error Checking
	if(d3ddev == NULL)
		return false;
	// clear the window to a deep blue
	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(100, 100, 0), 1.0f, 0);
	return true; 
}

// display the created frame on screen
bool E_Graphics::Display(){
	// Error checking
	if(d3ddev == NULL)
		return false;
	// Display to screen
	d3ddev->Present(NULL, NULL, NULL, NULL);
	return true;
}


//---------------------------
// Accesors Funtions
//---------------------------
LPDIRECT3D9 E_Graphics::GetDirect3DCOM(){
	return d3d;
}

LPDIRECT3DDEVICE9 E_Graphics::GetDeviceCOM(){
	return d3ddev;
}

LPD3DXSPRITE E_Graphics::GetSpriteCOM(){
	return d3dspt;
}

bool E_Graphics::Shutdown(){
	d3dspt->Release();
		d3dspt = NULL;

	d3ddev->Release();
		d3ddev = NULL;
	
	d3d->Release();
		d3ddev = NULL;
	return true;
}
E_Graphics.h
#pragma once

class E_Graphics{

	private:
		LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
		LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
		LPD3DXSPRITE d3dspt;    // the pointer to our Direct3D Sprite interface

	public:

		bool Init(HWND hWnd);
		bool Shutdown();

		LPDIRECT3D9       GetDirect3DCOM();
        LPDIRECT3DDEVICE9 GetDeviceCOM();
        LPD3DXSPRITE      GetSpriteCOM();

		bool BeginScene();
		bool EndScene();

		bool BeginSprite();
		bool EndSprite();
		
		bool ClearScreen();

		bool Display();

		E_Graphics(void);
		~E_Graphics(void);
};

//E_Globa.h
#pragma once
#ifndef _E_GLOBAL_H_
#define _E_GLOBAL_H_

// Windows includes
#include <windows.h>

// Standard ANSI-C includes
#include <stdio.h>

// DirectX includes
#include <d3d9.h>
#include <d3dx9.h>

// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

// Engine Includes
#include "E_Graphics.h"

// Application window dimensions, type, class and window name
#define WNDTYPE			WS_OVERLAPPEDWINDOW
#define CLASS_NAME		"Class Name"
#define WINDOW_TITLE	"Window Name"
#define SCREEN_WIDTH	800
#define SCREEN_HEIGHT	600


#endif

 

As you can see it is pretty standard stuff. Init, Run in loop, Shutdown. I am not sure why this is happening though. Any suggestion would be appreciated. Regards Chad [Edited by - chadsxe on August 3, 2007 7:15:26 AM]

Share this post


Link to post
Share on other sites
At the point where you call Release() the pointers could be null, or bad in general therefore dereferencing them leads to BANG! I would also recommend looking at CComPtr in <atlbase.h> as it wraps the LPDIRECT3D... COM pointer and makes things much safer in general.

Dave

Share this post


Link to post
Share on other sites
Well, the point is that you just need to check to avoid dereferencing a null pointer. Also, the crash you are experiencing may be D3D crashing on a memory leak. Make sure that you have D3D in debug mode, all debug settings on or maximum and lastly check your output window for information.

Share this post


Link to post
Share on other sites
Heck...I am so lost...

It seems like I have the defices and the interface when I am debugging. I just updated my post above with all my recent code. Like I said when I get to ShutDown(); it crashes.

Regards

Chad

Share this post


Link to post
Share on other sites
Quote:
Original post by chadsxe
Heck...I am so lost...

It seems like I have the defices and the interface when I am debugging. I just updated my post above with all my recent code. Like I said when I get to ShutDown(); it crashes.

Regards

Chad



Quote:
bool E_Graphics::Shutdown(){
d3dspt->Release();
d3dspt = NULL;

d3ddev->Release();
d3ddev = NULL;

d3d->Release();
d3ddev = NULL;
return true;
}

What if d3dspt is null already? You dereference it and then call a function, so your program crashes. Assuming the graphics object is global, it gets shut down twice - once in the DoShutdown() call, once in the global's destructor.

You probably want:

bool E_Graphics::Shutdown()
{
if(d3dspt) d3dspt->Release();
d3dspt = NULL;

if(d3ddev) d3ddev->Release();
d3ddev = NULL;

if(d3d) d3d->Release();
d3d = NULL; // You have a typo on this line in your original code too...

return true;
}

Share this post


Link to post
Share on other sites
[quote]Original post by Evil Steve

What if d3dspt is null already? You dereference it and then call a function, so your program crashes. Assuming the graphics object is global, it gets shut down twice - once in the DoShutdown() call, once in the global's destructor.
[quote]

So you code code work but I am still not catching where it is being derefernced beffor ShutDown() is called. The graphics object is not global and is private to the class object. I am assuming Shutdown() is actually dereferncing before the destructor, correct? Sorry if I sound like a moron. I am stil learning.

Regards

Chad

Share this post


Link to post
Share on other sites
The graphics object IS global!

main.cpp

// Test class objects
E_Graphics eGraphics;


The declaration of eGraphics here is not inside any function, thus it is global.
At program shutdown time (after main/winmain exists) all global variables that have destructors (in this case E_Graphics object has the destructor function called ~E_Graphics) will be destroyed, and their destructors will be called.
In your program, you call shutdown before WinMain exists, and then shutdown is called again by the destructor:

E_Graphics::~E_Graphics(void){
Shutdown();
}


But obviously the d3ddev and d3dspt are now null, so.. that's it.

Share this post


Link to post
Share on other sites
At the end of WinMain, you call DoShutdown(). DoShutdown() calls eGraphics.Shutdown(); That destroys your D3D objects and sets them to null.

Then, somewhere eGraphics is destroyed (It is a global, surely? otherwise where does this eGraphics object appear from?). The destructor for the E_Graphics class calls E_Graphics::Shutdown(), which tries to destroy your D3D objects, but since they've already been set to NULL by the last call to Shutdown(), you access a null pointer and it all goes tits up.

Share this post


Link to post
Share on other sites
Your real problem is that when eGraphics goes out of scope (at the end of the program) it's destructor E_Graphics::~E_Graphics is called automatically. As you can see if you are calling DoShutdown() in Main.h this is going to release everything once then when eGraphics is destructed it will be called again automatically.

Instead of having "E_Graphics eGraphics;" global I would recommend putting it inside WinMain somewhere near the top. This would mean that when eGraphics goes out of scope (at the end of WinMain eGraphics will clean itself up automatically.

One possible problem with this approach is that UnregisterWindowClasses(hInst); is called before eGraphics destructor I don't know whether this is a problem or not but it can be resolved using new. If instead of "E_Graphics eGraphics;" you use "E_Graphics* eGraphics = new E_Graphics;" this then means you then need to delete it when you are done (before UnregisterWindowClasses). When you delete it the destructor will be called "delete eGraphics;". And of course anything that uses eGraphics will need to change from "eGraphics.blah" to "eGraphics->blah".

Isn't C++ fun :)


Crap I took way too long writing that people beat me to it :P

Share this post


Link to post
Share on other sites
There should be some mechanism in this web that if you are in the middle of writing a reply, you would be notified immediately when someone else replies.
Then you can check it out and maybe abort your reply...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this