1. Introduction to Win32 Programming
Begin your path to Window mastery here.
Sherman Chin, Sunday 17 October 2004 - 05:00:00

Let us start our journey into the abyss of Windows programming. There are currently two major ways to code applications for Microsoft Windows. First, there is MFC (Microsoft Foundation Classes), which provides wrapper classes for the API (Application Programming Interface) of Windows. The second is Win32 that is the API of Windows itself. The Win32 API is a rich set of public functions that the programmers of Windows have created to allow other programmers to develop software that run on Windows.

The Win32 API allows us to access functions to display windows, play sound, and do other neat stuff common to Graphical User Interfaces (GUI) such as that of Windows. We do not need to worry about the actual hardware calls, which are made transparent to us by the Windows API. Think of Win32 programming as accessing the services provided by the Windows operating system so that our programs do not have to take care of the technical intricacies of the GUI. Win32 is named so because it is a 32-bit Windows API for the likes of Windows 98, Windows NT and Windows 2000. Before that, there was Win16 that was associated with 16-bit Windows 3.1 and below.

In this tutorial, you will be learning about Win32 programming as opposed to MFC. I personally believe that the Win32 API is rich enough to justify its usage rather than depending on the MFC wrapper classes that attempt to hide Win32's complexity. The only prerequisite is that you have read my earlier C++ articles or are already familiar with C++ programming.

To cut to the chase, let me teach you how to get a basic window to display on your screen. Some of you might be turned off by the slightly complicated method of setting up a window in Win32 but please bear with me as you only have to set the window up once and you can copy and paste the code for other Windows applications that you intend to create next.

First of all, let us look at the header file MyWin.h :


class MyWindow
{
private:
HINSTANCE hMemberThisInst;
int nMemberWinMode;
HWND hwnd;
WNDPROC lpfnWndProc;
WNDCLASSEX wcl;
HACCEL hAccel;
HMENU hMenu;
MSG msg;
char szWinName[30];
char szMenuName[30];
public:
BOOL init(HINSTANCE, int, char*, char*, WNDPROC);
BOOL registerMyWindow();
BOOL createMyWindow();
UINT createMyMessageLoop();
HWND getMyWindowHandle() { return hwnd;}
HINSTANCE getMyInstance() { return hMemberThisInst; }
};

Just copy and paste the code above into a file named MyWin.h. For that matter, you can name the file anything as long as it is followed by a .h extension to indicate that it is a C++ header file. What is the header file for? Well, it contains the class declaration without going into the implementation details. Think of it as a indication of what class, member functions and member variables that is made available to another source code file that links to it. The header file has all the function declarations without their bodies. The only exception is the getMyWindowHandle() and the getMyInstance() member functions, which have short bodies. It is actually your decision whether to separate the bodies or not but it is generally advisable to do so if your code is long so that the class declaration does not appear cluttered and is easily understandable.

There is nothing much to explain for the class declaration except that in my class, all the variables are in the private section and all the functions are in the public section. The class name is MyWindow but it is user-definable and you can name it something else if it suits you. If you observe carefully, you will notice some variable types that are fully capitalized, e.g. HINSTANCE, HWND, BOOL, MSG, WNDPROC and so on. These variable types are specific to Microsoft Windows and are made available through the provided windows header file, windows.h that is included by your .cpp file that you will see next. Also, take note that the function declarations' parameter lists only show the data-type and not the variable name e.g. BOOL init(HINSTANCE, int, char*, char*, WNDPROC); Compare this to the .cpp file.


Now, let us look at the MyWin.cpp file that contains the definitions for the class member functions - in other words, the bodies of the functions:

#include <windows.h>
#include "MyWin.h"

BOOL MyWindow::init(HINSTANCE hThisInst, int nWinMode, 
char* pszWinName, char* pszMenuName, 
WNDPROC lplfnWndProc)
{
hMemberThisInst = hThisInst;
nMemberWinMode = nWinMode;
lstrcpy(szWinName, pszWinName);
lstrcpy(szMenuName, pszMenuName);
lpfnWndProc = lplfnWndProc;
if (!registerMyWindow()) return 0;
if (!createMyWindow()) return 0;
if (!createMyMessageLoop()) return 0;
return 1;
}

