Strange Event Receiver Crash

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
Xharock
Posts: 71
Joined: Wed May 10, 2006 3:50 pm

Strange Event Receiver Crash

Post by Xharock »

Well I've used Irrlicht before but now I'm trying to wrap it into my own "Application" Class. Everything was working OK until I implemented an Event Receiver. My Event Receiver Class is like this:

Code: Select all

class cEvent_c : public IEventReceiver
{
public:	
	int m_mouseX, m_mouseY;
	virtual bool OnEvent(SEvent event);	
};

Code: Select all

bool cEvent::OnEvent(irr::SEvent event)
{
	if(event.EventType)
	{
		//Keyboard input
		if(event.EventType == EET_KEY_INPUT_EVENT)
		{
			switch(event.KeyInput.Key)
			{
			default:
				return true;
			}
		}
		//Mouse input
		else if(event.EventType == EET_MOUSE_INPUT_EVENT)
		{
			m_mouseX = event.MouseInput.X;
			m_mouseY = event.MouseInput.Y;

			return true;
		}
	}
	return false;
}
My Application class is like this:

Code: Select all

class cApp_c
{
private:

	//Irrlicht
	IrrlichtDevice			*m_irrDevice;
	IVideoDriver			*m_videoDriver;
	ISceneManager		*m_sceneMan;
	IGUIEnvironment	*m_GUIEnv;
	cEvent					m_event;
	//FMOD
	FMOD::System		*m_soundSystem;

public:	
	//Constructor/Deconstructor
	cApp_c();
	~cApp_c();
	//Get Methods
	IrrlichtDevice			*getIrrlichtDevice();
	IVideoDriver			*getVideoDriver();
	ISceneManager		*getSceneManager();
	IGUIEnvironment	*getGUIEnvironment();
	cEvent					getEventReceiver();
	FMOD::System		*getSoundSystem();
	bool						isRunning();	
	int						getMouseX();
	int						getMouseY();
	//Set Methods
	void						setIrrlichtDevice(IrrlichtDevice *newDevice);
	void						setVideoDriver(IVideoDriver *newDriver);
	void						setSceneManager(ISceneManager *newSceneMan);
	void						setGUIEnvironment(IGUIEnvironment *newGUIEnv);
	void						setSoundSystem(FMOD::System *newSoundSystem);
};
Now I have an initialization function which sets up the Irrlicht engine etc.

Code: Select all

//Create Irrlicht device
	cEvent eventReceiver = app->getEventReceiver();
	app->setIrrlichtDevice(createDevice(EDT_DIRECT3D9, dimension2d<s32>(1024, 768), 32, false, true, true, &eventReceiver));
So I thought that would be enough to get the Event Receiver working. But when I run my program it outputs the first 4 lines of the log thing:

Irrlicht Engine version blah
Microsoft Windows XP
Using renderer Direct3D 9.0
RADEON 9800 SERIES

and it will then crash. After doing some debugging I have found that this piece of code in the engine crashes:

Code: Select all

//! send the event to the right receiver
void CIrrDeviceStub::postEventFromUser(SEvent event)
{
	bool absorbed = false;

	if (UserReceiver)
		absorbed = UserReceiver->OnEvent(event);

	if (!absorbed && GUIEnvironment)
		absorbed = GUIEnvironment->postEventFromUser(event);

	if (!absorbed && SceneManager)
		absorbed = SceneManager->postEventFromUser(event);
}
With the error: Unhandled exception at 0x1019dcd9 (Irrlicht.dll) in Metal Gear.exe: 0xC0000005: Access violation reading location 0xccccccd0.

Theres something wrong with UserReceiver:

Image

But I'm not sure what. Anyone got any ideas?
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

The problem is that you are creating an event receiver on the stack. When your initialization function is called, a temporary copy of your event receiver is copied. A pointer to that event receiver is passed to the device, and then when the function exits, the event receiver is destroyed. Now the irrlicht device has a pointer to an object that has been destroyed. That is A Very Bad Thing(tm).

The simplest thing to do would be to change the getEventReceiver() method to return a pointer to your internal event receiver...

Code: Select all

IEventReceiver* cApp_c::getEventReceiver()
{
  return &m_event;
}
You will have to adjust the initialization code and the function declaration.

Travis
Xharock
Posts: 71
Joined: Wed May 10, 2006 3:50 pm

Post by Xharock »

Ahh it's working now, cheers man.

It sounds a bit nooby but could you explain what you mean by "creating an event receiver on the stack"? I understand why it was going wrong know I'm just not sure what you mean by this.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

There are a few places that you can allocate objects. You can allocate on the heap, the stack or the global area. Here are some examples...

Code: Select all

#include <stdio.h>

class MyClass
{
public:
  MyClass(const char* where)
    : Where(where)
  {
    fprintf(stderr, "Allocated %s MyClass \n", Where);
  }

  ~MyClass()
  {
    fprintf(stderr, "Destroyed %s MyClass\n", Where);
  }

  const char* Where;
};

MyClass global_myClass("global");

void foo()
{
  MyClass stack_myClass("stack");
}

void bar()
{
  static MyClass static_myClass("static");
}

int main()
{
  MyClass* heap_myClass = new MyClass("heap");
  foo();
  delete heap_myClass;
  return 0;
}
The above code should compile and run if you want to step into it with a debugger to see what is going on, or if you just want to run it to see how the object lifetimes are affected by how an object is declared.

Here is the short of it. When you create an object with the function new the object is said to be allocated on the heap. You explicitly control the lifetime of the object. It will be completely destroyed when you call delete on the pointer.

When you create an object using the static keyword, or you create it at global scope [not inside a function], that object is said to have static storage duration and is allocated from the global store. That object is implicitly destroyed by the runtime environment when the program exits.

If you create an object with neither of these methods, then it is allocated on the stack, and is said to have automatic storage duration. The object will be automatically destroyed when the scope that it is created in is exited. That means that if you declare a variable inside a for loop, the variable will be destroyed when the for loop scope ends [where the } is].

Some of this can get confusing when you add pointers to the mix. Look at the definition of main in the code above. The variable heap_myClass is actually an automatic variable on the stack that points to a heap object. If the delete wasn't called, the variable heap_myClass would be destroyed [it is on the stack], but the object that it points to would not be destroyed [it is on the heap and is destroyed via a call to delete].

All of this is explained in almost every C++ book out there.

Travis
Xharock
Posts: 71
Joined: Wed May 10, 2006 3:50 pm

Post by Xharock »

Thanks, I think the book I got must have skimmed over it. Damn it. Oh well I understand now.
Post Reply