The Smallest WindowProc Function

When making a Windows application using C/C++, a WindowProc function is required to handle the messages sent to the application.  This is usually in the form of a very large switch statement with one case for each message.  I have found an easier way to make the proc function.  Here it is:

LRESULT (*Procs[65536])(WPARAM, LPARAM);
LRESULT WINAPI WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_CLOSE:
		DestroyWindow(hWnd);
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	default:
		if (Procs[message]) return Procs[message](wParam, lParam);
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
}

Of course, you do need to do a few extra things in the WinMain() function, like initializing the array of function pointers to zero.  If you don’t, WindowProc() may call a function that doesn’t exist, crashing the application.  Here’s an example of how to initialize the list of functions.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPCSTR lpCmdLine, int nCmdShow)
{
	ZeroMemory(&Procs, sizeof(LRESULT(*)(WPARAM,LPARAM)) * 65536);
	Procs[WM_KEYDOWN] = OnKeyDown;
	// Rest of WinMain() here.
}

Once the entire array has be zeroed-out, you then start filling-in each element of the array with the function that handles that specific message.  In a header file, you would have the prototype for that function declared, with the function defined in a .c or .cpp file.  For example:

// Prototype in header.
LRESULT OnKeyDown(WPARAM wParam, LPARAM lParam);

// Actual function not in header
LRESULT OnKeyDown(WPARAM wParam, LPARAM lParam)
{
	// Key handling code here.
	return 0;
}

For handling of raw input, the WindowProc() function becomes a little bit more complex because you are required to call DefWindowProc() to clean-up the input data.  So if you use raw input in your application, your WindowProc() function will probably look like this:

LRESULT WINAPI WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_CLOSE:
		DestroyWindow(hWnd);
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_INPUT:
		LRESULT result = Procs[message](wParam, lParam);
		DefWindowProc(hWnd, message, wParam, lParam);
		return result;
	default:
		if (Procs[message]) return Procs[message](wParam, lParam);
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
}

The rule for the WindowProc() function is if you handle a message, you supply the return code for the function (typically 0 unless there’s an error).  So in the above code, we store what our message-handling function returned in a variable called “result”, call DefWindowProc() to do the clean-up, then return with the value of “result”.  Note that the function above assumes that you’re handling raw input.  If you don’t provide an input handling function, then the application will crash!  To remedy this, simply write your function like this:

LRESULT WINAPI WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_CLOSE:
		DestroyWindow(hWnd);
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	default:
		if (Procs[message])
		{
			if (message == WM_INPUT)
			{
				LRESULT result = Procs[message](wParam, lParam);
				DefWindowProc(hWnd, message, wParam, lParam);
				return result;
			}
			return Procs[message](wParam, lParam);
		}
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
}

And there you have it!  A minimalist WindowProc() function that you’ll almost never have to modify in the development of your Windows application.  This doesn’t cover all the cases where you’ll need to call DefWindowProc() after handling a message, but this is a good start and it’s not too difficult to handle the other cases.