BOOL MyWindow::registerMyWindow()
{
wcl.cbSize = sizeof(WNDCLASSEX);
wcl.hInstance = hMemberThisInst;
wcl.lpszClassName = szWinName;
wcl.lpfnWndProc = lpfnWndProc;
wcl.style = 0;

wcl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcl.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
wcl.hCursor = LoadCursor(NULL, IDC_ARROW);

wcl.lpszMenuName = NULL;
wcl.cbClsExtra = 0;
wcl.cbWndExtra = 0;

wcl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);

return RegisterClassEx(&wcl);
}

BOOL MyWindow::createMyWindow()
{
hMenu = LoadMenu(hMemberThisInst, szMenuName);

hwnd = CreateWindowEx(
NULL,
szWinName,
szWinName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_DESKTOP,
hMenu,
hMemberThisInst,
NULL
);
if (!hwnd) 
return 0;

hAccel = LoadAccelerators(hMemberThisInst, szMenuName);

ShowWindow(hwnd, nMemberWinMode);
UpdateWindow(hwnd);
return 1;
}

UINT MyWindow::createMyMessageLoop()
{
while (GetMessage(&msg, NULL, 0, 0))
{
if(!TranslateAccelerator(hwnd, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam; 
}

Note: Just copy and paste the code above into a text file and name it MyWin.cpp or any other user-defined name.

In our source code, we have included two header files: windows.h and MyWin.h. windows.h is the header file provided to you by your compiler to link to Windows applications like what we are doing now. MyWin.h is our own custom header file that you have seen earlier on. The difference in the coding is that windows.h is enclosed in <> while MyWin.h is enclosed in "". The former tells the compiler that the header file can be found in the standard search directories (which can be set in your compiler) and the latter tells the compiler that the header file is located in the current working directory, i.e. the directory where you keep all your source code for this application we are working on. Incidentally, if you did not named your header file MyWin.h, then by all means use the name that you have delegated to your header file.

You will notice something in common between all the functions in MyWin.cpp. Their function names are preceded by MyWindow::. This is to indicate that the functions are actually members of the MyWindow class and have been declared in the MyWin.h header file. The general syntax for the functions are as follows: returnType className::functionName(dataType dataName, dataType dataName,...){} Please note that the returnType is the data-type to be returned by the function (Check the return statement in the function body to determine the variable to be returned). The ellipses in the parentheses (...) indicate that we may have many parameters.

Using a bird's eye view of our program, we now have a MyWindow class which represents our physical window on screen. The class framework and its function prototypes are declared in MyWin.h while its function bodies are defined in MyWin.cpp. From the source code of MyWin.cpp, we gather that our MyWindow class has the following member functions: init(), registerMyWindow(), createMyWindow(), and createMyMessageLoop(). These are the 4 standard kinds of member functions needed to set up our window.

It is time that I told you how the Microsoft Windows operating system functions to serve us, programmers. Firstly, Windows "knows" or our application window's characteristics (e.g. icon, cursor and background color) when we register our window class by using the RegisterClassEx() function provided by windows. The RegisterClassEx() function takes the wcl structure (also provided by Windows) as an argument. Of course, we have to set the various variables of the wcl structure beforehand. Although Windows "knows" of our application window's characteristics, it will not display our window until we create the window using the CreateWindowEx() function and show, as well as, update the window using the ShowWindow() and UpdateWindow() functions respectively. Once Windows put up our application window, it will await input from users to our application and send it to an internal message queue. Thus, in our program, we should create a message loop that frequently checks the message queue for messages (i.e. input) meant for our application. We get the message from the message queue using the GetMessage() function. We must put the function in the loop so that our application will continuously check the message queue for messages meant for it. In our loop, we must use the TranslateMessage() and DispatchMessage() function to send the message back to the Windows operating system.

This might appear confusing at first. Why must we send the message back to Windows after all the trouble to retrieve the message from the message queue? The reason is so that Windows can resend the message back to our application at specific times so that our application does not hog the processor time. This is called preemptive multitasking - Windows take care of the scheduling of messages to our applications so that if the user is using more than one program at a time, one of those programs will not snatch away all the processing power and starve the rest of the programs.

So how is Windows going to resend our messages back to our program? To answer this question, we have to create a callback function of which Windows will invoke automatically and pass our message back as arguments to the callback function.


We create our callback function, as well as, define our main function in our Win.cpp file as follows:

#include <windows.h>
#include "MyWin.h"

LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, 
LPSTR lpszArgz, int nWinMode)
{
MyWindow wMainWin;
if (!wMainWin.init(hThisInst, nWinMode, 
"Main Window", NULL, WindowProc)) return 0;
return 1;
}

// This function is called by Windows and is passed messages from
// the message queue.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, 
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT paintstruct;

switch(message)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &paintstruct);
EndPaint(hwnd, &paintstruct);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}

