Upcoming Events
5th Australasian Conference on Interactive Entertainment
12/3 - 12/5 @ Brisbane, Australia

2K Bot Prize
12/15 - 12/18 @ Perth, Australia

IEEE Symposium on Computational Intelligence and Games
12/15 - 12/18 @ Perth, Australia

IEEE Consumer Communications & Networking Conference
1/10 - 1/13 @ Las Vegas, NV

More events...


Quick Stats
4182 people currently visiting GDNet.
2240 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!



Link to us

  search:   

Hosting a C++ D3D engine in C# Winforms


Creating a simple D3D management class

I am not going to spend too much time explaining how to write a D3D9 rendering framework, but lets write a simple wrapper class to handle this for us. This class is very similar to the DXUT demo framework, only extremely simplified, and not able to handle full screen rendering.

class CD3DManager
{
public:
	CD3DManager();
	virtual ~CD3DManager();

	HRESULT  Initialize(HWND hWnd, BOOL bRenderOnPaint, BOOL bHookWnd);
	HRESULT  Shutdown();                          
	HRESULT  Reset();
	HRESULT  ProcessFrame();

	HRESULT  Resize();
	HRESULT  HandleW32Msg(HWND hWnd, UINT message,WPARAM wparam, LPARAM lparam);

protected:
	virtual HRESULT OnInit();
	virtual HRESULT OnShutdown();
	virtual HRESULT OnUpdate(double dTime, double dElapsedTime);
	virtual HRESULT OnRender(LPDIRECT3DDEVICE9 pd3dDevice);
	virtual HRESULT OnMsg(HWND hWnd, UINT message,WPARAM wparam, LPARAM lparam);
	virtual HRESULT OnInitDeviceObjects(LPDIRECT3DDEVICE9 pd3dDevice);
	virtual HRESULT OnRestoreDeviceObjects();
	virtual HRESULT OnInvalidateDeviceObjects();
	virtual HRESULT OnDeleteDeviceObjects();
};
Given an HWND we initialize our D3D device. When the device is lost we call Reset, when we wish to update and draw a frame we call ProcessFrame, and so on. We then add some virtual functions that a derived class will override to receive essential events it must handle.
  • OnInit: Called after the device has been initialized, a good place to load resources, and create objects
  • OnShutdown: Called right before the device is about to be destroyed. Destroy or release what you created or loaded in OnInit
  • OnUpdate: Update your simulation state
  • OnRender: Render your frame
  • OnMsg: Handle windows messages
  • OnInitDeviceObjects: Handle initialization of device objects
  • OnRestoreDeviceObjects: Handle restoration of device objects
  • OnInvalidateDeviceObjects: Handle invalidation of device objects
  • OnDestroyDeviceObjects: Handle destruction of device objects

Hooking an HWND for messages

One of the issues with hosting your rendering display in a C# Winforms application is gaining access to the appropriate windows events that must be handled. For example the WM_RESIZE and/or WM_EXITSIZEMOVE message(s) must be handled with care, since resizing our host HWND requires a D3D device reset. There is also the potential of many more windows message we might want to handle.

One approach is to add to the appropriate event delegate handler to our Winforms panel like this:

panel1.Resize += new EventHandler(myForm.Panel1_Resize);
One problem with this approach is that there are many events that Winforms does not expose in this manner. Another issue is that you might have existing input code that responds to raw windows messages (like I did). In light of this I have elected to use a low-level Win32 message hooking approach.

Essentially we can monitor all messages a window produces, by replacing its WndProc with one of our own, then invoking its original WndProc. Like this:

BOOL CD3DManager::hookWindow( HWND hWndNew)
{
	m_bHookedWnd = TRUE;

	//adds  HWND to a static map associating that HWND with this instance of D3DManager
	addHWNDPtr(m_hDevWindow); 

if(m_lpfnChildWndProc = (WNDPROC)SetWindowLong( m_hDevWindow, GWL_WNDPROC,    (LONG)RenderWndProc ) )
		return TRUE;

	return FALSE;
}

LRESULT CALLBACK CD3DManager::RenderWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT lRet = 0;

	CD3DManager* pThis = getHWNDPtr(hwnd);
	if(pThis==NULL)  return lRet;

	switch(uMsg)
	{
	case WM_EXITSIZEMOVE: 
pThis->Resize(); break;
	case WM_PAINT:
		if(pThis->m_bRenderOnPaint)  pThis->ProcessFrame(); break;
	case WM_CLOSE:
		pThis->unhookWindow(); break;
	}

	pThis->HandleW32Msg(hwnd,uMsg,wParam,lParam);
	lRet = CallWindowProc(pThis->m_lpfnChildWndProc, hwnd, uMsg, wParam,lParam);
	return lRet;
}
One issue with overriding a window’s WndProc is that, the function itself is a static function of type CALLBACK, with no implicit this pointer sent along with it. There is no direct way of associating the system’s call to our WndProc (with a specific HWND), to a specific instance of our CD3Dmanager class. One way to accomplish this is through the use of a static map member variable that links a HWND to a CD3Dmanager*. Our WndProc is called and we use the given HWND to find the corresponding object instance. We add to this map via the supplied addHWNDPtr function and retrieve from this map via the getHWNDPtr function.



Page 2


Contents
  Introduction
  Page 1
  Page 2
  Page 3
  Page 4

  Source code
  Printable version
  Discuss this article