1. Getting Started ... 7
What this tutorial is all about ... 7
Important notes ... 7
Names of WindowsParts ... 8
The simplest Win32 program ... 10
About the simplest Win32 program ... 13
Calling Conventions... 13
Hugarian Notation ... 13
Systems vs. Apps Hungarian ... 14
Examples ... 15
Advantages of Hungarian notation ... 16
Disadvantages of Hungarian notation ... 17
Win32 Data Types ... 18
Modify the above program as follows ... 18
Question ... 19
2. Application Creation ... 20
The Main Window Class ... 21
The Size of the Window Class ... 22
Additional Memory Request ... 23
The Application's Instance ... 23
Window Extra-Memory ... 23
The Main Window's Style ... 24
Window Styles ... 25
Message Processing ... 26
The Application Main Icon ... 26
Introduction to Cursors ... 28
The Window's Background Color ... 31
The Application's Main Menu ... 32
3. Finalizing an Application ... 33
Step 1: Registering the Window Class... 33
Step 2: Creating the Window ... 34
WS_EX_CLIENTEDGE, ... 34
MB_ICONEXCLAMATION | MB_OK); ... 36
Step 3: The Message Loop ... 36
Windows Messages ... 37
Message Types ... 37
Message Routing ... 40
Message Handling ... 41
Message Loop ... 42
Step 4: the Window Procedure ... 42
The Complete Program ... 44
Step 5: Assignment 2 to see if you can visualized ―a little‖ on what is going on. ... 47
Question ... 47
4. Handling More Messages ... 48
Button click Example: window_click ... 49
QUESTIONS ... 55
PAINTSTRUCT ps;... 55
HDC hdc; ... 55
Assignment 2b ... 55
5. Understanding the Message Loop ... 56
What is a Message? ... 56
Structure of message ... 56
Message: WM_COMMAND ... 57
Message: WM_KEYDOWN ... 57
Message: WM_MOUSEMOVE ... 58
Messages: WM_LBUTTONDOWN, WM_RBUTTONDOWN ... 59
Messages: WM_LBUTTONUP, WM_RBUTTONUP ... 59
Posting message manually ... 59
Message that must be processed ... 59
InvalidateRect(hWnd,NULL,TRUE); ... 59
What is the Message Queue ... 60
What is a Message Loop ... 60
QUESTIONS ... 61
In the following code ... 61
6. Windows Messages ... 62
Classes Type: ... 62
Classes Message: ... 62
7. Message Boxes... 64
8... 65
Message Box Creation ... 65
Practical Learning: Introducing Additional Resources ... 68
9. Modal and Modeless Windows Forms ... 69
10. Resources Fundamentals ... 69
Introduction ... 70
Resource Creation ... 70
11. Component of Resource Script ... 71
Identifiers ... 71 DISCARDABLE... 71 Icons ... 71 Bitmaps ... 72 Mouse Cursors ... 74 String Tables ... 74 Accelerators ... 75 Menus ... 77 Version Information ... 78 Dialog Boxes ... 78 12. Resource-Definition Statements ... 80 Resources ... 80 Controls ... 81 Statements ... 82 13. Using Resources... 83 Creating resource ... 83
14. Menus and Icons using resource ... 86
Creating menu using resource script. ... 86
Defining id numbers ... 87
Processing the Messages generated by menu resources. ... 88
Creating menu using child process. ... 88
Example: menu_two ... 88
QUESTIONS ... 95
15. Dialogs Box Using Resource ... 96
16. Modeless Dialogs Using Resource ... 104
17. Windows Controls ... 107
Button Control Window Class ... 109
Styles ... 109
Constant Definitions ... 111
Combo Box Control Window Class ... 112
Styles ... 112
Constant Definitions ... 113
Edit Control Window Class ... 113
Styles ... 113
Constant Definitions ... 114
IP Address Control's Window Class ... 115
Styles ... 115
List Box Control Window Class ... 115
Styles ... 115
Constant Definitions ... 116
Scroll Bar Control Window Class... 117
Styles ... 117
Constant Definitions ... 118
Static Control Window Class ... 118
Styles ... 118
Constant Definitions ... 120
18. Styles Common to All Classes ... 121
Styles ... 121
Constant Definitions ... 122
Extended Window Styles ... 122
Extended Styles ... 122
Constant Definitions ... 124
19. App Part 1: Creating controls at runtime ... 125
Example: app_one ... 125
Static control ... 125
Button ... 127
Check box ... 128
The SetWindowText() function sets the title of the window. ... 130
Edit Control ... 130
Radio buttons and GroupBox... 132
ComboBox ... 135
Progress bar ... 137
20. App Part 2: Using files and the common dialogs ... 140
The Open File Dialogs ... 141
Reading and Writing Files ... 142
Reading ... 143
Writing ... 144
The full program ... 145
ListBox ... 150
21. Creating Custom Controls ... 153
The Burning control ... 153
22. The GDI ... 156 Rectangle... 158 Pen... 159 Brush ... 161 Hatch brushes ... 163 Shapes ... 165 Fonts ... 166
Setting up ... 169
Setting the Timer... 170
Animating in WM_TIMER... 171
Double Buffering ... 172
Faster Double Buffering ... 173
Killing the Timer... 173
Loading Fonts ... 173 Default Fonts ... 174 Drawing Text ... 175 Choosing Fonts ... 176 Choosing Colours... 177 Control Fonts ... 178 Books ... 178 Links ... 179 Getting It ... 179 Download Free VC++ 2008 ... 179
24. Solutions to Common Errors ... 179
Error LNK2001: unresolved external symbol _main ... 180
Fixing ... 180
Error C2440: cannot convert from 'void*' to 'HICON__ *' (or similar) ... 180
Fixing ... 180
Fatal error RC1015: cannot open include file 'afxres.h'. ... 181
Error LNK2001: unresolved external symbol InitCommonControls ... 181
Dialog does not display when certain controls are added ... 181
25. Why you should learn the API before MFC ... 182
The Controversy ... 182
My Answer... 182
1. Getting Started
What this tutorial is all about
This tutorial is intended to present to you the basics (and common extras) of writing
programs using the Win32 API. The language used is C, most C++ compilers will compile it as well. As a matter of fact, most of the information is applicable to any language that can access the API, inlcuding Java, Assembly and Visual Basic. I will not however present any code relating to these languages and you're on your own in that regard, but several people have previously used this document in said languages with quite a bit of success.
This tutorial will not teach you the C language, nor will it tell you how to run your perticular compiler (Borland C++, Visual C++, LCC-Win32, etc...) I will however take a few moments in the appendix to provide some notes on using the compilers I have knowledge of.
If you don't know what a macro or a typedef are, or how a switch() statement works, then turn back now and read a good book or tutorial on the C language first.
Important notes
Sometimes throughout the text I will indicate certain things are IMPORANT to read. Because they screw up so many people, if you don't read it, you'll likely get caught too. The first one is this:
The source provided in the example ZIP file is not optional! I don't include all the code in the text itself, only that which is relevant to whatever I'm currently discussing. In order to see how this code fits in with the rest of the program, you must take a look at the source provided in the ZIP file.
And here's the second one:
Read the whole thing! If you have a question during one section of the tutorial just have a little patience and it might just be answered later on. If you just can't stand the thought of not knowing, at least skim or search (yes computers can do that) the rest of the document before asking the nice folks on IRC or by email.
Another thing to remember is that a question you might have about subject A might end up being answered in a discussion of B or C, or maybe L. So just look around a little.
The simplest Win32 program
If you are a complete beginner lets make sure you are capable of compiling a basic windows application. Slap the following code into your compiler and if all goes well you should get one of the lamest programs ever written.
Remember to compile this as C, not C++. It probably doesn't matter, but since all the code here is C only, it makes sense to start off on the right track. In most cases, all this requires if you add your code to a .c file instead of a .cpp file. If all of this hurts your head, just call the file test.c and be done with it.
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL, "Goodbye, cruel world!", "Note", MB_OK); return 0;
}
If that doesn't work, your first step is to read whatever errors you get and if you don't
Make sure you have specified a Win32 GUI (NOT "Console") project/makefile/target, whatever applies to your compiler.
You will get this error
―1>c:\users\zuraimi\documents\visual studio 2008\projects\test\test\test.cpp(6) : error C2664: 'MessageBoxW' : cannot convert parameter 2 from 'const char [22]' to 'LPCWSTR'‖
Before you can solve the error you must know about the MessageBox function.
This error occur because MessageBox by definition support Unicode data while the string constant "Goodbye, cruel world!" and "Note" are ANSI data.
The following are four possible solution.
Select Project|Test Properties and in the test Property Pages|Character Set select Use Muti-Byte Character Set.
Solution #1
Solution #2
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
MessageBox(NULL, (LPCWSTR)"Goodbye, cruel world!", (LPCWSTR)"Note", MB_OK); return 0; } Or #include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL, L"Goodbye, cruel world!", L"Note", MB_OK); return 0;
}
This method will type cast the string constant to Unicode data Solution #3
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MessageBoxA(NULL, "Goodbye, cruel world!", "Note", MB_OK); return 0;
}
This method uses MessageBoxA which support ANSI data. Solution #4
#include <windows.h>
#define MessageBox MessageBoxA
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MessageBox (NULL,"Goodbye, cruel world!", "Note", MB_OK); return 0;
}
This method will change MessageBox
to
MessageBoxA as defined in the ―#define MessageBox MessageBoxA‖ statement.How to fix them vary from compiler to compiler (and person to person). While you fix error you be more literate on the language and the windows system. Use MSDN to get help and be more skilful in Windows API and C++ programming.
You may get some warnings about you not using the parameters supplied to WinMain(). This is OK. Now that we've established you can in fact compile a program, let’s go through that little bit of code....
About the simplest Win32 program
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
WinMain() is windows equivalent of main() from DOS or UNIX. This is where your program starts execution. The parameters are as follows:
HINSTANCE hInstance
Handle to the programs executable module (the .exe file in memory) HINSTANCE hPrevInstance
Always NULL for Win32 programs. LPSTR lpCmdLine
The command line arguments as a single string. NOT including the program name.
int nCmdShow
An integer value which may be passed to ShowWindow(). We'll get to this later.
hInstance is used for things like loading resources and any other task which is performed on a per-module basis. A module is either the EXE or a DLL loaded into your program. For most (if not all) of this tutorial, there will only be one module to worry about, the EXE. hPrevInstance used to be the handle to the previously run instance of your program (if any) in Win16. This no longer applies. In Win32 you ignore this parameter.
Calling Conventions
WINAPI specifies the calling convention and is defined as _stdcall. If you don't know what this means, don't worry about it as it will not really affect us for the scope of this tutorial. Just remember that it's needed here.
Hugarian Notation
Hungarian notation is a naming convention in computer programming, in which the name of a variable indicates its type or intended use. There are two types of Hungarian notation:
Systems Hungarian notation and Apps Hungarian notation.
Hungarian notation was designed to be language-independent, and found its first major use with the BCPL programming language. Because BCPL has no data types other than the machine word, nothing in the language itself helps a programmer remember variables' types. Hungarian notation aims to remedy this by providing the programmer with explicit
knowledge of each variable's data type.
In Hungarian notation, a variable name starts with a group of lower-case letters which are
mnemonics for the type or purpose of that variable, followed by whatever the name the programmer has chosen; this last part is sometimes distinguished as the given name. The first
character of the given name can be capitalised to separate it from the type indicators (see also
CamelCase). Otherwise the case of this character denotes scope.
Systems vs. Apps Hungarian
Where Systems notation and Apps notation differ is in the purpose of the prefixes.
In Systems Hungarian notation, the prefix encodes the actual data type of the variable. For example:
f: Flag (Boolean, logical). If qualifier is used, it should describe the true state of the flag. Exception: the constants fTrue and fFalse.
w: Word with arbitrary contents. lp: long integer pointer
wcx: WNDCLASSEX structure. ch: Character, usually in ASCII text.
b: Byte, not necessarily holding a coded character, more akin to w. Distinguished from the b constructor by the capital letter of the qualifier in immediately following.
sz: Pointer to first character of a zero terminated string. st: Pointer to a string. First byte is the count of characters cch. Hpp: heap structure
Example:
lAccountNum : variable is a long integer ("l");
arru8NumberList : variable is an array of unsigned 8-bit integers ("arru8");
szName : variable is a zero-terminated string ("sz"); this was one of Simonyi's original suggested prefixes.
hWnd: variable is a handle
lpText: long integer pointer to a string
lpCaption: long integer pointer to a string
uType: using integer
Apps Hungarian notation doesn't encode the actual data type, but rather, it gives a hint as to what the variable's purpose is, or what it represents.
rwPosition : variable represents a row ("rw");
usName : variable represents an unsafe string ("us"), which needs to be "sanitized" before it is used (e.g. see code injection and cross-site scripting for examples of attacks that can be caused by using raw user input)
strName : Variable represents a string ("str") containing the name, but does not specify how that string is implemented.
Most, but not all, of the prefixes Simonyi suggested are semantic in nature. The following are examples from the original paper: [1]
pX is a pointer to another type X; this contains very little semantic information. d is a prefix meaning difference between two values; for instance, dY might represent
a distance along the Y-axis of a graph, while a variable just called y might be an absolute position. This is entirely semantic in nature.
sz is a null- or zero-terminated string. In C, this contains some semantic information because it's not clear whether a variable of type char* is a pointer to a single
character, an array of characters or a zero-terminated string.
w marks a variable that is a word. This contains essentially no semantic information at all, and would probably be considered Systems Hungarian.
b marks a byte, which in contrast to w might have semantic information, because in C the only byte-sized data type is the char, so these are sometimes used to hold numeric values. This prefix might clear ambiguity between whether the variable is holding a value that should be treated as a letter (or more generally a character) or a number. While the notation always uses initial lower-case letters as mnemonics, it does not prescribe the mnemonics themselves. There are several widely used conventions (see examples below), but any set of letters can be used, as long as they are consistent within a given body of code. It is possible for code using Apps Hungarian notation to sometimes contain Systems
Hungarian when describing variables that are defined solely in terms of their type.
Examples
bBusy : boolean
dwLightYears : double word (systems) fBusy : boolean (flag)
nSize : integer (systems) or count (application) iSize : integer (systems) or index (application) fpPrice: floating-point
dbPi : double (systems) pFoo : pointer
rgStudents : array, or range
szLastName : zero-terminated string
u32Identifier : unsigned 32-bit integer (systems) stTime : clock time structure
fnFunction : function name
The mnemonics for pointers and arrays, which are not actual data types, are usually followed by the type of the data element itself:
pszOwner : pointer to zero-terminated string rgfpBalances : array of floating-point values aulColors : array of unsigned long (systems)
While Hungarian notation can be applied to any programming language and environment, it was widely adopted by Microsoft for use with the C language, in particular for Microsoft Windows, and its use remains largely confined to that area. In particular, use of Hungarian notation was widely evangelized by Charles Petzold's "Programming Windows", the original (and for many readers, the definitive) book on Windows API programming. Thus, many commonly-seen constructs of Hungarian notation are specific to Windows:
For programmers who learned Windows programming in C, probably the most memorable examples are the wParam (word-size parameter) and lParam (long-integer parameter) for the WindowProc() function.
hwndFoo : handle to a window
lpszBar : long pointer to a zero-terminated string
The naming convention guidelines for .NET Framework, Microsoft's more recent software development platform, advise that Hungarian notation should not be used.[2]
The notation is sometimes extended in C++ to include the scope of a variable, separated by an underscore. This extension is often also used without the Hungarian type-specification:
g_nWheels : member of a global namespace, integer m_nWheels : member of a structure/class, integer m_wheels : member of a structure/class
s_wheels : static member of a class _wheels : local variable
Advantages of Hungarian notation
(Some of these apply to Systems Hungarian only.) Supporters argue that the benefits of Hungarian Notation include:[1]
The variable type can be seen from its name
The formatting of variable names may simplify some aspects of code refactoring
Multiple variables with similar semantics can be used in a block of code: dwWidth, iWidth, fWidth, dWidth
Variable names can be easy to remember from knowing just their types. It leads to more consistent variable names
Deciding on a variable name can be a mechanical, and thus quick, process
Inappropriate type casting and operations using incompatible types can be detected easily while reading code
Useful with string based languages where numerics are strings (Tcl for example) In Apps Hungarian, the variable name guards against using it in an improper
operation with the same data type by making the error obvious as in: heightWindow = window.getWidth()
When programming in a language that uses dynamic typing or that is completely untyped, the decorations that refer to types cease to be redundant. Such languages typically do not include declarations of types (or make them optional), so the only sources of what types are allowed are the names themselves, documentation such as comments, and by reading the code to understand what it does. In these languages, including an indication of the type of a variable may aid the programmer. As mentioned above, Hungarian Notation expanded in such a language (BCPL). In complex programs with lots of global objects (VB/Delphi Forms), having a basic
prefix notation can ease the work of finding the component inside of the editor. Typing btn and pressing <Ctrl-Space> causes the editor to pop up a list of Button objects.
Applying Hungarian notation in a narrower way, such as applying only for member variables helps avoiding naming collision.
Disadvantages of Hungarian notation
Most arguments against Hungarian notation are against System Hungarian notation, not Apps Hungarian notation. Some[who?] argue that:
The Hungarian notation is redundant when type checking is done by the compiler. Compilers for languages providing type checking ensure the usage of a variable is consistent with its type automatically; checks by eye are redundant and subject to human error.
Some modern Integrated development environments display variable types on
demand, and automatically flag operations which use incompatible types, making the notation largely obsolete.
Hungarian Notation becomes confusing when it is used to represent several
properties, as in a_crszkvc30LastNameCol: a constantreferenceargument, holding the contents of a database column LastName of type varchar(30) which is part of the table's primary key.
It may lead to inconsistency when code is modified or ported. If a variable's type is changed, either the decoration on the name of the variable will be inconsistent with the new type, or the variable's name must be changed. A particularly well known example is the standard WPARAM type, and the accompanying wParam formal
parameter in many Windows system function declarations. The 'w' stands for 'word', where 'word' is the native word size of the platform's hardware architecture. It was originally a 16 bit type on 16-bit word architectures, but was changed to a 32-bit on 32-bit word architectures, or 64-bit type on 64-bit word architectures in later versions of the operating system while retaining its original name (its true underlying type is UINT_PTR, that is, an unsigned integer large enough to hold a pointer). The semantic impedance, and hence programmer confusion and inconsistency from platform-to-platform, is on the assumption that 'w' stands for 16-bit in those different
environments.
Most of the time, knowing the use of a variable implies knowing its type.
Furthermore, if you don't know what a variable is used for, knowing its type won't help you.
Win32 Data Types
You will find that many of the normal keywords or types have windows specific definitions, UINT for unsigned int, LPSTR for char* etc... Which you choose is really up to you. If you are more comfortable using char* instead of LPSTR, feel free to do so. Just make sure that you know what a type is before you substitute something else.
Just remember a few things and they will be easy to interpret. An LP prefix stands for Long
Pointer. In Win32 the Long part is obsolete so don't worry about it. And if you don't know
what a pointer is, you can either 1) Go find a book or tutorial on C, or 2) just go ahead anyway and screw up a lot. I'd really recommend #1, but most people go with #2 (I would :). But don't say I didn't warn you.
Next thing is a C following a LP indicates a const pointer. LPCSTR indicates a pointer to a const string, one that can not or will not be modified. LPSTR on the other hand is not const and may be changed.
You might also see a T mixed in there. Don't worry about this for now, unless you are intentionally working with Unicode, it means nothing.
Modify the above program as follows
#include <windows.h>
#define MessageBox MessageBoxA
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MessageBox (NULL,lpCmdLine, "SEL 4263", MB_YESNOCANCEL); return 0;
}
Build the program and resolve any errors. The Open a command prompt window at the directory where the executable file of the successfully compiled program stored
(\C:\Users\UserName\Documents\Visual Studio 2008\Projects\testcmdline for Windows Vista).
Run the program at the command prompt with a parameter Computer System & Multimedia as above. The output will be as follow.
Question
1. Is Hungarian Notation mandatory to be used in a computer program. 2. Given the following indentifier whis uses Hungarian Notation:
i. hFile, ii. lpBuffer, iii. nNumberOfBytesToRead, iv. lpNumberOfBytesRead, v. lpOverlapped vi. szFileName
a. Which indentifier are long integer pointers?
b. Which indentifier(s) is (are) meant to functions as counter(s)?
c. Which indentifier(s) is (are) meant to function()s to hold a handle of a file? d. Which identifier is a pointer to a zero terminated string:
e. Which identifier(s) is (are) a pointer to a filename string? f. Which identifier(s) is (are) a variable(s)?
g. Which identifier(s) is (are) an integer variable? h. Which identifier(s) is (are) has integer size value?
3.
Which part of the program retrieves the string Computer System & Multimedia. ?2. Application Creation
A Simple Window
Here is the code to a simple window which will be explained shortly.
// SimpleWindow.cpp
#include <windows.h>
char g_szClassName[] = "myWindowClass"; // Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default:
return DefWindowProc(hwnd, msg, wParam, lParam); }
return 0; }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc; HWND hwnd; MSG Msg;
//Step 1: Registering the Window Class wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0;
wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0; }
else
MessageBox(NULL, "Window Registration Succeed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0; }
}
For most part this is the simplest windows program you can write that actually creates a functional window, a mere 70 or so lines. If you got the first example to compile then this one should work with no problems.
The Main Window Class
There are two primary things you must do in order to create even the simplest window: you must create the central point of the program, and you must tell the operating system
how to respond when the user does what.
Just like a C++ program always has a main() function, a Win32 program needs a central function call WinMain. The syntax of that function is:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );
Unlike the C++ main() function, the arguments of the WinMain() function are not optional. Your program will need them to communicate with the operating system.
The first argument, hInstance, is a handle to the instance of the program you are writing.
The second argument, hPrevInstance, is used if your program had any previous instance. If not, this argument can be ignored, which will always be the case.
The third argument, lpCmdLine, is a string that represents all items used on the command line to compile the application.
The last argument, nCmdShow, controls how the window you are building will be displayed. An object that displays on your screen is called a window. Because there can be various types of windows in your programs, your first responsibility is to control them, know where they are, what they are doing, why, and when. The first control you must exercise on these different windows is to host them so that all windows of your program belong to an entity called the main window. This main window is created using an object that can be called a class (strictly, a structure).
The Win32 library provides two classes for creating the main window and you can use any one of them. They are WNDCLASS and WNDCLASSEX. The second adds only a slight feature to the first. Therefore, we will mostly use the WNDCLASSEX structure for our lessons.
The WNDCLASS and the WNDCLASSEX classes are defined as follows:
typedef struct _WNDCLASS { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS, *PW
typedef struct _WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX, *PWNDCLASSEX;
To create a window, you must "fill out" this class, which means you must provide a value for each of its members so the operating system would know what your program is expected to do.
The first thing you must do in order to create an application is to declare a variable of either WNDCLASS or WNDCLASSEX type. Here is an example of a WNDCLASSEX variable: INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEX WndClsEx;
return 0; }
The Size of the Window Class
After declaring a WNDCLASSEX variable, you must specify its size. This is done by initializing your variable with the sizeof operator applied to the window class as follows:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX);
}
Additional Memory Request
Upon declaring a WNDCLASSEX variable, the compiler allocates an amount of memory space for it, as it does for all other variables. If you think you will need more memory than allocated, assign the number of extra bytes to the cbClsExtra member variable. Otherwise, the compiler initializes this variable to 0. If you do not need extra memory for your WNDCLASSEX variable, initialize this member with 0. Otherwise, you can do it as follows:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ WNDCLASSEX WndClsEx; WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.cbClsExtra = 0; return 0; }
The Application's Instance
Creating an application is equivalent to creating an instance for it. To communicate to the WinMain() function that you want to create an instance for your application, which is, to make it available as a resource, assign the WinMain()'s hInstance argument to your WNDCLASS variable:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ WNDCLASSEX WndClsEx; WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.cbClsExtra = 0; WndClsEx.hInstance = hInstance; return 0; }
Window Extra-Memory
When an application has been launched and is displaying on the screen, which means an instance of the application has been created, the operating system allocates an amount of memory space for that
application to use. If you think that your application's instance will need more memory than that, you can request that extra memory bytes be allocated to it. Otherwise, you can let the operating system handle this instance memory issue and initialize the cbWndExtra member variable to 0:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ WNDCLASSEX WndClsEx; WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0; WndClsEx.hInstance = hInstance; return 0; }
The Main Window's Style
The style member variable specifies the primary operations applied on the window class. The actual available styles are constant values. For example, if a user moves a window or changes its size, you would need the window to be redrawn to get its previous characteristics. To redraw the window
horizontally, you would apply the CS_HREDRAW. In the same way, to redraw the window vertically, you can apply the CS_VREDRAW.
The styles are combined using the bitwise OR (|) operator. The CS_HREDRAW and the CS_VREDRAW styles can be combined and assigned to the style member variable as follows: INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.cbClsExtra = 0;
WndClsEx.cbWndExtra = 0;
WndClsEx.hInstance = hInstance;
}
Window Styles
WS_BORDER Creates a window that has a border.
WS_CAPTION Creates a window that has a title bar (implies the WS_BORDER style). Cannot be
used with the WS_DLGFRAME style.
WS_CHILD Creates a child window. Cannot be used with the WS_POPUP style. WS_CHILDWINDOW Same as the WS_CHILD style.
WS_CLIPCHILDREN Excludes the area occupied by child windows when you draw within the
parent window. Used when you create the parent window.
WS_CLIPSIBLINGS Clips child windows relative to each other; that is, when a particular child
window receives a paint message, the WS_CLIPSIBLINGS style clips all other overlapped child windows out of the region of the child window to be updated. (If WS_CLIPSIBLINGS is not given and child windows overlap, when you draw within the client area of a child window, it is possible to draw within the client area of a neighboring child window.) For use with the WS_CHILD style only.
WS_DISABLED Creates a window that is initially disabled.
WS_DLGFRAME Creates a window with a double border but no title.
WS_GROUP Specifies the first control of a group of controls in which the user can move from one
control to the next with the arrow keys. All controls defined with the WS_GROUP style FALSE after the first control belong to the same group. The next control with the WS_GROUP style starts the next group (that is, one group ends where the next begins).
WS_HSCROLL Creates a window that has a horizontal scroll bar.
WS_ICONIC Creates a window that is initially minimized. Same as the WS_MINIMIZE style. WS_MAXIMIZE Creates a window of maximum size.
WS_MAXIMIZEBOX Creates a window that has a Maximize button.
WS_MINIMIZE Creates a window that is initially minimized. For use with the WS_OVERLAPPED
style only.
WS_MINIMIZEBOX Creates a window that has a Minimize button.
WS_OVERLAPPED Creates an overlapped window. An overlapped window usually has a caption
and a border.
WS_OVERLAPPEDWINDOW Creates an overlapped window with the WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, and
WS_MAXIMIZEBOX styles.
WS_POPUP Creates a pop-up window. Cannot be used with the WS_CHILD style.
WS_POPUPWINDOW Creates a pop-up window with the WS_BORDER, WS_POPUP, and WS_SYSMENU styles. The WS_CAPTION style must be combined with the WS_POPUPWINDOW
style to make the Control menu visible.
WS_SIZEBOX Creates a window that has a sizing border. Same as the WS_THICKFRAME style. WS_SYSMENU Creates a window that has a Control-menu box in its title bar. Used only for
windows with title bars.
WS_TABSTOP Specifies one of any number of controls through which the user can move by using
the TAB key. The TAB key moves the user to the next control specified by the WS_TABSTOP style.
WS_THICKFRAME Creates a window with a thick frame that can be used to size the window. WS_TILED Creates an overlapped window. An overlapped window has a title bar and a border.
Same as the WS_OVERLAPPED style.
WS_TILEDWINDOW Creates an overlapped window with the WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles. Same
as the WS_OVERLAPPEDWINDOW style.
WS_VISIBLE Creates a window that is initially visible. WS_VSCROLL Creates a window that has a vertical scroll bar.
Message Processing
The name of the window procedure we reviewed in the previous lesson must be assigned to the
lpfnWndProc member variable of the WNDCLASS or WNDCLASSEX variable. This can be
defined as follows: #include <windows.h>
LRESULT WndProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.lpfnWndProc = WndProcedure; WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0; WndClsEx.hInstance = hInstance; return 0; }
LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { switch(Msg) { case WM_DESTROY: PostQuitMessage(WM_QUIT); break; default:
return DefWindowProc(hWnd, Msg, wParam, lParam); }
return 0; }
An icon can be used to represent an application in My Computer or Windows Explorer. To assign this small picture to your application, you can either use an existing icon or design your own. To make your programming a little faster, Microsoft Windows installs a few icons. The icon is assigned to the
hIcon member variable using the LoadIcon() function. For a Win32 application, the syntax of this
function is:
HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);
The hInstance argument is a handle to the file in which the icon was created. This file is usually stored in a library (DLL) of an executable program. If the icon was created as part of your
application, you can use the hInstance of your application. If your are using one of the icons below, set this argument to NULL.
The lpIconName is the name of the icon to be loaded. This name is added to the resource file when you create the icon resource. It is added automatically if you add the icon as part of your resources; otherwise you can add it manually when creating your resource script. Normally, if you had created and designed an icon and gave it an identifier, you can pass it using the MAKEINTRESOURCE macro.
To make your programming a little faster, Microsoft Windows installs a few icons you can use for your application. These icons have identification names that you can pass to the LoadIcon() function as the lpIconName argument. The icons are:
ID Picture IDI_APPLICATION IDI_INFORMATION IDI_ASTERISK IDI_QUESTION IDI_WARNING IDI_EXCLAMATION IDI_HAND IDI_ERROR
If you designed your own icon (you should make sure you design a 32x32 and a 16x16 versions, even for convenience), to use it, specify the hInstance argument of the LoadIcon() function to the instance of your application. Then use the MAKEINTRESOURCE macro to convert its identifier to a null-terminated string. This can be done as follows:
The icon can be specified by its name, which would be a null-terminated string passed as
lpszResourceName. If you had designed your icon and gave it an ID, you can pass this identifier to
the LoadIcon() method.
The LoadIcon() member function returns an HICON object that you can assign to the hIcon member variable of your WNDCLASS object. Besides the regular (32x32) icon, the WNDCLASSEX
structure allows you to specify a small icon (16x16) to use in some circumstances. You can specify both icons as follows:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return 0; }
Introduction to Cursors
If you designed your own icon (you should make sure you design a 32x32 and a 16x16 versions, even for convenience), to use it, specify the hInstance argument of the LoadIcon() function to the instance of your application. Then use the MAKEINTRESOURCE macro to convert its identifier to a null-terminated string. This can be done as follows:
WndCls.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_STAPLE));
The icon can be specified by its name, which would be a null-terminated string passed as
lpszResourceName. If you had designed your icon and gave it an ID, you can pass this identifier to
the LoadIcon() method.
The LoadIcon() member function returns an HICON object that you can assign to the hIcon member variable of your WNDCLASS object. Besides the regular (32x32) icon, the WNDCLASSEX
structure allows you to specify a small icon (16x16) to use in some circumstances. You can specify both icons as follows:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return 0; }
A cursor is used to locate the position of the mouse pointer on a document or the screen. To use a cursor, call the Win32 LoadCursor() function. Its syntax is:
HCURSOR LoadCursor(HINSTANCE hInstance, LPCTSTR lpCursorName);
The hInstance argument is a handle to the file in which the cursor was created. This file is usually stored in a library (DLL) of an executable program. If the cursor was created as part of your
application, you can use the hInstance of your application. If your are using one of the below cursors, set this argument to NULL.
When Microsoft Windows installs, it also installs various standard cursors you can use in your program. Each one of these cursors is recognized by an ID which is simply a constant integers. The available cursors are:
ID Picture Description
IDC_APPSTARTING Used to show that something undetermined is going on or the application is not stable
IDC_ARROW This standard arrow is the most commonly used cursor IDC_CROSS The crosshair cursor is used in various circumstances such as
drawing
IDC_HAND The Hand is standard only in Windows 2000. If you are using a previous operating system and need this cursor, you may have to create your own.
IDC_HELP The combined arrow and question mark cursor is used when providing help on a specific item on a window object
IDC_IBEAM The I-beam cursor is used on text-based object to show the position of the caret
IDC_ICON This cursor is not used anymore
IDC_NO This cursor can be used to indicate an unstable situation
IDC_SIZE This cursor is not used anymore
IDC_SIZEALL The four arrow cursor pointing north, south, east, and west is highly used to indicate that an object is selected or that it is ready to be moved
IDC_SIZENESW The northeast and southwest arrow cursor can be used when resizing an object on both the length and the height
IDC_SIZENS The north - south arrow pointing cursor can be used when shrinking or heightening an object
IDC_SIZENWSE The northwest - southeast arrow pointing cursor can be used when resizing an object on both the length and the height
IDC_SIZEWE The west - east arrow pointing cursor can be used when narrowing or enlarging an object
IDC_UPARROW The vertical arrow cursor can be used to indicate the presence of the mouse or the caret
IDC_WAIT The Hourglass cursor is usually used to indicate that a window or the application is not ready.
The LoadCursor() member function returns an HCURSOR value. You can assign it to the hCursor member variable of your WNDCLASS object. Here is an example:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.cbWndExtra = 0;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW); WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return 0; }
The Window's Background
Color
To paint the work area of the window, you must specify what color will be used to fill it. This color is created as an HBRUSH and assigned to the hbrBackground member variable of your WNDCLASS or WNDCLASSEX variable. The color you are using must be a valid HBRUSH or you can cast a known color to HBRUSH. The Win32 library defines a series of colors known as stock objects. To use one of these colors, call the GetStockObject() function. For example, to paint the windows background in black, you can pass the BLACK_BRUSH constant to the GetStockObject() function, cast it to HBRUSH and assign the result to hbrBackground.
In addition to the stock objects, the Microsoft Windows provides a series of colors for its own internal use. These are the colors used to paint the borders of frames, buttons, scroll bars, title bars, text, etc. The colors are named (you should be able to predict their appearance or role from their name) COLOR_ACTIVEBORDER, COLOR_ACTIVECAPTION,
COLOR_APPWORKSPACE, COLOR_BACKGROUND, COLOR_BTNFACE, COLOR_BTNSHADOW, COLOR_BTNTEXT, COLOR_CAPTIONTEXT, COLOR_GRAYTEXT, COLOR_HIGHLIGHT, COLOR_HIGHLIGHTTEXT, COLOR_INACTIVEBORDER, COLOR_INACTIVECAPTION, COLOR_MENU, COLOR_MENUTEXT, COLOR_SCROLLBAR, COLOR_WINDOW,
COLOR_WINDOWFRAME, and COLOR_WINDOWTEXT. You can use any of these colors to paint the background of your window. First cast it to HBRUSH and assign it to hbrBackground: INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0;
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW); WndClsEx.hbrBackground = GetStockObject(WHITE_BRUSH); WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return 0; }
The Application's Main Menu
If you want the window to display a menu, first create or design the resource menu (we will
eventually learn how to do this). After creating the menu, assign its name to the lpszMenuName name to your WNDCLASS or WNDCLASSEX variable. Otherwise, pass this argument as NULL. Here is an example:
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW); WndClsEx.hbrBackground = GetStockObject(WHITE_BRUSH); WndClsEx.lpszMenuName = NULL;
WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return 0; }
The Window's Class Name
To create a window, you must provide its name as everything else in the computer has a name. The class name of your main window must be provided to the lpszClassName member variable of your WNDCLASS or WNDCLASSEX variable. You can provide the name to the variable or declare a
global null-terminated string. Here is an example: LPCTSTR ClsName = L"BasicApp";
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW); WndClsEx.hbrBackground = GetStockObject(WHITE_BRUSH); WndClsEx.lpszMenuName = NULL;
WndClsEx.lpszClassName = ClsName; WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return 0; }
3. Finalizing an Application
Step 1: Registering the Window Class
In the above program we register a window class in the windows system using the Win32 API function RegisterClass(&wc)of which address of structure wc is given the parameter. wc is defined as the object of structure WNDCLASSEX. This structure data defines the properties of the window including its class name.
When you register a class once, and create as many windows as you want from it, without having to specify all those attributes over and over. Most of the attributes you set in the window class can be changed on a per-window basis if desired.
A Window Class has NOTHING to do with C++ classes.
The following statement in the program must be executed initialize the member of the object of wc. Before you can register the class in the window system.
wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc)) {
MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0; }
This is the code we use in WinMain() to register our window class. We must fill out the members of a WNDCLASSEX structure before calling RegisterClassEx () as explained above in The Main Window Class.
We then call RegisterClassEx() and check for failure, if it fails we pop up a message which says so and abort the program by returning from the WinMain() function.
Number one cause of people not knowing what the heck is wrong with their programs is probably that they didn't check the return values of their calls to see if they failed or not. RegisterClassEx() may fail so we us the ―if(!RegisterClassEx(&wc))‖ structure to check and informbus if it does.
Step 2: Creating the Window
Once the class is registered, we can create a window with it. You should look up the
paramters for CreateWindowEx() (as you should ALWAYS do when using a new API call), but I'll explain them briefly here.
HWND hwnd;
hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, g_szClassName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL);
The first parameter (WS_EX_CLIENTEDGE) is the extended windows style, in this case I have set it to give it a sunken inner border around the window. Set it to 0 if you'd like to see the difference. Also play with other values to see what they do.
Next we have the class name (g_szClassName in this example), which tells the system what kind of window to create. Since we want to create a window from the class we just registered, we use the name of that class. (In other application, we may use windows defines class like ―EDIT‖, ―BUTTONS‖, etc to create dialogboxes) After that we specify our window name or title which is the text that will be displayed in the Caption, or Title Bar on our window. The parameter we have as WS_OVERLAPPEDWINDOW is the Window Style parameter. There are quite a few of these and you should look them up and experiment to find out what they do. These will be covered more later.
The next four parameters (CW_USEDEFAULT, CW_USEDEFAULT, 320, 240) are the X and Y co-ordinates for the top left corner of your window, and the width and height of the window. I've set the X and Y values to CW_USEDEFAULT to let windows choose where on the screen to put the window. Remeber that the left of the screen is an X value of zero and it increases to the right; The top of the screen is a Y value of zero which increases towards the bottom. The units are pixels, which is the smallest unit a screen can display at a given resolution.
Next (NULL, NULL, g_hInst, NULL) we have the Parent Window handle, the menu handle, the application instance handle, and a pointer to window creation data. In windows, the windows on your screen are arranged in a heirarchy of parent and child windows. When you see a button on a window, the button is the Child and it is contained within the window that is it's Parent. In this example, the parent handle is NULL because we have no parent, this is our main or Top Level window. If you have successfully created a window and store the handle and the next window you are opening is a child so you must put the value of the created window in this parameter (Parent Window handle). The menu is NULL for now since we don't have one yet. If you are creating a window with menu defined in the resource file (which is of the same name but with the .rc e extension), fill the name of the menu here. If you are
opening a window, The instance handle is set to the value that is passed in as the first parameter to WinMain(). The creation data (which I almost never use) that can be used to send additional data to the window that is being created is also NULL.
If you're wondering what this magic NULL is, it's simply defined as 0 (zero). Actually, in C it's defined as ((void*)0), since it's intended for use with pointers. Therefore you will possibly get warnings if you use NULL for integer values, depending on your compiler and the warning level settings. You can choose to ignore the warnings, or just use 0 instead.
CreateWindow() will fail at some point even if you're an experianced coder, simply because there are lots of mistakes that are easy to make. Untill you learn how to quickly identify those mistakes, at least give yourself the chance of figuring out where things go wrong, and
if(hwnd == NULL) {
MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0; }
After we've created the window and checked to make sure we have a valid handle we show the window, using the last parameter in WinMain() and then update it to ensure that it has properly redrawn itself on the screen.
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
The nCmdShow parameter is optional, you could simply pass in SW_SHOWNORMAL all the time and be done with it. However using the parameter passed into WinMain() gives whoever is running your program to specify whether or not they want your window to start off visible, maximized, minimized, etc... You will find options for these in the properties of windows shortcuts, and this parameter is how the choice is carried out.
Step 3: The Message Loop
This is the heart of the whole program, pretty much everything that your program does passes through this point of control.
while(GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam;
GetMessage() gets a message from your application's message queue. Any time the user moves the mouse, types on the keyboard, clicks on your window's menu, or does any number of other things; messages are generated by the system and entered into your program's
message queue. By calling GetMessage() you are requesting the next available message to be removed from the queue and returned to you for processing. If there is no message,
GetMessage() Blocks. If you are unfamiliar with the term, it means that it waits untill there is a message, and then returns it to you.
TranslateMessage() does some additional processing on keyboard events like generating WM_CHAR messages to go along with WM_KEYDOWN messages.
Finally DispatchMessage() sends the message out to the window that the message was sent to. This could be our main window or it could be another one, or a control, and in some cases a window that was created behind the scenes by the system or another program. This isn't something you need to worry about because all we are concerned with is that we get the
message and send it out, the system takes care of the rest making sure it gets to the proper window.
Windows Messages
The system passes input to a window procedure in the form of messages. Messages are generated by both the system and applications. The system generates a message at each input event — for example, when the user types, moves the mouse, or clicks a control such as a scroll bar. The system also generates messages in response to changes in the system brought about by an application, such as when an application changes the pool of system font
resources or resizes one of its windows. An application can generate messages to direct its own windows to perform tasks or to communicate with windows in other applications. The system sends a message to a window procedure with a set of four parameters: a window handle, a message identifier, and two values called message parameters. The window handle identifies the window for which the message is intended. The system uses it to determine which window procedure should receive the message.
A message identifier is a named constant that identifies the purpose of a message. When a window procedure receives a message, it uses a message identifier to determine how to process the message. For example, the message identifier WM_PAINT tells the window procedure that the window's client area has changed and must be repainted.
Message parameters specify data or the location of data used by a window procedure when processing a message. The meaning and value of the message parameters depend on the message. A message parameter can contain an integer, packed bit flags, a pointer to a structure containing additional data, and so on. When a message does not use message parameters, they are typically set to NULL. A window procedure must check the message identifier to determine how to interpret the message parameters.
Message Types
This section describes the two types of messages: System-Defined Messages
Application-Defined Messages
System-Defined Messages
The system sends or posts a system-defined message when it communicates with an application. It uses these messages to control the operations of applications and to provide input and other information for applications to process. An application can also send or post system-defined messages. Applications generally use these messages to control the operation of control windows created by using preregistered window classes.
Each system-defined message has a unique message identifier and a corresponding symbolic constant (defined in the software development kit (SDK) header files) that states the purpose of the message. For example, the WM_PAINT constant requests that a window paint its contents.
Symbolic constants specify the category to which system-defined messages belong. The prefix of the constant identifies the type of window that can interpret and process the message. Following are the prefixes and their related message categories.
Prefix Message category ABM Application desktop toolbar BM Button control
CB Combo box control
CBEM Extended combo box control CDM Common dialog box
DBT Device DL Drag list box
DM Default push button control DTM Date and time picker control EM Edit control
HDM Header control HKM Hot key control IPM IP address control LB List box control LVM List view control MCM Month calendar control PBM Progress bar
PGM Pager control PSM Property sheet
RB Rebar control SB Status bar window SBM Scroll bar control STM Static control TB Toolbar TBM Trackbar TCM Tab control TTM Tooltip control TVM Tree-view control UDM Up-down control WM General window
General window messages cover a wide range of information and requests, including messages for mouse and keyboard input, menu and dialog box input, window creation and management, and Dynamic Data Exchange (DDE).
Application-Defined Messages
An application can create messages to be used by its own windows or to communicate with windows in other processes. If an application creates its own messages, the window
procedure that receives them must interpret the messages and provide appropriate processing. Message-identifier values are used as follows:
The system reserves message-identifier values in the range 0x0000 through 0x03FF (the value of WM_USER – 1) for system-defined messages. Applications cannot use these values for private messages.
Values in the range 0x0400 (the value of WM_USER) through 0x7FFF are available for message identifiers for private window classes.
If your application is marked version 4.0, you can use message-identifier values in the range 0x8000 (WM_APP) through 0xBFFF for private messages.
The system returns a message identifier in the range 0xC000 through 0xFFFF when an application calls the RegisterWindowMessage function to register a message. The message identifier returned by this function is guaranteed to be unique throughout the system. Use of this function prevents conflicts that can arise if other applications use the same message identifier for different purposes.
Message Routing
The system uses two methods to route messages to a window procedure: posting messages to a first-in, first-out queue called a message queue, a system-defined memory object that temporarily stores messages, and sending messages directly to a window procedure. Messages posted to a message queue are called queued messages. They are primarily the result of user input entered through the mouse or keyboard, such as WM_MOUSEMOVE,
WM_LBUTTONDOWN, WM_KEYDOWN, and WM_CHAR messages. Other queued messages include the timer, paint, and quit messages: WM_TIMER, WM_PAINT, and
WM_QUIT. Most other messages, which are sent directly to a window procedure, are called
nonqueued messages.
Queued Messages
Nonqueued Messages
Queued Messages
The system can display any number of windows at a time. To route mouse and keyboard input to the appropriate window, the system uses message queues.
The system maintains a single system message queue and one thread-specific message queue for each graphical user interface (GUI) thread. To avoid the overhead of creating a message queue for non–GUI threads, all threads are created initially without a message queue. The system creates a thread-specific message queue only when the thread makes its first call to one of the specific user functions; no GUI function calls result in the creation of a message queue.
Queued Messages
Whenever the user moves the mouse, clicks the mouse buttons, or types on the keyboard, the device driver for the mouse or keyboard converts the input into messages and places them in the system message queue. The system removes the messages, one at a time, from the system message queue, examines them to determine the destination window, and then posts them to the message queue of the thread that created the destination window. A thread's message queue receives all mouse and keyboard messages for the windows created by the thread. The thread removes messages from its queue and directs the system to send them to the
appropriate window procedure for processing.
With the exception of the WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, the system always posts messages at the end of a message queue. This ensures that a window receives its input messages in the proper first in, first out (FIFO) sequence. The WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, however, are kept in the queue and are forwarded to the window procedure only when the queue contains no other messages. In addition, multiple WM_PAINT messages for the same window are combined into a single WM_PAINT message, consolidating all invalid parts of the client area into a single area. Combining WM_PAINT messages reduces the number of times a window must redraw the contents of its client area.