Windows Mobile Pocket PC and Smartphone
Game Programming Tutorials


Part 2 - Diving into Hello World

Sections:: Understanding the code

 

:: Understanding the code

In this part of the tutorial series we will be taking a closer look at the functions in our helloworld
project. We will look through the source code and try to develop a deeper understanding of how
these functions work. Understanding the basic structure of C/C++ Windows Mobile software, how
things happen and where they happen, will let us know where to integrate our code to introduce the
functionality our games will require. We will need to know where to tie in code that initializes
variables, handles graphics or reads user input.

WinMain

The code of the WinMain function follows:

	int WINAPI WinMain(HINSTANCE hInstance,
         HINSTANCE hPrevInstance,
         LPWSTR lpCmdLine,
         int CmdShow)
{ MSG msg; HWND hHelloWnd = NULL; //Check if Hello.exe is running. If it's running then focus on the window hHelloWnd = FindWindow(szAppName, szTitle); if (hHelloWnd) { SetForegroundWindow (hHelloWnd); return 0; }

if ( !hPrevInstance ) { if ( !InitApplication ( hInstance ) ) { return (FALSE); }
}
if ( !InitInstance( hInstance, CmdShow ) ) { return (FALSE); } while ( GetMessage( &msg, NULL, 0,0 ) == TRUE ) { TranslateMessage (&msg); DispatchMessage(&msg); } return (msg.wParam); }

This is pretty standard for Windows applications and is fairly straightforward. WinMain makes a
call to the FindWindow function to check if an instance of this application is already running. The
Windows Mobile OS prefers to minimize applications and keep the running in the background
rather than continuously starting and quitting them every time they're used. So if the application is
already running then there is no need to create a new instance of it and a call is made to
SetForegroundWindow to make the currently running application appear. At this point there is
nothing left for this instance to do and thus it terminates with 'return 0'

If there wasn't another instance running then we have to create this instance of the application. So
calls are made to the functions InitApplication and InitInstance to get everything initialized and
create the window. Then the message loop is started.

WndProc

