Learning C++ and DirectX: Step one, create a window.

I planned, quite some time ago now, to start writing a series covering my journey, well I’ve finally gotten round to the first bit of it. Hopefully I’ll write a bit more frequently than I have recently, but we’ll see – I’m not making any promises.

I’ve bought some books on C++ and DirectX and have started working through them. The book I am working through at the moment Introduction to 3D Game Programming with DirectX 10, has based its demos on a small, bespoke application framework. Whilst this is all well and good, I find that I learn by doing, by writing the programs myself, rather than reviewing demo code. So, I’ll be starting from scratch, writing my own, similar application framework, following the lessons in the book, and blogging about it here. So, onto “Step one, create a window”.

Creating the Visual Studio Project

To start with, create a new Win32 project in Visual C++. Do this by selecting File -> New -> New Project, then by selecting Win32 in the Project Types pane, and Win32 in the Project Templates pane. Give it a name, and a location, and click OK. You’ll then be presented with a wizard. Click Next, then tick the box which says “Empty project”. You should now have a solution with a single project in it. The project should contain three folders “Header Files”, “Resource Files” and “Source Files”.

Figure 1: Creating a new Win32 project in Visual C++
Figure 1: Creating a new Win32 project in Visual C++

Now we can start coding. We’ll start by making a flat, single-file program which throws up a window, and then we’ll go about abstracting the window functionality into a class. We could have got Visual Studio to create a whole bunch of code that creates the window for us, but that skips the learning process, and gives us over-complicated code.

Create Your C++ File

I’ve uploaded the code for the first stage of our lesson here.

I’ll go through it section by section, although I’ve stripped out the comments because I’m giving an explanation here.

#include <windows.h>

This tells the C++ compiler to include the headers for the Win32 API. This gives us all the declarations we need to write code against the Win32 API. Angle brackets indicate that the file is in one of the include path directories. Alternatively we could use speech marks, and give a file name relative to our project folder.

HWND MainWindowHandle = 0;

This declares our window handle. It’s a variable which will hold an unique id for our window when we get windows to create it.

bool InitWindow(HINSTANCE hInstance, int showCmd);
int Run();
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

We declare the prototypes for some functions:

  • InitWindow will be the function which initialises our window and throws it onto the screen.
  • Run will handle the message queue.
  • WndProc is called while handling the message queue, and actually handles the messages we want to handle (like when the window closes)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd)
{
    if ( !InitWindow( hInstance, nShowCmd ) )
        return 0;

    return Run();
}

This is our entry-point. WinMain, is the windows equivalent of main(). All we do here is initialise our window and, if it was successful, start handling messages.

bool InitWindow(HINSTANCE instanceHandle, int showCmd)
{
	WNDCLASS wc;
	wc.style			= CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc			= WndProc;
	wc.cbClsExtra			= 0;
	wc.cbWndExtra			= 0;
	wc.hInstance			= instanceHandle;
	wc.hIcon			= LoadIcon(0, IDI_APPLICATION);
	wc.hCursor			= LoadCursor(0, IDC_ARROW);
	wc.hbrBackground		= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName		        = 0;
	wc.lpszClassName		= L"BasicWndClass";

	if(!RegisterClass(&wc))
	{
		MessageBox(0, L"RegisterClass FAILED", 0, 0);
		return false;
	}

	MainWindowHandle = CreateWindow(
		L"BasicWndClass",
		L"Basic Win32 Window",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		0,
		0,
		instanceHandle,
		0);

	if ( MainWindowHandle == 0 )
	{
		MessageBox(0, L"CreateWindow FAILED", 0, 0);
		return false;
	}

	ShowWindow(MainWindowHandle, showCmd);
	UpdateWindow(MainWindowHandle);

	return true;
}

This is where a bunch of the magic happens. In the first “paragraph” we create a structure and fill it out. This structure defines a class. If you think of the window you see on screen as a class, we’re defining that class here. (see the cpp file for the specifics, its got comments in). The next part registers the class with windows, effectively we’re saying “hey Windows, here’s a window class definition that I want to be able to make instances (windows) of.” After we’ve registered the class we can create a window from it with our call to CreateWindow. The parameters are the class name, the window name, the window style, the x, y, width and height, the handle of the parent, the handle of the menu, the handle of the windows application instance, and potential extra parameters. If that worked we continue onwards to show, and update the window. Simple.

int Run()
{
	MSG msg = {0};

	while ( msg.message != WM_QUIT )
	{
		if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{

		}

	}

	return (int)msg.wParam;

}

This method effectively loops until we get a WM_QUIT message. Some people like to do it slightly differently and use GetMessage instead of PeekMessage. GetMessage blocks if there are no messages, which is not particularly useful if you want to use the time not spent handling messages to update your game, or draw your scene. If you’re implementing a renderer that runs in a separate thread, you would probably want to use GetMessage. The calls to TranslateMessage and DispatchMessage do some processing on the messages (like converting key values), and dispatch the message to your WndProc respectively. We’ll eventually expand the else bit to draw a scene and stuff.

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

	switch ( msg )
	{
		case WM_DESTROY:
			PostQuitMessage(0);
			return 0;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}