Note: Just copy and paste the code above into a text file and name it Win.cpp or any other user-defined name.

Notice that we have included our MyWin.h header file as well as the provided windows.h file. The MyWin.h file is needed to link our main function to our MyWindow class that we declared in MyWin.h and defined in MyWin.cpp. After the include section, we declare our callback function that we see further down the source code. Next, we have our main function. You may notice that our main function is named WinMain() instead of main() as in normal C++ programs. This is a necessity in Windows. WinMain() is where the running of our program will start off in Windows. In WinMain(), we simply create an object of type MyWindow. Then, we call the member function init() to initialize our class member variables. We return 0 if something went wrong or return 1 if everything went right.

After our WinMain() function, we have our callback function. I named it WindowProc() but you can name it any other user-defined name. Windows will automatically invoke WindowProc() and resend our application messages that we dispatched earlier. In WindowProc(), we use a switch/case construct to check what messages (e.g. WM_CREATE, WM_PAINT, WM_DESTROY and so on) was send to our application. These messages are usually generated by the actions of the user i.e. input.


Since I never explained about the switch/case construct in my C++ tutorials, here is a brief description: The switch/case construct works very much like a series of if constructs. So, when we say:

switch(message)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &paintstruct);
EndPaint(hwnd, &paintstruct);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}

it is akin to:


if (message==WM_PAINT)
{
hdc = BeginPaint(hwnd, &paintstruct);
EndPaint(hwnd, &paintstruct);
}
else 
{
if (message==WM_DESTROY)
{
PostQuitMessage(0);
}
}
else 
{
return DefWindowProc(hwnd, message, wParam, lParam);
}

The big difference is that switch/case only accepts integers while if accepts more data types. In this respect, please take note that WM_PAINT and WM_DESTROY are macros that represent integers. What this means is that the compiler will replace WM_PAINT and WM_DESTROY with integers when the program is compiled. Windows defines the actual integers values of WM_PAINT and WM_DESTROY when we include the windows.h file. Also, take note that we have to put a break statement after every case section to make sure that we exit the case/switch construct if that section is executed.

Finally, I would like to explain how to compile and run the program in Microsoft Visual C++. When the Visual C++ loads up, just select new from the file menu. From the pop-up window, select the project tab and click on Win32 application. Specify your project name (anything sensible) and project directory location. Then, click on the OK button. In the next pop-up window, choose empty project and click on the finish button. An empty workspace should appear. Click on the new text file graphical button on your toolbar. An empty text file should appear on your desktop. Cut and paste the MyWin.h source code into the empty text file. After which, you should click on the save graphical button on your toolbar. A pop-up window will allow you to name your text file. Name it MyWin.h and click OK. Next, right click anywhere on your MyWin.h text file on your desktop. Choose "Insert file into Project" and click on your project name. Repeat the preceeding steps for MyWin.cpp and Win.cpp. Finally, click on "Execute ProjectName.exe" from the Build menu. Our window skeleton program will then be compiled and executed. You should be able to see an empty window appear on your screen (assuming, of course, that your are running Window 95, Windows NT 4 or higher).

This tutorial is longer than I expected so I guess I will only explain the line-by-line details of our source code in our next tutorial. Make sure you can compile and run the source files in today's tutorial and get a general feel of Windows programming. To sum it up, we have learned that we have to register our window, create our window, and create our message loop to get a basic Windows application of ours to run. We also need to create a callback function to be passed to our windows class. Last but not least, we should create our WinMain function where it all begins.
The output of your program should resemble the window titled "Main Window" displayed below. As an exercise, try changing the title of the Window. You can do so by altering the arguments to the init() function in WinMain(). Play around with the code and see what you can accomplish.


Image: ../../articles/tutorial/prog/images/WINTRO.GIF



this content item is from Sherman3D
( http://sherman3d.com/S3Dplugins/content/content.php?content.9 )