Here is the code for the WndProc function:

	LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
	{
		LRESULT lResult = TRUE;
		HDC hdc;
     	PAINTSTRUCT ps;
    	RECT rect;
     
     	switch(msg)
     	{
       	case WM_CREATE:
       		// create the menu bar
       		SHMENUBARINFO mbi;
       		ZeroMemory(&mbi, sizeof(SHMENUBARINFO));
       		mbi.cbSize = sizeof(SHMENUBARINFO);
       		mbi.hwndParent = hwnd;
       		mbi.nToolBarId = IDR_HELLO_MENUBAR;
       		mbi.hInstRes = g_hInst;
       
       		if (!SHCreateMenuBar(&mbi)) 
{ PostQuitMessage(0); } break; case WM_COMMAND: switch (wp) { case IDOK: SendMessage(hwnd,WM_CLOSE,0,0); break; default: return DefWindowProc(hwnd, msg, wp, lp); } break; case WM_PAINT: { hdc = BeginPaint (hwnd, &ps); GetClientRect (hwnd, &rect); DrawText (hdc, szMessage, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint (hwnd, &ps); } break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: lResult = DefWindowProc(hwnd, msg, wp, lp); break; } return (lResult); }

The WndProc callback function is where a lot of the action takes place. When the OS needs to
inform you of an action that has taken place or that you need to cause to take place, this is how
you will receive the message.

This function is made up of one big switch statement: switch(msg). The variable 'msg' is the
message that the Windows Mobile OS is sending to your program. The switch statement allows
your program to take appropriate action based on what the message is. The case sections of the
switch statement hold the different actions. If your program doesn't know what the message is (or
if you don't care to handle it) then you can ignore it and send it to the default handler.

Our helloworld project contains six cases. We handle five message types and send any others to
the default handler. The five messages we act upon are: WM_CREATE, WM_COMMAND,
WM_PAINT, WM_CLOSE, WM_DESTROY. Any other messages we receive will fall under the
default section starting at the line 'default:'

Lets look at these messages. We will introduce new messages in future tutorials as we come
across them and require their use.

WM_CREATE

This message is sent when the window is first created by CreateWindow. We can insert any
initialization code in here (actually we'll throw the code in a function and just make a call to that
function from here. That will let us minimize the clutter.) The code that is currently in the creation
section just creates a menu.

WM_COMMAND

The Windows Mobile OS will send us a WM_COMMAND whenever the user makes an action
that we need to respond to. These actions coincide with choices in the menu. The code in our
program handles these choices using another switch statement 'switch(wp)'. There is only one
command that is handled in our program: 'IDOK'. This WM_COMMAND message is sent when
the user presses the OK button on the menu. All other commands are sent to the default handler
for WM_COMMAND. The IDOK case has one main line of code:
'SendMessage(hwnd,WM_CLOSE,0,0);' which sends a WM_CLOSE message to the program.

WM_PAINT

Our program receives the WM_PAINT command from the OS whenever it needs to redraw its
window. This can be when it's first drawn or because its been obscured and needs to be
updated. We can put our drawing code in here. The code we have here in our example is pretty
much the same as would be used in desktop Windows. There is a call to BeginPaint which
prepares the window to be painted. Then we see a call to GetClientRect which gets the
co-ordinates of the area that we can draw on. Then we see the call to DrawText, this is what
draws the text "Hello World" that we see when we run the program. Lastly there is a call to
EndPaint. EndPaint is paired with BeginPaint and tells the OS that we're done painting to the
window. In this setup, if we wanted to do any additional drawing we would do it inbetween the
call to BeginPaint and EndPaint.

WM_CLOSE

This is message is sent when the program should quit such as when the user presses the X on
the titlebar (if there is one) or in our case it is sent by our own program to itself when the user
presses the OK button in the menu. This is where we can add code to ask the user "Are you sure
you want to quit?" Our example doesn't ask anything, it just calls the function DestroyWindow
which sends yet another message to our program telling it to destory the window. This is one of
the areas where things in Windows Land get confusing. There are WM_CLOSE, WM_DESTROY
and WM_QUIT. All of these have a hand in quitting. It's your choice on how you want to do it. We'll
probably just make one common function with all code that needs to be executed before quitting
and have all of the messages make a call to it.

WM_DESTROY

This message is sent to the window when its going to be destroyed. Our example makes a call
to PostQuitMessage. PostQuitMessage tells the Windows Mobile OS that to terminate our
programs process. We want to make sure we finished cleaning up before we make this call.

Our helloworld program ignores all other messages and so they are handled by:

		default: 
          	lResult = DefWindowProc(hwnd, msg, wp, lp); 
           break;

If we wanted to handle more messages we would simply add corresponding cases in the switch
statement. Some messages we'll encounter in the near future are: WM_KEYDOWN,
WM_SETFOCUS, WM_KILLFOCUS, WM_LBUTTONDOWN and others.

Lets move on to the next function.

InitInstance

Here is the code for InitInstance:

	BOOL InitInstance (HINSTANCE hInstance, int CmdShow )
   {
g_hInst = hInstance; hwndMain = CreateWindow(szAppName, szTitle, WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );

if ( !hwndMain ) { return FALSE; }
ShowWindow(hwndMain, CmdShow ); UpdateWindow(hwndMain); return TRUE; }

Here we have the code that creates the window. We see that the CreateWindow function is
used right off the bat to get the window created. Then there is an if statement that checks to
see if the window creation was successful. If it wasn't then the function returns with FALSE
which causes the program to quit without continuing. If it was successful then it continues
execution and calls ShowWindow and UpdateWindow. ShowWindow causes the window to
be shown or hidden, depending on the flags sent to it. In this case the CmdShow flag tells the
window to show up. UpdateWindow sends a WM_PAINT message to the program sp that it
can update its window.

InitApplication


Next we have the code for InitApplication:

	BOOL InitApplication ( HINSTANCE hInstance )
   {
 		WNDCLASS wc;
 		BOOL f;

wc.style = CS_HREDRAW | CS_VREDRAW ; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hIcon = NULL; wc.hInstance = hInstance; wc.hCursor = NULL; wc.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH ); wc.lpszMenuName = NULL; wc.lpszClassName = szAppName; f = (RegisterClass(&wc));

return f; }

This function fills out the WNDCLASS structure 'wc' in preparation to create the window. It sets
things such as an icon, style flags, background, and most importantly it links to our callback
function WndProc. After filling out the structure it is registered with the function RegisterClass.
Now we are ready to call CreateWindow.

We should now be ready to start adding code and learning some features and functionality that
we'll need to know when developing our Windows Mobile games.

 

 

Previous: Part 1 -- Next: Part 3

Return to Tutorials Index

 

Back to Kicking Software

Copyright © 2006-2007 Kicking Software. All rights reserved.