This chapter introduces you to the basics of Windows programming and its event driven philosophy. It also explains why Windows programs look different from other C programs and introduces you to some major terms used in Windows programming.
Contents of this chapter
It was only about a decade ago, when most computers had none or only very limited graphics capabilities. Interaction between the machine and the human was mainly done by typing in strange commands with a keyboard (and many self called UNIX Gurus still have not come over that yet) and receiving some sort of text message on the screen. Programs at that time had control over the interaction process as they gave the user a number of options what to do like pressing a key for example and they reacted by changing some of the say 2000 characters (25 lines by 80 columns) on the screen. Even if a program used a graphic mode this still applied, the only difference was, that instead of characters there were now a much larger number of pixels. So as a result there was a direct correlation of the way the computer saw the screen and the way the user saw it.
With GUI like Windows things have changed a bit. First you got this fancy device called mouse which enables you to go somewhere and click and even more important, you have now windows which you can drag around and arrange in any way you want. It would be a hard job for a program if it had to keep track of where it actually is and hence which of the thousands or millions of pixels it has to change to display a single character by also taking into account that some of the pixels may be hidden by another window. Someone's got to do the job though and this is where the GUI comes in. Now for a program that means that the correlation between the way the program sees the screen and the way the user sees it is lost. For the program there is only one window which is always visible and in which the coordinates of pixels never change.
This alone would not justify the need for a new programming philosophy because as you can see, many old non GUI programs still run happily in a "shell" window. However the reason why this works is that there is a windows program which suggests the old-style-application that the screen runs in text or full screen graphic mode and hence it does its output in the usual way. Since it would mess up the screen completely if the output would go directly to the screen the Windows program directs it to a buffer which it then displays on the screen. However this approach has the following disadvantages:
Another major problem is that although you might be able to change the size of such a window, it is not able to react to it i.e. if the program assumes a screen of 80x25 characters it won't give you more or less rows or columns just by changing the windows size.
All the old DOS or UNIX program did was processing every command sequentially. So apart from some programs which did their own interrupt handling (don't bother if you don't know what that is) it is always determinable with which command the program will succeed once the current command had been processed.
Under Windows things must obviously change a bit if we want to overcome the problems mentioned above. It can no longer be just us, telling the operating system or the GUI respectively what to do, here the OS must also be able to request services from us. Let me give you two examples of what that means:
So as you can see the main difference between a DOS or UNIX program and a Windows program is that the latter cannot just sequentially process the code but must have a mechanism of reacting to events which may occur at any time. This type of programming is called event-driven programming. As a result it depends mainly on the user's actions which part of your program will get executed and the user is controlling the program rather than the program controlling the user. An event-driven program might e.g. have to redraw the window's contents five consecutive times without any changes because of some action undertaken by the user whereas in a sequential program there has never been a need to do that.
If you have ever read a book about learning a programming language you have probably come across the fist program which printed "Hello World" on the screen. They always do that and so do most books about Windows programming although there is no such thing as a new programming language.
So in a conventional C program you have probably tried the following:
#include <stdio.h> int main() { printf("Hello World"); return 1; }
Now with Windows you expect things to be a bit more complicated and indeed in most books they give you about 50 lines of code or more to do that, although it can be as easy as
#include <windows.h> int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MessageBox(NULL,"Hello World", "My first program", MB_OK); return TRUE; }
Although this is a fully working Windows program there is a good reason why you won't find it in the books: This is not what you really want. The MessageBox command displays only a simple message to the user and all the work for creating and displaying the window is done by Windows. The is no functionality you can add to a message box apart from the options provided in the arguments of the command and the window is owned by Windows rather than yourself. But what you really want is to have your own window which you can fill with more complex contents such as menu's, buttons, text and graphics and which the user can resize, minimise or maximise.
Before we do that however I want to analyse the above program to and make you familiar with some important conventions that are required for understanding Windows and our further exercises.
If you have done some conventional C or C++ programming and someone would ask you to name some of the commands in C you might say something like: switch(), printf(), getc(), fopen(), etc. Among those four command however there are two which cannot be used under Windows, one that you can but should not and only one that is a truly valid and acceptable command in a Windows C program.
In fact the latter three are commands which themselves call one or more functions in the OS and are thus specific to a particular platform. The implementation of these commands is provided in so called libraries which are linked together with your program, whereas the core C commands such as switch() can be understood directly by the compiler and thus do not require header or library files.
Under Windows you still use the same syntax, the same brackets and the same variable types as in a conventional C program and of course you need all the core C commands plus the so called preprocessor commands which begin with a hash (like e.g. #include or #define). Here is a list of the most common core C commands:
switch case break if else while goto return sizeof typedef struct
However with other commands you have to be careful. Library commands which are all defined in header files like stdio.h, stdlib.h, io.h, etc. cannot always be used under Windows. Printf (defined in stdio.h) is for example a command that you cannot use whereas localtime (defined in time.h) you can. For some functions that can be used, you'll find a similar function in the Windows API as for example for sprintf(). Sprintf does the same as printf except writing the output to a string rather than the screen (or any other stream). Sprintf (defined in the string.h file) can still be used under Windows and you'll find it in many of even Microsoft's own sample code. However I recommend not to use it and use wsprintf which is the equivalent Windows function instead.
The trouble with library commands is:
However there is one important difference between sprintf and wsprintf: wsprintf cannot deal with floating point numbers whereas sprintf can. So if you need to format floating point numbers you have to use sprintf.
If you got a bit confused now, here's a rule you can stick to: Before using a library function you should always check whether there is an equivalent or similar function in the Windows API and if so use it. Otherwise look up the description of the command in the manual or help file and check whether this command can be used under Windows.
In every program things have got to start somewhere and in a conventional C program this is the function "main" which is executed by the OS when the program is called. Under Windows this function is called "WinMain" and it has got some additional parameters with it. If you look at the line of code with the implementation of the function WinMain you might realise a statement, that you have not seen before: PASCAL. You might have heard of the programming language PASCAL but since we're doing C does this make sense? It does and it actually tells the compiler that this function uses the PASCAL calling convention. Now what does that mean?
When a function is called by a program regardless whether it is an OS function or one defined in your own program, the parameters witch you specified in the function call have to be passed to the function. This is done by pushing these parameters on the stack and in C the order in which the parameters are pushed is from right to left (last parameter first) whereas in PASCAL it is vice versa. Not a big deal but as a result there are a couple of differences:
In C you can have functions with variable parameter lists as e.g. with printf where you can specify as many parameters as you wish (theoretically). With the PASCAL calling convention this is not possible but therefore functions using the PASCAL calling convention require 3 Bytes less of code when called (in 16-Bit programs). Now 3 Bytes may not seem very much, but by the time Microsoft designed Windows, Memory was a scarce resource and 3 Bytes saved for every of the thousands of function calls in Windows was a really big issue. 3 Bytes less mean also an improvement in speed since this also saves a few CPU cycles every time a function is called.
All of Window's API functions use the PASCAL calling convention except wsprintf because of the variable parameters required. And Windows expects, that all the functions it calls in your program also use the PASCAL calling convention. This is the reason why we have to specify the PASCAL keyword in the WinMain definition, otherwise the parameters would be messed up and you will most likely get a General Protection Fault (GPF) as soon a you use them. As we will see later there are other functions witch are called directly by windows such as window and callback procedures. These must in the same manner be declared with the PASCAL calling convention.
You might now ask yourself whether it is possible of using the PASCAL calling convention in general for all you functions and indeed most compilers offer an option to do that. However, I do not recommend to do so as you will get in trouble if you use other than the Windows API functions and which do not explicitly declare the calling convention in the associated header file. The compiler would then assume that these functions (which are provided in a LIB or OBJ file) also use the PASCAL calling convention and again you program would crash on execution. By default all C Compilers use the C calling convention an you should stick to that and only use PASCAL declaration where necessary.
If you have a brief look at the above example again you might spot another difference between the two programs: the writing of the words. Where in the conventional C program you get only a rough idea if any at all what a command like e.g. printf does, the MessageBox command in the Windows program is much more understandable.
In the early days when C was invented programmers tended to be weird freaks, to lazy to write whole words and even too lazy to press the shift key. Programming commands were there sort of secret language and this might be one reason why many people have been afraid of programming.
In the Windows API all commands are concatenated full words in a natural language order and each of these words begins with a capital letter. To create a window e.g. the old C freaks would have probably introduced a command like crwnd or wcreate or createwnd whereas in Windows you can be sure to find a command called CreateWindow. This helps you the programmer a lot to find a command that you have never used before. In many cases you just think what you would call the command you are looking for according to these rules and you will find such a command.
In Windows not just functions are named in a special way but also variables and structures. Every variable or function parameter starts with a lowercase type prefix abbreviation followed by the meaning in natural language with a capital letter. This way of naming variables is called the Hungarian notation in honour of an apparently legendary Microsoft programmer called Charles Simonyi (if you see him, give him my regards). The following table shows examples of how the Hungarian notation is used on variables:
Type Data Example Description Prefix Type b BOOL bEnabled Boolean variable which can be TRUE (0) or FALSE (=0) c char cKey Single character h HANDLE hWnd Handle to a Windows Object dw DWORD dwRop Unsigned 32-Bit value l LONG lParam Singed 32-Bit value w WORD wParam unsigned 16-Bit value u UINT unsigned integer value (can be 16 or 32 bit) n int nHeight singed integer value (can be 16 or 32 bit) rc RECT rcWnd Rectangle structure containing 4 coordinate values sz char[] szFileName Array of characters containing a String terminated by a character value of zero (zero terminated string) lp void lpCallbackProc Far pointer to something (e.g a hidden data structure) far* In 32-Bit Windows there are only far pointers so the type is (void *). lpsz LPSTR lpszClassName Far pointer to a zero terminated Sting lprc LPRECT lprcWnd Far Pointer to a Rectangle structure lppt LPPOINT lpptPos Far Pointer to a Point structure
When you define new data types or structure using the typedef statement all letters should be capital letters. This is e.g. how the types BOOL and POINT are defined in the windows.h header file:
typedef int BOOL; typedef struct { int x,y } POINT;
Naming your variables and functions according to the Hungarian notation is not a must but I strongly recommend to adapt this style in your own programs. This will not only help others to understand your programs but also yourself it you look at them again after months or years. In fact I have not seen a single Windows program yet that does not use this notation.
Now we've got all this fancy windows, icons, fonts etc. stuff but how do we handle it? Well, with handles of course. In the real world e.g. most glass windows have a handle and you use it to open and close it. In a windows program it basically the same thing. When you create the window you get a handle which you can then use to show, hide, move, size or destroy the window. And thanks to the Hungarian naming convention the functions are called respectively ShowWindow, HideWindow, MoveWindow (used for both moving and sizing) and DestroyWindow. All these functions require the handle of the window you want to perform the action on as the first parameter.
However window handles are just one particular type of handles and there are a lot more types in the Windows API. You get handles for all types of windows objects such as icons, bitmaps, dialogues, menus, fonts, color palettes and so on.
Now what exactly is an handle? Again if you have done some conventional programming you might have already come across one type of handles: file handles. A file handle is a positive integer value returned by either open or create and it identifies a file. Every time you read of write to/ from the file you need to specify the file handle and when you finished you close the file and the file handle becomes invalid.
In MS-DOS and UNIX the value of a file handles is an index number to a table owned by the OS where information about the file is stored. Since you cannot access the table the value of the file handle is more or less meaningless to you and it can only be used to call other file functions. Under Windows handles are usually pointers to a data structure describing the associated object and in the same manner it is meaningless to the application. However since the value of a handle is unique for a particular object type (i.e. there won't be two different objects of the same type with identical handles) you can compare two handles and if they have the same value they refer to the same object.
A common mistake in windows programming and the cause for many general protection faults (GPFs) is to pass an invalid handle to a function. A handle is only valid as long as an object exists. A window handle e.g. becomes invalid either if you explicitly call DestroyWindow in your program or if the user closes the window. If a handle becomes invalid you should set its value to NULL to be able to detect whether is valid or invalid later on.
In the real word nothing is unlimited and this also true for the computer world. For all those who own a car, parking space is (apart form money) probably one the things that is most limited and hard to find these days. In general we call things like that Resources.
What exactly is a resource? Well first it is something that is given by nature (or something/ someone else that beyond our control) i.e. it can't easily be produced, second it is limited and third we can decide when and how much we want to use of it. Real word resources are as we all know oil and coal for example.
When we look at the computer world hardware resources are the most obvious. There is a given amount of physical memory, the CPU operates at a given speed and there is a certain amount of hard disk space available. Obvious isn't it, but have you ever looked at the screen a resource? Well its properties are given by the manufacturer of your monitor and you can't easily change them, it is limited, as there is a maximum number of horizontal and vertical pixels and it is used up by all these programs that you simultaneously run in your GUI environment.
On the software bit there are also resources but they are not quite as easily to spot. First there are the so called System Resources. These are normally linked to hardware resources in some way like for example the available memory (but here its physical + virtual memory). Under Windows there are some other limits which are especially a problem under Windows 3.x and were divided into KERNEL, GDI and USER Resources, which all refer to subsystems of Windows. There is e.g. a limit on the number of file handles that can be obtained and that means that only a certain number of files can be opened simultaneously. Since IO operations are handled by the Kernel subsystem, this is a Kernel resource. GDI resources are limits set by the graphical subsystem of Windows and it actually means a limit of the memory available to store information about graphical objects as pens, brushes, bitmaps etc. Note that the limit only refers to the information stored about an object not the object itself i.e. for bitmaps that is amongst other things the size in pixels and a pointer to the bitmap data but not the bitmap data itself. The limit is also not set for each object type individually but for the sum of all GDI objects. So if the limit was 10 objects there could be either combination of the number of pens, brushes and bitmaps up to the total limit of 10. GDI resources were normally the most likely to run out of under Windows 3.1 and once you ran out, you were lucky if you were able to save your data and reboot windows. Finally USER resources are the number of Windows classes and individual windows that can be defined and opened throughout the system. As windows come and go there is very rarely a deficiency so this is generally not a problem to worry about.
As I mentioned before this is a particular problem of Windows 3.x. Under Windows NT there is no preallocated memory for resources and thus the only limit it the total memory available which will lead to other problems first once you are running out. Windows 95 is again somewhere in between. Technically its still the old inflexible system of Windows 3.x, but they have split resource types up and use more resource heaps, which basically means that it takes longer before a shortage occurs.
Since now resources have been a pain so let's now get on to a more friendly type of resources, the so called Program resources. Program resources are any kind of no-code information you create yourself in order to add it to your program. Examples are tables of any kind, bitmap images, icons, cursors, dialogue boxes etc. We'll go into more detail later on, when we create program resources.
From your point of view these might not look like resources but from the programs point of view they are since they are
In conventional DOS and UNIX program resources are normally stored in external files which you have to take care of yourself. In Windows program resources are linked together with your EXE file and there a plenty of functions to access and use these resources.
Obviously the most important thing you need to compile a Windows program is at least one C or CPP (C++) file containing your code. In addition every "real" Windows program needs another file type: a resource file with the file extension .RC. This is where you put all your program resources in such as e.g. menus and dialogue boxes and the first thing that should go in there is a program icon. Without an icon, Windows will use a default icon which gives little information about your application when installed in the Program Manager or the Windows 95 Desktop.
Most Windows projects contain another file, the definition file with the extension .DEF. This is required for 16 Bit Windows applications and optional for Win32. It contains general information about the program and sets some initial runtime parameters. DEF files are normally created at the beginning of the project and are hardly changed later on. If you set up a new project just copy and existing .DEF file and modify it according to your requirements.
Now what happens with all these files when you compile them? The following graphic shows what happens with your source files when compiling:
In the first stage all your code files are compiles into OBJ files. Then the resource compiler translates your RC file into a RES file. This is the binary representation of your resource description and contains also files included in the RC file such as .ICO and .BMP files.
In stage 2 all object files are linked to an EXE file. This is a temporary executable file which cannot be executed. In the next stage the resource compiler links the RES file with the temporary EXE file and marks it as a Windows program.