Here we handle the messages, in this case if we get a WM_DESTROY (from someone going ALT+F4, or clicking the X), then we post a WM_QUIT (which will end the loop in Run) and return.

This code should run and you should get a window with a white background, and the title “Basic Win32 Window”.

It’s Refactoring Time

What we have is all well and good, some simple code to create a Win32 window. What we have, however, does not represent particularly good design; it would be far better if we abstracted this code into a class which we could instantiate from WinMain, and call Start, or Run or something. So what we’ll do is cover the process of abstracting our code into a class – we’ll call it Form. I’ve uploaded the following files which, once again, give you the full source: stage2.cpp, form.cppform.h.

Lets talk through Form.h: First we include a few things like the Win32 API, and the string and exception classes from the STL (Standard Template Library, the primary C++ framework).

#include
#include
#include

Next we declare our class, that is we write down a skeleton of the class, with all the member variables, and prototypes of all the member functions:

        class Form
	{
	public:

	protected:

	};

Inside “public:” we have:

                Form(HINSTANCE appInstanceHandle);
		virtual ~Form(void);

		int Run(int showCmd);

		void SetCaption( std::wstring windowCaption );

		void SetWidth( int windowWidth );
		void SetHeight( int windowHeight );

And inside “protected:” we have:

		virtual bool initialiseWindow(HINSTANCE appInstanceHandle);

		virtual LRESULT WndProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam);
		static LRESULT CALLBACK MainWndProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam);

		HWND formWindowHandle;
		HINSTANCE appInstanceHandle;

		int width;
		int height;
		std::wstring windowCaption;

I won’t put the full code for Form.cpp in here because it’s very similar to what I’ve talked about already. Rather I’ll talk about the changes I made. The first funny thing that you’ll notice is that we have to member functions which look like they deal with messages. One is MainWndProc, and it is static. The other is WndProc, and it is an instance member. You’ll see in Form.cpp that when we define our window, we pass a pointer to MainWndProc, instead of WndProc (like we did earlier). MainWndProc then handles the WM_CREATE message, gets a pointer to the class instance, and forever more delegates message handling to the instance member WndProc.

        wc.lpfnWndProc			= &Form::MainWndProc;

and the code for MainWndProc:

LRESULT CALLBACK Form::MainWndProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
{
	static Form* me = 0;

	switch ( message )
	{
		case WM_CREATE:
		{
			CREATESTRUCT* createStruct = (CREATESTRUCT*)lParam;
			me = (Form*)createStruct->lpCreateParams;
			return 0;
		}
	}

	if ( me )
	{
		return me->WndProc(windowHandle, message, wParam, lParam);
	}
	else
	{
		return DefWindowProc(windowHandle, message, wParam, lParam);
	}
}

I’ve also moved the CreateWindow call (as well as ShowWindow and UpdateWindow) to the start of the Run member function – meaning that the window doesn’t show until we actually want to start handling messages for it.

You’ll notice in Form.h that I’ve added some members for width, height and windowCaption so that we can customise these aspects of our window. There are setter functions for these:

void Form::SetCaption(std::wstring windowCaption)
{
	this->windowCaption = windowCaption;
}

void Form::SetWidth(int windowWidth)
{
	this->width = windowWidth;
}

void Form::SetHeight(int windowHeight)
{
	this->height = windowHeight;
}

And you can work out how I’ve changed the call to CreateWindow.

That really just leaves our constructor, and empty destructor:

Form::Form(HINSTANCE appInstanceHandle)
{
	windowCaption = L"Abstracted Win32 Window";
	width = 800;
	height = 600;

	this->appInstanceHandle = appInstanceHandle;

	if ( !this->initialiseWindow(appInstanceHandle) )
		throw std::exception("Could not initialise window.");
}

Form::~Form(void)
{
}

You’ll also find that stage2.cpp is far simpler because now all we have to do is create our Form, set its properties, and then run it.

Conclusion

We’ve learned how to create a Win32 window, and also how to wrap that up in a class. The implementation I’ve show here isn’t at all fancy, but it works and it illustrates the principal. I’ll be working on it in the future as I work my way through learning DirectX. That said, having written this article I’m not too sure whether I’ll keep up with writing detailed articles for the rest of my journey, probably just short notes on what I’ve learned – I really don’t have time for more.

Please leave any comments below :).

2 thoughts on “Learning C++ and DirectX: Step one, create a window.

  1. hi..i found that this tutorial very helpful. however, all the file code link did not work. since you do not gave all the coding, so it is hard for me to fully develop this program. this my first time with directX and hoping that you will make the link to the codes usable. thank you

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s