Вы находитесь на странице: 1из 183

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
MB_ICONEXCLAMATION | MB_OK); .............................................................................. 34
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
Dialogs ................................................................................................................................. 60
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
Base Window Styles .......................................................................................................... 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
23. Timers and Animation ................................................................................................ 169
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
So basically... ..................................................................................................................... 183
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.

Ok I think that's all the ranting I have to do for the moment, lets try some actual code.
Names of WindowsParts
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
understand them, look them up in the help or whatever documents accompany your compiler.
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
 u: unsigned integer
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
 cApples : count of items
 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 constant reference argument, 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. ?
4. Which part of the program display the [Yes][No][Cancel] button?
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 { typedef struct _WNDCLASSEX {
UINT style; UINT cbSize;
WNDPROC lpfnWndProc; UINT style;
int cbClsExtra; WNDPROC lpfnWndProc;
int cbWndExtra; int cbClsExtra;
HINSTANCE hInstance; int cbWndExtra;
HICON hIcon; HINSTANCE hInstance;
HCURSOR hCursor; HICON hIcon;
HBRUSH hbrBackground; HCURSOR hCursor;
LPCTSTR lpszMenuName; HBRUSH hbrBackground;
LPCTSTR lpszClassName; LPCTSTR lpszMenuName;
} WNDCLASS, *PW 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);

return 0;
}

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;

return 0;
}

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;
}

The Application Main Icon


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:

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;
}

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.cbClsExtra = 0;
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.hIcon = LoadIcon(NULL, IDI_APPLICATION);
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.
WNDCLASSEX wc;
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,
"The title of my window",
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
Always check return values!
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.
The system posts a message to a thread's message queue by filling an MSG structure and then
copying it to the message queue. Information in MSG includes: the handle of the window for
which the message is intended, the message identifier, the two message parameters, the time
the message was posted, and the mouse cursor position. A thread can post a message to its
own message queue or to the queue of another thread by using the PostMessage or
PostThreadMessage function.

An application can remove a message from its queue by using the GetMessage function. To
examine a message without removing it from its queue, an application can use the
PeekMessage function. This function fills MSG with information about the message.

After removing a message from its queue, an application can use the DispatchMessage
function to direct the system to send the message to a window procedure for processing.
DispatchMessage takes a pointer to MSG that was filled by a previous call to the
GetMessage or PeekMessage function. DispatchMessage passes the window handle, the
message identifier, and the two message parameters to the window procedure, but it does not
pass the time the message was posted or mouse cursor position. An application can retrieve
this information by calling the GetMessageTime and GetMessagePos functions while
processing a message.

A thread can use the WaitMessage function to yield control to other threads when it has no
messages in its message queue. The function suspends the thread and does not return until a
new message is placed in the thread's message queue.

You can call the SetMessageExtraInfo function to associate a value with the current thread's
message queue. Then call the GetMessageExtraInfo function to get the value associated with
the last message retrieved by the GetMessage or PeekMessage function.

Nonqueued Messages

Nonqueued messages are sent immediately to the destination window procedure, bypassing
the system message queue and thread message queue. The system typically sends nonqueued
messages to notify a window of events that affect it. For example, when the user activates a
new application window, the system sends the window a series of messages, including
WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR. These messages notify the
window that it has been activated, that keyboard input is being directed to the window, and
that the mouse cursor has been moved within the borders of the window. Nonqueued
messages can also result when an application calls certain system functions. For example, the
system sends the WM_WINDOWPOSCHANGED message after an application uses the
SetWindowPos function to move a window.

Some functions that send nonqueued messages are BroadcastSystemMessage,


BroadcastSystemMessageEx, SendMessage, SendMessageTimeout, and SendNotifyMessage.

Message Handling
An application must remove and process messages posted to the message queues of its
threads. A single-threaded application usually uses a message loop in its WinMain function to
remove and send messages to the appropriate window procedures for processing.
Applications with multiple threads can include a message loop in each thread that creates a
window. The following sections describe how a message loop works and explain the role of a
window procedure:

 Message Loop
 Window Procedure

Message Loop

A simple message loop consists of one function call to each of these three functions:
GetMessage, TranslateMessage, and DispatchMessage. Note that if there is an error,
GetMessage returns -1 -- thus the need for the special testing.

Step 4: the Window Procedure


If the message loop is the heart of the program, the window procedure is. This is where all the
messages that are sent to our window get processed.
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;
}

The case selection determine what the window procedure (WinProc()) shoud process upon
receiving a message. The case selection may be added to process more windows message.

The window procedure is called for each message, the HWND parameter is the handle of your
window, the one that the message applies to. This is important since you might have two or
more windows of the same class and they will use the same window procedure (WndProc()).
The difference is that the parameter hwnd will be different depending on which window it is.
For example when we get the WM_CLOSE message we destroy the window. Since we use the
window handle that we received as the first paramter, any other windows will not be affected,
only the one that the message was intended for.
WM_CLOSE is sent when the user presses the Close Button or types Alt-F4. This will cause
the window to be destroyed by default, but I like to handle it explicitly, since this is the
perfect spot to do cleanup checks, or ask the user to save files etc. before exiting the program.

When we call DestroyWindow() the system sends the WM_DESTROY message to the window
getting destroyed, in this case it's our window, and then destroys any remaining child
windows before finally removing our window from the system. Since this is the only window
in our program, we are all done and we want the program to exit, so we call
PostQuitMessage(). This posts the WM_QUIT message to the message loop. We never receive
this message, because it causes GetMessage() to return FALSE, and as you'll see in our
message loop code, when that happens we stop processing messages and return the final
result code, the wParam of WM_QUIT which happens to be the value we passed into
PostQuitMessage(). The return value is only really useful if your program is designed to be
called by another program and you want to return a specific value.
The Complete Program
#include <windows.h>

char g_szClassName[] = "myWindowClass";//Name of window class

// Step 4: the Window Procedure - This is the brain


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);//sends the WM_DESTROY message to the
window
//(hwnd) getting destroyed,
break;
case WM_DESTROY:
PostQuitMessage(0);//This posts the WM_QUIT message to the
message loop
//This window will never receive this message,
because
// it causes GetMessage() to return FALSE
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);//allows
//window to, like be sized, maximised, etc...
}
return 0;
}

//
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) //Entry of program
{
WNDCLASSEX wc; //Create WNDCLASSEX object

//Step 1: Registering the Window Class


wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;// Window Procedure: mandatory
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;// owner of the class: mandatory
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);// optional
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);// optional
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;// Name of window class:mandatory
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wc)) //Registering successful


{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
else
{
MessageBox(NULL, "Window Registration Succeed!", "OK",
MB_ICONEXCLAMATION | MB_OK);
}

//Step 2: Creating the Window


HWND hwnd; //Create variable hwnd to store handle of Instance
(process)
hwnd = CreateWindowEx(
WS_EX_APPWINDOW,
g_szClassName,// name of a registered window class
"The title of my window",// window caption
WS_OVERLAPPEDWINDOW,// window style
CW_USEDEFAULT, CW_USEDEFAULT,//w and y position,
240, 120,// width and height
NULL, // handle to parent window
NULL, // handle to menu
hInstance, // application instance
NULL);// window creation data
if(hwnd==0) //If window creation failed
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
else
{
MessageBox(NULL, "Window Creation Succeed!", "OK!",
MB_ICONEXCLAMATION | MB_OK);
}
ShowWindow(hwnd, nCmdShow);//Show window identified by hwnd using
//parameter passed by the last parameter in WinMain()(nCmdShow)
UpdateWindow(hwnd);//then update it to ensure that it has properly
//redrawn itself on the screen

//Step 3: The Message Loop - This is the heart


MSG Msg; //Create MSG structure to store data for windows message
// Keep pumping messages--they end up in our Window Procedure
while(GetMessage(&Msg, NULL, 0, 0) > 0)//while (message from
// application's message queue)>0)
{
TranslateMessage(&Msg);//do some additional processing on
//keyboard events like generating WM_CHAR messages to
//go along with WM_KEYDOWN messages
DispatchMessage(&Msg);//sends the message out to the
//windows procedure
}
return Msg.wParam;
}
Step 5: Assignment 2 to see if you can visualized “a little”
on what is going on.
Modify the program such that:

1. The window is created:


a. With the class name ―Assignment 1‖
b. size of 640 by 480 pixel
c. No message is displayed if there is no class registration or windows creation
error
d. Displays your name as the caption and also as the windows class
e. Has a horizontal and vertical scroll bar.

2. And when you exit use MessageBox() to display the window’s class name as the
caption of the windows and display your name as the message.

Question

1. Give the difference between WNDCLASS and WNDCLASSEX structure.


2. Can CreateWindowsEX use WNDCLASS structure registered by
RegisterClassEx Function.
3. Say that you have successfully registerd a window class. Can the classname be reuse
again in the program when you create a child window. .
4. The hInstance member of the WNDCLASSEX structure takes the instance of the
WinMain parameter. What does this mean? Ans: The WNDCLASSEX is contain in
that instance that contain the Window Procdure,
5. Will GetMessage() pick up the message generated when you left-click on the close
button while running SimpleWindow above? Ans: Yes
6. Will TranslateMessage() pick up the message generated when you left-click on the
close button while running SimpleWindow above? Ans: No
7. Will if any of these event be processed by the TranslateMessage function:
a. Any mouse button click
b. Any key press.

Ans: Yes

8. Is WM_KEYDOWN and and WM_KEYUP message generated as the windows


system-defined message.Ans: Yes
9. Is the WM_CLOSE message a system-defined message. Ans: Yes
10. Is the WM_COMMAND message a system-defined message. Ans: Yes
11. Is the message created by sendmessage() a system-defined message. Ans: No
12. Is the message created by sendmessage() a application-defined message. Ans: Yes
13. An application can create messages to be used by its own windows or to
communicate with windows in other processes. What type of message is this? Ans:
Application-Defined Messages
14. How do Window Procedure retrieve windows system-defined message. Ans: The
wParam of the MSG structure
15. How do Window Procedure retrieve windows system-defined message. Ans: The
wParam of the MSG structure
16. On what message do PostQuitMessage function response to? Ans: WM_DESTROY
message.

4. Handling More Messages


Whenever anything happens to your window, Windows will call this Windows Procedure
telling you what has happened. The message parameter of the Windows Procedure contains
the message sent. You don't want to have to handle all the messages but just the ones you are
interested in so any messages you decide not to handle should be passed back to Windows to
handle in a default way. You do this by calling: DefWindowProc(...)

Reference: Open Windows API guide. Under Contents Tab, open ref folder and select
Windows API Reference: Messages. The jump to button and look under mouse title. You will
find the following message listed.

 Mouse
o WM_LBUTTONDBLCLK
o WM_LBUTTONDOWN
o WM_LBUTTONUP
o WM_MBUTTONDBLCLK
o WM_MBUTTONDOWN
o WM_MBUTTONUP
o WM_MOUSEMOVE
o WM_RBUTTONDBLCLK
o WM_RBUTTONDOWN
o WM_RBUTTONUP

Button click Example: window_click


Alright, we've got a window, but it doesn't do anything except what DefWindowProc()
allows it to, like be sized, maximised,
etc... Not really all that exciting.

In the next section I am going to show


you how to modify what you already
have to do something new. This way I
can just tell you "Handle this message,
and do this in it..." and you will know
what I mean and be able to do so
without seeing an entire example. That's
the hope anyway, so pay attention :P

Okay for starters take the example code for the last window we worked on and make sure it
compiles and runs as expected. Then you can either keep working on it for the next little bit
or copy it to a new project to modify.

We're going to add the capability to show the user what the name of our program is when
they click on our window. Not very exciting, it's basically to get the hang of handling
messages. Lets look at what we have in our WndProc():

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;
}

If we want to handle mouse clicks, we need to add a WM_LBUTTONDOWN handler (or


WM_RBUTTONDOWN, WM_MBUTTONDOWN, for right and middle clicks respectively).
If I or someone else refers to handling a message they mean to add it into the WndProc() of
your window class as follows:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_LBUTTONDOWN: // <-
// <- we just added this stuff
break; // <-
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
The order in which you handle your messages rarely matters. Just make sure you've got your
break; after each one. As you can see we added another case into our switch(). Now we
want something to happen when we get to this part of our program.

First I will present the code we want to add (that will show the user the filename of our
program) and then I will integrate it into our program. Later on I will probably just show you
the code and let you integrate it into your program. This is of course better for me as I don't
have to type as much and it's better for you because you will be able to add the code into
ANY program and not just the ones I present. If you aren't sure how to do it, look at the
example zip file included with the section.

GetModuleFileName(hInstance, szFileName, MAX_PATH);


MessageBox(hwnd, szFileName, "This program is:", MB_OK |
MB_ICONINFORMATION);
Now this code does not stand on it's own, it can't just be slapped into our code any old place.
We specifically want it to run when the user clicks the mouse button so this is how I would
merge this small bit of code into our skeleton program:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_LBUTTONDOWN:
// BEGIN NEW CODE
{
char szFileName[MAX_PATH];
HINSTANCE hInstance = GetModuleHandle(NULL);

GetModuleFileName(hInstance, szFileName, MAX_PATH);


MessageBox(hwnd, szFileName, "This program is:",
MB_OK | MB_ICONINFORMATION);
break;
}
// END NEW CODE
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

Note the new set of curly braces {} . These are required when declaring variables inside a
switch() statement. This should be basic C knowledge but I thought I should point it out
anyway for those of you doing things the hard way.

So if you've added in that code, compile it now. If it works, click on the window and you
should see a box with the name of the .exe pop up.

You'll notice we've added two variables, hInstance and szFileName. Look up
GetModuleFileName() and you will see that the first parameter is a HINSTANCE refering to
the executable module (our program, the .exe file). Where do we get such a thing?
GetModuleHandle() is the answer. The references for GetModuleHandle() indicate that
passing in NULL will return us "a handle to the file used to create the calling process",
which is exactly what we need, the HINSTANCE just mentioned. Putting all this information
together we end up with the following declaration:

HINSTANCE hInstance = GetModuleHandle(NULL);

Now on to the second parameter, again turning to our trusty reference manual, we see that it
is " a pointer to a buffer that receives the path and file name of the specified module" and the
data type is LPTSTR (or LPSTR if your references are old). Since LPSTR is equivalent to char*
we can declare an array of char's like this:

char szFileName[MAX_PATH];

MAX_PATH is a handy macro included via <windows.h> that is defined to the maximum length
of a buffer needed to store a filename under Win32. We also pass MAX_PATH to
GetModuleFileName() so it knows the size of the buffer.

After GetModuleFileName() is called, the buffer szFileName will be filled with a null
terminated string containing the name of our .exe file. We pass this value to MessageBox()
as an easy way of displaying it to the user.

So if you've added in that code, compile it now. If it works, click on the window and you
should see a box with the name of the .exe pop up.

If it doesn't work, here's the full code to the program. Compare it to what you have and see
what, if any, mistakes you made.
#include <windows.h>

char g_szClassName[] = "myWindowClass";//Name of window class

// Step 4: the Window Procedure - This is the brain


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);//sends the WM_DESTROY message to the
window
//(hwnd) getting destroyed,
break;
case WM_DESTROY:
PostQuitMessage(0);//This posts the WM_QUIT message to the
// message loop This window will never receive this
// message,because it causes
//GetMessage() to return FALSE
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);//
// default window procedure to provide default processing
// for any window messages that is not processed.
}
return 0;
}

//
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) //Entry of program
{
WNDCLASSEX wc; //Create WNDCLASSEX object

//Step 1: Registering the Window Class


wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;// Window Procedure: mandatory
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;// owner of the class: mandatory
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);// optional
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);// optional
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;// Name of window class:mandatory
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wc)) //Registering successful


{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
else
{
MessageBox(NULL, "Window Registration Succeed!", "OK",
MB_ICONEXCLAMATION | MB_OK);
}

//Step 2: Creating the Window


HWND hwnd; //Create variable hwnd to store handle of Instance
//(process)
hwnd = CreateWindowEx(
WS_EX_APPWINDOW,
g_szClassName,// name of a registered window class
"The title of my window",// window caption
WS_OVERLAPPEDWINDOW,// window style
CW_USEDEFAULT, CW_USEDEFAULT,//w and y position,
240, 120,// width and height
NULL, // handle to parent window
NULL, // handle to menu
hInstance, // application instance
NULL);// window creation data
if(hwnd==0) //If window creation failed
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
else
{
MessageBox(NULL, "Window Creation Succeed!", "OK!",
MB_ICONEXCLAMATION | MB_OK);
}
ShowWindow(hwnd, nCmdShow);//Show window identified by hwnd using
//parameter passed by the last parameter in WinMain()(nCmdShow)
UpdateWindow(hwnd);//Send WM_PAINT message toe Windows Procedure which
//update it to ensure that it has
// properly redrawn itself on the screen

//Step 3: The Message Loop - This is the heart


MSG Msg; //Create MSG structure to store data for windows message
// Keep pumping messages--they end up in our Window Procedure
while(GetMessage(&Msg, NULL, 0, 0) > 0)//while (message from
// application's message queue)>0)
{
TranslateMessage(&Msg);//do some additional processing on
//keyboard events like generating WM_CHAR messages to
//go along with WM_KEYDOWN messages
DispatchMessage(&Msg);//sends the message out to the
//windows procedure
}
return Msg.wParam;
}
QUESTIONS
1. Draw the block diagram which show the functionl execution of a windows application
program.
2. Which part of the program draw the window on the screen?
3. Name the identifier of the Windows Procedure in the program?
4. Which statement in the program identify the name of Windows Procedure in the
program?
5. Which part of the program send a WM_PAINT message directly to the window
procedure of the specified window, bypassing the application queue?
6. Add the following code in red appropriately into the windows procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 0, 0, "Hello, Windows!", 15);
EndPaint(hwnd, &ps);
return 0L;

// Process other messages.


}
}

a) The condition of case WM_PAINT: is executed once. Which code in the


program send the WM_PAINT message and cause the case WM_PAINT: to be
executed?
b) Is it mandatory to process the WM_PAINT message?
c) Can you reuse the class myWindowClass which has been registered in the
program?
d) Name the statement in the program which handles unattended message.
3. The class myWindowClass has been used once in the program. Name the statement
that use it.

Assignment 2b
Modify the program such that:
If you left right-click the mouse in the window, message box a messagebox like this will be
generated the following message.

5. Understanding the Message


Loop
Understanding the message loop and entire message sending structure of windows programs
is essential in order to write anything but the most trivial programs. Now that we've tried out
message handling a little, we should look a little deeper into the whole process, as things can
get very confusing later on if you don't understand why things happen the way they do.

What is a Message?
A message is an integer value. If you look up in your header files (which is good and
common practice when investigating the workings of API's) you can find things like:
#define WM_INITDIALOG 0x0110
#define WM_COMMAND 0x0111

#define WM_LBUTTONDOWN 0x0201


...and so on. Messages are used to communicate pretty much everything in windows at least
on basic levels. If you want a window or control (which is just a specialized window) to do
something you send it a message. If another window wants you to do something it send you a
message. If an event happens such as the user typing on the keyboard, moving the mouse,
clicking a button, then messages are sent by the system to the windows affected. If you are
one of those windows, you handle the message and act accordingly.

Structure of message
Each windows message may have up to two parameters, wParam and lParam. Originally
wParam was 16 bit and lParam was 32 bit, but in Win32 they are both 32 bit. Not every
message uses these parameters, and each message uses them differently. For example the
WM_CLOSE message doesn't use either, and you should ignore them both. The WM_COMMAND
message uses both, wParam contains two values, HIWORD(wParam) is the notification message
(if applicable) and LOWORD(wParam) is the control or menu id that sent the message. lParam
is the HWND (window handle) to the control which sent the message or NULL if the messages
isn't from a control.

HIWORD() and LOWORD() are macros defined by windows that single out the two high bytes
(High Word) of a 32 bit value (0xFFFF0000) and the low word (0x0000FFFF) respectively. In
Win32 a WORD is a 16bit value, making DWORD (or Double Word) a 32bit value.

Message: WM_COMMAND

Sent when a menu item or accelerator key is pressed. The low word or wParam is the id of
the menu item. So if you had defined in the resource editor a menu with two items, save and
load and given them the ids IDM_SAVE and IDM_LOAD you might write:

case WM_COMMAND:
{

switch(LOWORD(wParam))
{
case IDM_SAVE:
SaveProject()
break;
case IDM_LOAD:
LoadProject()
break;
default:
break;
}
}

Message: WM_KEYDOWN

This message is sent to your window when a key is pressed. Note that there is also a
WM_KEYUP message. The wParam contains a virtual key code and the lParam specifies
some extra information like the repeat count, extended-key flag, context code, previous key-
state flag etc.

case WM_KEYDOWN:
{
switch(wParam)
{
case 'W':
// w key pressed
break;
case VK_RIGHT:
// Right arrow pressed
break;
default:
break;
}
}

There are many virtual key codes available. As you can see above you can type the character
(upper case) for letters and numbers but for special keys you need to use a code like
VK_RIGHT, VK_LEFT, VK_HOME etc. These are all defined in Winuser.h.

It is not recommended to use WM_KEYDOWN for text entry as the keyboard layout depends
a lot on the locality etc. instead you should use WM_CHAR.

Message: WM_MOUSEMOVE

This message is sent to your window when the mouse is moved over its surface area.
wParam indicates if a specific key or mouse button is held down. The low word of lParam
is the x position of the mouse and the high word is the y position. So to retrieve the position
and button states:

case WM_MOUSEMOVE:
{

// Retrieve mouse screen position


int x=(short)LOWORD(lParam);
int y=(short)HIWORD(lParam);

// Check to see if the left button is held down:


bool leftButtonDown=wParam & MK_LBUTTON;

// Check if right button down:


bool rightButtonDown=wParam & MK_RBUTTON;

In your game you may want to control a camera rotation dependant on the change in the
mouse position. To do this you need to remember the previous mouse position and use the
difference to map onto camera rotations. If you are running your game in a window you can
get into problems when the mouse leaves the window area as you stop receiving mouse
messages. To solve this you can capture the mouse, this means only your window receives
messages from the mouse. Obviously you do not want to do this all the time or the user
would not be able to work outside of your program so one way to do it is to capture the
mouse only when the left button is first held down and then release it when the button is
lifted.

To capture the mouse:

SetCapture(hWnd);

To release the mouse:

ReleaseCapture();
Messages: WM_LBUTTONDOWN, WM_RBUTTONDOWN

These are sent if the left button or right button on the mouse has been pressed.

Messages: WM_LBUTTONUP, WM_RBUTTONUP

These are sent if the left button or right button on the mouse has been released.

Posting message manually


To send a message you can use PostMessage() or SendMessage(). PostMessage() puts the
message into the Message Queue and returns immediatly. That means once the call to
PostMessage() is done the message may or may not have been processed yet.
SendMessage() sends the message directly to the window and does not return untill the
window has finished processing it. If we wanted to close a window we could send it a
WM_CLOSE message like this PostMessage(hwnd, WM_CLOSE, 0, 0); which would have the
same effect as clicking on the button on the top of the window. Notice that wParam and
lParam are both 0. This is because, as mentioned, they aren't used for WM_CLOSE.

Message that must be processed


Note: you must return DefWindowProc if you do not handle a message or your Window will
not appear. Also do not trap a message if you are not going to do anything with it e.g. do not
trap WM_PAINT and then not do any painting as the window will not be redrawn.

The WM_DESTROY message must be handled, this message is sent if the user has closed the
window. You should post a message telling windows to destroy the window as shown above.

The WM_COMMAND message is sent to your window when a menu item is selected or an
accelerator key pressed. The low word of the wParam contains the id of the menu item (as
you defined it in the resource editor).

The WM_PAINT message is sent when your window needs redrawing, this can happen when
the window is created, when another window is removed from over the top of our window,
when the window is maximized etc. As I mentioned earlier you can only draw to your
window when Windows tells you to, however if you want to change what is displayed
without waiting you can tell Windows that your window is dirty and needs a redraw by
using this function:

InvalidateRect(hWnd,NULL,TRUE);

The first parameter is the handle of the window, the second is the rectangular are of the
window that needs redrawing or NULL to indicate the whole window. The third parameter is
TRUE if you want the current contents to be erased first.

Note: if you handle the WM_PAINT message you must actually do some drawing otherwise
the window will not be refreshed. So if you do not want to do anything do not trap the
message.
Note: there are loads and loads of types of messages that can be sent to your window, look in
the MSDN help for details, I will just describe a few of the most useful here:

Dialogs
Once you begin to use dialog boxes, you will need to send messages to the controls in order
to communicate with them. You can do this either by using GetDlgItem() first to get the
handle to the control using the ID and then use SendMessage(), OR you can use
SendDlgItemMessage() which combines the steps. You give it a window handle and a child
ID and it will get the child handle, and then send it the message. SendDlgItemMessage() and
similar APIs like GetDlgItemText() will work on all windows, not just dialog boxes.

What is the Message Queue


Let’s say you were busy handling the WM_PAINT message and suddenly the user types a bunch
of stuff on the keyboard. What should happen? Should you be interrupted in your drawing to
handle the keys or should the keys just be discarded? Wrong! Obviously neither of these
options is reasonable, so we have the message queue, when messages are posted they are
added to the message queue and when you handle them they are removed. This ensure that
you aren't going to miss messages, if you are handling one, the others will be queued up untill
you get to them.

What is a Message Loop


while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

1. The message loop calls GetMessage(), which looks in your message queue. If the
message queue is empty your program basically stops and waits for one (it Blocks).
2. When an event occures causing a message to be added to the queue (for example the
system registers a mouse click) GetMessages() returns a positive value indicating
there is a message to be processed, and that it has filled in the members of the MSG
structure we passed it. It returns 0 if it hits WM_QUIT, and a negative value if an error
occured.
3. We take the message (in the Msg variable) and pass it to TranslateMessage(), this
does a bit of additional processing, translating virtual key messages into character
messages. This step is actually optional, but certain things won't work if it's not there.
4. Once that's done we pass the message to DispatchMessage(). What
DispatchMessage() does is take the message, checks which window it is for and
then looks up the Window Procedure for the window. It then calls that procedure,
sending as parameters the handle of the window, the message, and wParam and
lParam.
5. In your window procedure you check the message and it's parameters, and do
whatever you want with them! If you aren't handling the specific message, you almost
always call DefWindowProc() which will perform the default actions for you (which
often means it does nothing).
6. Once you have finished processing the message, your windows procedure returns,
DispatchMessage() returns, and we go back to the beginning of the loop.

This is a very important concept for windows programs. Your window procedure is not
magically called by the system, in effect you call it yourself indirectly by calling
DispatchMessage().

As you can see, your application spends the majority of its time spinning round and round in
this message loop, where you joyfully send out messages to the happy windows that will
process them. But what do you do when you want your program to exit? Since we're using a
while() loop, if GetMessage() were to return FALSE (aka 0), the loop would end and we
would reach the end of our WinMain() thus exiting the program. This is exactly what
PostQuitMessage() accomplishes. It places a WM_QUIT message into the queue, and instead
of returning a positive value, GetMessage() fills in the Msg structure and returns 0. At this
point, the wParam member of Msg contains the value that you passed to PostQuitMessage()
and you can either ignore it, or return it from WinMain() which will then be used as the exit
code when the process terminates.

IMPORTANT: GetMessage() will return -1 if it encounters an error. Make sure you


remember this, or it will catch you out at some point... even though GetMessage() is defined
as returning a BOOL, it can return values other than TRUE or FALSE, since BOOL is defined as
UINT (unsigned int). The following are examples of code that may seem to work, but will
not process certain conditions correctly:

while(GetMessage(&Msg, NULL, 0, 0))


while(GetMessage(&Msg, NULL, 0, 0) != 0)
while(GetMessage(&Msg, NULL, 0, 0) == TRUE)
The above are all wrong! It may be of note that I used to use the first of these throughout the
tutorial, since as I just mentioned, it works fine as long as GetMessage() never fails, which
when your code is correct it won't. However I failed to take into consideration that if you're
reading this, your code probably won't be correct a lot of the time, and GetMessage() will
fail at some point :) I've gone through and corrected this, but forgive me if I've missed a few
spots.
while(GetMessage(&Msg, NULL, 0, 0) > 0)
This, or code that has the same effect should always be used.

I hope you now have a better understanding of the windows message loop, if not, do not fear,
things will make more sense once you have been using them for a while.

QUESTIONS
In the following code

while(GetMessage(&Msg, NULL, 0, 0) > 0)


{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

1. The program get out of the while loop if it receive a WM_QUIT message. Explain why.
2. Why does the program need to exit the loop if it receive a WM_QUIT message.
3. What happen when DispatchMessage(&Msg); is executed?

6. Windows Messages
Below is a categorical list of the API messages currently documented on this web site. Please
keep in mind that this site does not encompass the entire API yet, so unfortunately may not
find what you are looking for. To suggest any additions you would like to see made, please
contact the author with your request. All pages added since the last update of this site are
clearly marked with NEW.

Classes Type:
 Buttons
 Combo Boxes
 Edit Controls
 IP Address Control
 List Boxes
 Media Control Interface (MCI)
 Menus
 Mouse
 Timers
 Windows

Classes Message:
 Buttons
o BM_CLICK
o BM_GETCHECK
o BM_GETSTATE
o BM_SETCHECK
o BM_SETSTATE
 Combo Boxes
o CB_ADDSTRING
o CB_DELETESTRING
o CB_GETCOUNT
o CB_GETCURSEL
o CB_GETDROPPEDSTATE
o CB_GETLBTEXT
o CB_GETLBTEXTLEN
o CB_INSERTSTRING
o CB_RESETCONTENT
o CB_SETCURSEL
o CB_SHOWDROPDOWN
 Edit Controls
o EM_CANUNDO
o EM_GETFIRSTVISIBLELINE
o EM_GETLINE
o EM_GETPASSWORDCHAR
o EM_GETSEL
o EM_LINEINDEX
o EM_LINELENGTH
o EM_REPLACESEL
o EM_SETPASSWORDCHAR
o EM_SETSEL
o EM_UNDO
 IP Address Control
o IPM_CLEARADDRESS
o IPM_GETADDRESS
o IPM_ISBLANK
o IPM_SETADDRESS
o IPM_SETFOCUS
o IPM_SETRANGE
 List Boxes
o LB_ADDSTRING
o LB_DELETESTRING
o LB_GETCOUNT
o LB_GETCURSEL NEW
o LB_GETSEL NEW
o LB_GETSELCOUNT NEW
o LB_GETSELITEMS NEW
o LB_GETTEXT
o LB_GETTEXTLEN
o LB_INSERTSTRING
o LB_RESETCONTENT
o LB_SETCURSEL NEW
o LB_SETSEL NEW
 Media Control Interface (MCI)
o MM_MCINOTIFY
 Menus
o WM_COMMAND
o WM_INITMENU
o WM_SYSCOMMAND
 Mouse
o WM_LBUTTONDBLCLK
o WM_LBUTTONDOWN
o WM_LBUTTONUP
o WM_MBUTTONDBLCLK
o WM_MBUTTONDOWN
o WM_MBUTTONUP
o WM_MOUSEMOVE
o WM_RBUTTONDBLCLK
o WM_RBUTTONDOWN
o WM_RBUTTONUP
 Timers
o WM_TIMER
 Windows
o WM_CLOSE
o WM_GETTEXT
o WM_GETTEXTLENGTH
o WM_HELP
o WM_SETTEXT

7. Message Boxes
8.
Introduction
A message box is a rectangle object that displays short message to the user. The message can be made
of one sentence, one paragraph, or a few paragraphs. To make the creation of a message box easy, the
Win32 library provides a specific function that can be used to for this purpose.

Message Box Creation

To create a message box, use the MessageBox() function. Its syntax is:

int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

The first argument, hWnd, can be a handle to the window from where the message box will be called.
Otherwise, it can NULL.

The second argument, lpText, is a null-terminated string, such as an array of characters. This is the
actual message that will be presented to the user. As stated already, it can be one word, a whole
sentence, a paragraph, even a hew paragraphs.

The third argument, lpCaption, is the title that will display on the title bar. It also can be a null-
terminated string, if you know what title you would like to display. Otherwise, it can be NULL, in
which case the title bar would display Error.

The simplest way you can create a message is by calling the MessageBox() function with all
arguments set to NULL, in which case the message box would not make any sense:

MessageBox(NULL, NULL, NULL, NULL);

As stated already, the first argument is either a handle of the window that is calling it, or NULL.

The simplest way to specify the second argument is by including a word or a sentence in double-
quotes. Here is an example:

MessageBox(NULL, L"I am just trying my wedding dress", NULL, NULL);


If you want to display the message on various lines, you can separate sections with the new line
character '\n'. Here is an example:

MessageBox(NULL, L"It happened earlier\nDidn't it?", NULL, NULL);

You can also use string editing techniques to create a more elaborate message. This means that you
can use functions of the C string library to create your message.

The caption of the message can be any word or sentence but convention wisdom would like this
sentence to be in tune with the actual message. After all, unless the message is about bad news, Error
as a title is not particularly cute.

The fourth argument actually does three things. First it displays one or a few buttons. The buttons
depend on the value specified for the argument. If this argument is NULL, the message box displays
(only) OK. The values and their buttons can be as follows:

Constant Integer Buttons

MB_OK

MB_OKCANCEL

MB_ABORTRETRYIGNORE

MB_YESNOCANCEL

MB_YESNO

MB_RETRYCANCEL

MB_CANCELTRYCONTINUE

MB_HELP
Besides the buttons, the message box can also display a friendly icon that accompanies the message.
Each icon is displayed by specifying a constant integer. The values and their buttons are as follows:

Value Icon Suited when

MB_ICONEXCLAMATION Warning the user of an action performed on the


MB_ICONWARNING application

MB_ICONINFORMATION
Informing the user of a non-critical situation
MB_ICONASTERISK

Asking a question that expects a Yes or No, or a


MB_ICONQUESTION
Yes, No, or Cancel answer

MB_ICONSTOP A critical situation or error has occurred. This icon


MB_ICONERROR is appropriate when informing the user of a
MB_ICONHAND termination or deniability of an action

The icons are used in conjunction with the buttons constant. To combine these two flags, use the
bitwise OR operator ―|‖.

The second thing this fourth argument does is to let the user close the message box after selecting one
of the buttons. Once the user clicks one of the buttons, the message box is closed.

The third role of this fourth argument is to control the result derived from the user dismissing the
message box. For example, clicking OK usually means that the user acknowledges what the message.
Clicking Cancel usually means the user is changing his or her mind about the action performed
previously. Clicking Yes instead of No usually indicates that the user agrees to perform an action.

In reality, the message box only displays a message and one or a few buttons. It is your responsibility
as the programmer to decide what to do when what button is clicked.

When a message box is configured to display more than one button, the operating system is set to decide
which button is the default. The default button has a thick border that sets it apart from the other
button(s). If the user presses Enter, the message box would behave as if the user had clicked the
default button. Fortunately, if the message box has more than one button, you can decide what button
would be the default. To specify the default button, use one of the following constants:

If the message box has more than one button,


Value the default button would be

MB_DEFBUTTON1 The first button

MB_DEFBUTTON2 The second button

MB_DEFBUTTON3 The third button

MB_DEFBUTTON4 The fourth button

To specify the default button, use the bitwise OR operator to combine the constant integer of the
desired default button with the button's constant and the icon.
Practical Learning: Introducing Additional Resources

1. Create a new Win32 application


2. If you are using Microsoft Visual C++, set the location to a folder called Win32C
3. If you are using Microsoft Visual C++, create a C++ source file and save it as Main.cpp
4. Replace the file's content with the following:
Test the program and return to your programming environment
5. To create a more elaborate message, change the file as follows:

//---------------------------------------------------------------------------
#include <windows.h>

//---------------------------------------------------------------------------

LPCTSTR Caption = L"Application Programming Interface";

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,


LPSTR lpCmdLine, int nCmdShow)
{
MessageBox( NULL,
L"Welcome to Win32 Application Development\n"
L"You will learn about functions, classes, "
L"communication, and other cool stuff\n"
L"Are you ready to rumble!!!!!!!!!!!!!!",
Caption,
MB_YESNOCANCEL | MB_ICONQUESTION);

return 0;
}
//---------------------------------------------------------------------------

6. Test the application


7. Return to your programming environment
8. If you remove the L prefix infront of all the string and reassemble, you will get the following
errors:
>c:\users\zuraimi\documents\visual studio 2008\projects\win32d\win32d\win32d.cpp(17) : error C2664:
'MessageBoxW' : cannot convert parameter 2 from 'const char [156]' to 'LPCWSTR'

9. To solve the error select Project|Main properties.


10. Under the Main property Page on the Charater set selection chose Use Multi-Byte Character Set
and press select <OK>
11. GTest the application

9. Modal and Modeless Windows


Forms
Forms and dialog boxes are either modal or modeless.

A modal form or dialog box must be closed or hidden before you can continue working with
the rest of the application. For more information about working with dialog boxes, see User
Input to Dialog Boxes.

Dialog boxes that display important messages should always be modal. The About dialog
box in Visual Studio is an example of a modal dialog box.

Modeless forms let you shift the focus between the form and another form without having to
close the initial form. The user can continue to work elsewhere in any application while the
form is displayed.

Modeless forms are harder to program, because users can access them in an unpredictable
order. You have to keep the state of the application consistent no matter what the user
does. Often, tool windows are shown in a modeless fashion. The Find dialog box,
accessible from the Edit menu in Visual Studio, is an example of a modeless dialog box.
Use modeless forms to display frequently used commands or information.

10. Resources Fundamentals


Introduction

A resource is an object that cannot be defined in C++ terms but that is needed to complete a program.
In the strict sense, it is text that contains a series of terms or words that the program can interpret
through code. Examples of resources are menus, icons, cursors, dialog boxes, sounds, etc.

There are various means of creating a resource and the approach you use depends on the resource. For
example, some resources are completely text-based, such is the case for the String Table or the
Accelerator Table. Some other resources must be designed, such is the case for icons and cursors.
Some other resources can be imported from another, more elaborate application, such is the case for
high graphic pictures. Yet some resources can be a combination of different resources.

Resource Creation

As mentioned already, resources are not a C++ concept but a Microsoft Windows theory of
completing an application. Therefore, the programming environment you use may or may not
provide you with the means of creating certain resources. Some environments like Borland
C++ Builder or Visual C++ (6 and .NET) are complete with (almost) anything you need to
create (almost) any type of resources. Some other environments may appear incomplete,
allowing you to create only some resources, the other resources must be created using an
external application not provided; such is the case for C++BuilderX.

Upon creating a resource, you must save it. Some resources are created as their own file, such
is the case for pictures, icons, cursors, sound, etc. Each of these resources has a particular
extension depending on the resource. After creating the resources, you must add them to a
file that has the extension .rc. Some resources are listed in this file using a certain syntax.
That's the case for icons, cursors, pictures, sounds, etc. Some other resources must be created
directly in this file because these resources are text-based; that's the case for menus, strings,
accelerators, version numbers, etc.

After creating the resource file, you must compile it. Again, some environments, such as
Microsoft Visual C++, do this automatically when you execute the application. Some other
environments may require you to explicitly compile the resource. That's the case for Borland
C++ Builder and C++BuilderX. (The fact that these environments require that you compile
the resource is not an anomaly. For example, if you create a Windows application that is
form-based in C++ Builder 6 or Delphi, you can easily add the resources and they are
automatically compiled and added to the application. If you decide to create a Win32
application, C++ Builder believes that you want to completely control your application; so, it
lets you decide when and how to compile a resource. This means that it simply gives you
more control).
11. Component of Resource Script
Identifiers
Identifiers are generally named in a certain way, although the reader and all programmers are
free to alter this naming scheme. It is simply a suggestion. Identifiers generally start with the
prefix "ID", followed by a letter that denotes the type of identifier:

 IDS: A string resource


 IDM: A menu resource
 IDC: A command identifier
 IDD: A dialog box resource
 IDA: An Accelerator table resource
 IDI: An Icon or bitmap resource
 IDB: A Bitmap resource
 ID: A custom resource, or an uncommon resource type.

Sometimes, the command identifiers in a menu are given an "IDM_" prefix, to distinguish
between commands from other sources.

DISCARDABLE
Resources are loaded into memory when the program is run. However, if a resource is not in
use, and if Windows does not need them immediately, resources can be optionally unloaded
from memory until needed. To specify that it is okay to unload an unused resource from
memory, you may list the DISCARDABLE keyword with the resource. DISCARDABLE
resources allow more efficient memory usage, but can slow down your program if they need
to be loaded from disk.

The DISCARDABLE keyword is ignored for 32-bit Windows, but remains for compatibility.

Icons
Icons can be stored in a resource file using the ICON keyword. Here is a general example of
using an Icon in a resource script:

(icon ID number or name) ICON [DISCARDABLE] "iconfile.ico"

Windows explorer will display the program executable with the first icon from the script. For
instance, if we load 2 icons, as such:

IDI_ICON1 ICON DISCARDABLE "icon1.ico"


IDI_ICON2 ICON DISCARDABLE "icon2.ico"

And we define our macros as such in our resource.h:

#define IDI_ICON1 1
#define IDI_ICON2 2

The executable file will have icon1.ico as its icon.

To Load an icon from an executable module, assuming we have an instance handle to the
module ("hInst" in this example), we can get a handle to the icon as such:

HICON hIcon;
hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1));

This will return a handle to the icon associated with the identifier "IDI_ICON1". Icon
identifiers are generally prefixed with an "IDI_" which is short for "ID for an Icon".

The second parameter to the LoadIcon function is a pointer to a string. String pointers are 32
bit values. However, if the most signficant 16 bits are all zero, Windows will treat the value
as a resource number, and not a string. To make the conversion between a string and a 16-bit
integer, Microsoft provides the MAKEINTRESOURCE macro. Similarly, we could have
used a string to define our Icon:

MYICON1 ICON DISCARDABLE "icon1.ico"

And we could load this string by name:

HICON hIcon;
hIcon = LoadIcon(hInst, "MYICON1");

String identifiers for resources are case insensitive.

WNDCLASSEX has handle values for 2 icons: a large icon and a small icon. The small icon
is the icon used in the upper-left corner. Small icons are generally 16 pixels square. Larger
icons are 32 pixels square. If no small icon handle is provided, the large icon will be shrunk
down to fit.

If the LoadIcon function is supplied with a NULL instance handle, Windows will supply a
default icon for use.

Recently, the Win32 API provides the LoadImage function for loading icons, bitmaps, and
mouse cursors from a single function. You can find more information about this function on
MSDN.

Bitmaps
Bitmaps can be loaded similarly to Icons in resource files:

(bitmap ID or name) BITMAP [DISCARDABLE] "bitmapfile.bmp"

Bitmaps can be accessed with the aptly named LoadBitmap function (again, new versions of
the Win32 API prefer you use LoadImage to load a bitmap, icon, or cursor). LoadBitmap
returns an HBITMAP handle type:
HBITMAP hBmp;
hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));

Or, if we have named our bitmap resource:

hBmp = LoadBitmap(hInst, "MyBitmapRes");

Bitmaps are large resources, and if windows can't load the bitmap into memory (or if the ID
or name value is invalid), the function will return a NULL value. Make sure you test this
value before you use the handle.

Bitmaps must be unloaded from memory by passing the handle to the DestroyObject
function. You can find more information about this on MSDN

Bitmap identifiers generally use a "IDB_" prefix, to indicate that it is the ID of a bitmap.
Mouse Cursors
Mouse cursors are specified similarly to icons and bitmaps, and are loaded with the
LoadCursor function.

String Tables
A resource script can have many string tables, although this is unneccessary: the tables aren't
differentiated, and each string object, in any table, must have a unique identifier. Strings in a
string table also may not use names, but instead must use numeric identifiers. After all, it
doesn't make any sense to have to address a string with a string, does it?

Here is a general string table:

STRINGTABLE DISCARDABLE
BEGIN
IDS_STRING1, "This is my first string"
IDS_STRING2, "This is my second string"
...
END

It is important to note that in place of the BEGIN and END keywords, the programmer may
also use the more C-like curly brackets, as such:

STRINGTABLE DISCARDABLE
{
IDS_STRING1, "This is my first string"
IDS_STRING2, "This is my second string"
...
}

Some people prefer one over the other, but they are all the same to the resource compiler.

Strings can be loaded using the LoadString function. LoadString is more involved then the
LoadBitmap or LoadIcon functions:

int LoadString(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int


nBufferMax);

The hInstance parameter, as we know, is the instance handle for the module that contains the
string. The uID parameter contains the string number that we are trying to access. lpBuffer is
the character array variable that will receive the string, and the nBufferMax number tells
windows what the maximum number of characters that can be loaded is. This count is a
security precaution, so make sure not to allow Windows to write character data beyond the
end of the string. MSDN displays a large warning on the page for this function, and it is
important that programmers heed this warning. msdn

Windows will automatically zero-terminate the string, once it is written to the buffer.
LoadString will return the number of characters that were actually written into the string, in
case the number of characters is less then the maximum number allowed. If this return value
is 0, the string resource does not exist, or could not be loaded.

Accelerators
Keyboard accelerators are a common part of nearly every windows application, and therefore
it is a good idea to simplify the job of creating accelerators by putting them in a resource
script. Here is how to create an accelerator table:

(Accelerator Table ID or name) ACCELERATORS [DISCARDABLE]


BEGIN
(key combination), (Command ID)
...
END

Key combinations are specified in terms of either a string literal character ("A" for instance)
or a virtual key code value. Here are some examples:

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE


BEGIN
"A", IDA_ACTION_A //Shift+A
END

Now, when the key combination "Shift+A" is pressed, your window procedure will receive a
WM_COMMAND message with the value IDA_ACTION_A in the WPARAM field of the
message.

If we want to use combinations of the "Alt" key, or the "Ctrl" key, we can use the ALT and
CONTROL keywords, respectively:

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE


BEGIN
"a", IDA_ACTION_A, ALT //Alt+A
"b", IDA_ACTION_B, CONTROL //Ctrl+B
"c", IDA_ACTION_C, ALT, CONTROL //Alt+Ctrl+A
END

Also, we can use the "^" symbol to denote a CONTROL key code:

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE


BEGIN
"^a", IDA_ACTION_A //Control+A
END

Similarly, if we want to be super hackers, could use the ASCII code directly:

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE


BEGIN
65, IDA_ACTION_A, ASCII //65 = "A", Shift+A
END

Or, we could refer to keys (including non-alphanumeric keys) with their Virtual Key Code
identifiers, by using the VIRTKEY identifier:
IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE
BEGIN
VK_F12, IDA_ACTION_F12, VIRTKEY //press the "F12 Key"
VK_DELETE, IDA_ACTION_DEL, VIRTKEY, CONTROL //Ctrl+Delete
END

Now, If we make an accelerator correspond to a menu command, the menu command will
light up when we press the accelerator. That is, the menu will light up unless we specify the
"NOINVERT" keyword:

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE


BEGIN
"A", IDA_ACTION_A, NOINVERT //Shift+A (non inverted menu selection)
END

To Load an accelerator table, we need to use the LoadAccelerators function, as such:

HACCEL hAccel;
hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDA_ACCEL_TABLE));

Again, we could have given our resource a string name, and used that string to load the table.

When using accelerators, we need to alter our message loop to intercept the keypress
messages, and translate them into command messages according to our accelerator table
rules. We use the TranslateAccelerator function, to intercept the keypress messages, and
translate them into command messages, as such:

while ( (Result = GetMessage(&msg, NULL, 0, 0)) != 0)


{
if (Result == -1)
{
// error handling
}
else
{
if (!TranslateAccelerator(hwnd, haccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

Also, if we are writing an MDI application, we need to intercept Accelerator messages from
the child windows, we use the TranslateMDISysAccel function also:

while ( (Result = GetMessage(&msg, NULL, 0, 0)) != 0)


{
if (Result == -1)
{
// error handling
}
else
{
if ( !TranslateMDISysAccel(hwndClient, &msg)
&& !TranslateAccelerator(hwndFrame, haccel, &msg) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

Where "hwndFrame" is the handle to the frame window, and "hwndClient" is the handle to
the MDI client window.

Menus
Menus can be defined in a resource script using the MENU keyword. There are 2 types of
items that appear in a menu, the top level "POPUP" menu items, and the secondary
"MENUITEM" items. These are defined in a menu as such:

(ID or name) MENU [DISCARDABLE]


BEGIN
POPUP "File"
POPUP "Edit"
BEGIN
MENUITEM "Copy", IDM_EDIT_COPY
MENUITEM "Paste", IDM_EDIT_PASTE
END
...
END

We have included a few examples here, so that you can see the difference between a POPUP
and a MENUITEM. When we have a menu with the ID_MENU identifier, we can load it into
our program as such:

HMENU hmenu;
hmenu = LoadMenu(hInst, MAKEINTRESOURCE(ID_MENU));

Once we have this handle, we can pass it to the CreateWindow function, and apply it to our
window.

When a menu item is selected, the host program receives a WM_COMMAND message, with
the menu item identifier in the WPARAM parameter. If we have a basic window procedure
switch-case statement, we can see this as follows:

case WM_COMMAND:
switch(WPARAM)
{
case IDM_EDIT_COPY:
//handle this action
break;
case IDM_EDIT_PASTE:
//handle this action
break;
}
break;

In a menu, if we want to associate a menu item with an accelerator, we can define it as such:
ID_MENU MENU DISCARDABLE
BEGIN
POPUP "File"
POPUP "Edit"
BEGIN
MENUITEM "&Copy", IDM_EDIT_COPY
MENUITEM "&Paste", IDM_EDIT_PASTE
END
...
END

Notice how we put the ampersand (&) in front of the "C" in "Copy" and the "P" in "Paste".
This means that those letters will be underlined, but more importantly, if an accelerator key
combination is pressed, those items in the menu will be highlighted (unless the NOINVERT
tag is specified in the accelerator table). If an ampersand is placed before a POPUP menu
item, pressing ALT+ that letter will popup that menu. For instance, lets define our menu:

ID_MENU MENU DISCARDABLE


BEGIN
POPUP "&File"
POPUP "&Edit"
BEGIN
MENUITEM "Copy", IDM_EDIT_COPY
MENUITEM "Paste", IDM_EDIT_PASTE
END
...
END

Now, if we press ALT+F, we will pop open the File menu, and if we press ALT+E it will
open the Edit menu. That's pretty nice functionality for only a single extra character to type.

Version Information
A program can include certain information about its version, and its author in a resource
script. This version information appears when you right-click the executable in Windows, and
click "Properties". In the properties dialog box, this information appears on the "Version" tab.

Dialog Boxes
Dialog box resources follow a general pattern:

(Dialog ID or name) DIALOG [DISCARDABLE] x, y, width, height


TITLE "(dialog box title)"
[CLASS "(class name)"]
FONT "(font name)"
BEGIN
...
END

if a dialog box is not being associated with a class, the CLASS field does not need to be filled
in. All strings listed as being in quotes must be in quotes in the resource script or there will
be an error. Individual items in a dialog box are then specified between the BEGIN and END
tags.
12. Resource-Definition Statements
The resource-definition statements define the resources that the resource compiler puts in the resource (.Res)
file. After the .Res file is linked to the executable file, the application can load its resources at run time as
needed. All resource statements associate an identifying name or number with a given resource.

The resource-definition statements can be divided into the following categories:

 Resources
 Controls
 Statements
The following tables describe the resource-definition statements.

Resources
Resource Description

ACCELERATORS Defines menu accelerator keys.

BITMAP Defines a bitmap by naming it and specifying the name of the file that contains it. (To use a particular bitmap
name.)

CURSOR Defines a cursor or animated cursor by naming it and specifying the name of the file that contains it. (To use
application requests it by name.)

DIALOG Defines a template that an application can use to create dialog boxes.

DIALOGEX Defines a template that an application can use to create dialog boxes.

FONT Specifies the name of a file that contains a font.

HTML Specifies an HTML file.

ICON Defines an icon or animated icon by naming it and specifying the name of the file that contains it. (To use a p
requests it by name.)

MENU Defines the appearance and function of a menu.

MENUEX Defines the appearance and function of a menu.

MESSAGETABLE Defines a message table by naming it and specifying the name of the file that contains it. The file is a binary
themessage compiler.

POPUP Defines a menu item that can contain menu items and submenus.

PLUGPLAY Obsolete.

RCDATA Defines data resources. Data resources let you include binary data in the executable file.

STRINGTABLE Defines string resources. String resources are Unicode or ASCII strings that can be loaded from the executab

TEXTINCLUDE A special resource that is interpreted by Visual C++. For more information, see TN035.

TYPELIB A special resource that is used with the /TLBID and /TLBOUT linker options.

User-Defined Defines a resource that contains application-specific data.

VERSIONINFO Defines a version-information resource. Contains information such as the version number, intended operating
VXD Obsolete.

For more information about pre-defined MFC resources, see TN023 and TN024.

Controls
Control Description

AUTO3STATE Creates an automatic three-state check box control.

AUTOCHECKBOX Creates an automatic check box control.

AUTORADIOBUTTON Creates an automatic radio button control.

CHECKBOX Creates a check box control.

COMBOBOX Creates a combo box control.

CONTROL Creates an application-defined control.

CTEXT Creates a centered-text control.

DEFPUSHBUTTON Creates a default pushbutton control.

EDITTEXT Creates an edit control.

GROUPBOX Creates a group box control.

ICON Creates an icon control. This control is an icon displayed in a dialog box.

LISTBOX Creates a list box control.

LTEXT Creates a left-aligned text control.

PUSHBOX Creates a push box control.

PUSHBUTTON Creates a push button control.

RADIOBUTTON Creates a radio button control.

RTEXT Creates a right-aligned control.

SCROLLBAR Creates a scroll bar control.

STATE3 Creates a three-state check box control.


Statements
Statement Description

CAPTION Sets the title for a dialog box.

CHARACTERISTICS Specifies information about a resource that can be used by tool that can read or write resource-definition

CLASS Sets the class of the dialog box.

EXSTYLE Sets the extended window style of the dialog box.

FONT Sets the font with which the system will draw text for the dialog box.

LANGUAGE Sets the language for all resources up to the next LANGUAGE statement or to the end of the file. When t
appears before the beginning of the body of an ACCELERATORS, DIALOG, MENU, RCDATA, or STRING
the specified language applies only to that resource.

MENU Sets the menu for the dialog box.

MENUITEM Defines a menu item.

STYLE Sets the window style for the dialog box.

VERSION Specifies version information for a resource that can be used by tool that can read or write resource-defin
13. Using Resources
In Microsoft Windows, resources are read-only data embedded in EXE or DLL files.

The Windows API provides for easy access to all applications' resources.

Each resource has a type and a name, both being either numeric identifiers or strings.

Windows has a set of predefined resource types:

 Cursor and animated cursor


 Icon
 Bitmap
 Dialog box template
 Font
 HTML document
 String and message template
 Version data

The programmer can also define custom data types in resources.

Before we get any deeper I will cover the topic of resources so that I won't have to re-write it
for each section. You don't actually need to compile the stuff in this section, it's as
example only.

Resources are pre-defined bits of data stored in binary format inside your executable file.

Creating resource
You ca create resources in a resources script, a file with an extension of ".rc". commercial
compilers will have a visual resource editor which allows you to create resources without
manually editing this file but sometimes editing it is the only way to go, especially if your
compiler has no visual editor, it sucks, or doesn't support the exact feature you need.

Unfortunately different compiler suites handle resources differently. I will do the best I can to
explain the common features needed to work with resources in general.

The resource editor included with MSVC++ makes it very difficult to edit the resources
manually, since it enforces a proprietary format on them, and will totally mangle the file if
you save one that you had created by hand. In general you shouldn't bother with creating .rc
files from scratch, but knowing how to modify them manually can be very useful. Another
annoyance is that MSVC++ will by default name the resource header file "resource.h" even if
you wanted to call it something else. I will go with this for the sake of simplicity in this
document, but will show you how to change this in the appendix on compilers.

1. Create a new project name FirstMenu.


2. Create FirstMenu.cpp and copy program in SimpleWindow.cpp into it.
3. On the Resource Files folder of the Solution Explorer create FirstMenu.rc file.
4. The on the Header Files folder of the Solution Explorer create resource.h file.
5. Right-click on the resource.h icon and select Open With...,
a. then select Source code (Text) Editor under Choose the program to want to
use to open this menu.
b. then select the [OK] button.
c. A FirstMenu.rc text editor window will open.

First let’s take a very simple resource script, with a single icon.

#include "resource.h"

IDI_MYICON ICON "my_icon.ico"

That's the entire file. IDI_MYICON is the identifier of the resource, ICON is the type and
"my_icon.ico" is the name of the external file which contains it. This should work on any
compiler.

Now search for ―*,ico‖ files in your hard disk and copy one of the icon file of your choice, to
the same directory as where ―FirstMenu.cpp‖ is located. Rename the copied icon file to
"my_icon.ico".

Now what about this #include "resource.h" ? Well your program needs a way to identify
the icon, and the best way to do that is to assign it a unique ID (IDI_MYICON). We can do this
by creating the file "resource.h" and including it in both our resource script, and our source
file.

#define IDI_MYICON 101

As you can see, we've assigned IDI_MYICON the value of 101. We could just forget about the
identifier and use 101 wherever we need to reference the icon, but IDI_MYICON is a lot clearer
as to what you are refering too, and easier to remember when you have large number of
resources.

Now lets say we add a MENU resource:

#include "resource.h"

IDI_MYICON ICON "my_icon.ico"

IDR_MYMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_FILE_EXIT
END
END

Again IDR_MYMENU is the name of the resource and MENU is the type. Now a fine point, see the
BEGIN and END up there? Some resource editors or compilers use { in place of BEGIN and } in
place of END. If your compiler supports both feel free to pick which one you use. If it only
supports one or the other, you will need to make the necessary replacements to get it to work.
We've also added a new identifier, ID_FILE_EXIT, so we need to add this to our resource
header file, resource.h, in order to use it in our program.

#define IDI_MYICON 101

#define ID_FILE_EXIT 4001

Generating and keeping track of all these ids can become a real chore with large projects,
that's why most people use a visual resource editor which takes care of all this for you. They
still screw up from time to time, and you could end up with multiple items with the same ID
or a similar problem, and it's good to be able to go in and fix it yourself.

Now an example of how to use a resource in your program.

HICON hMyIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON));

The first parameter of LoadIcon() and many other resource using functions is the handle to
the current instance (which we are given in WinMain() and can also be retreived by using
GetModuleHandle() as demonstrated in previous sections). The second is the identifier of
the resource.

You're probably wondering what's up with MAKEINTRESOURCE() and possibly wondering why
LoadIcon() takes a parameter of type LPCTSTR instead of say UINT when we're passing it an
ID. All MAKEINTRESOURCE() does is cast from an integer (what our ID is) to LPCTSTR, which
LoadIcon() expects. This brings us to the second way of identifying resources, and that's
with strings. Almost nobody does this any more, so I won't go into details, but basically if
you don't use #define to assign an integer value to your resources then the name is interpreted
as a string, and can be referenced in your program like this:

HICON hMyIcon = LoadIcon(hInstance, "MYICON");

LoadIcon() and other resource loading APIs can tell the difference between an integer
passed in and a pointer to a string passed in by checking the high word of the value. If it's 0
(as would be the case of any integer with a value less than or equal to 65535) then it assumes
it is a resource ID. This effectively limits your resources to using IDs below 65535, which
unless you have a whole lot of resources, should not be a problem. If it's not 0 then it assumes
the value is a pointer, and looks up the resource by name. Never rely on an API to do this
unless it is explicitly stated in the documentation.

For example, this doesn't work for menu commands like ID_FILE_EXIT, since they can only
be integers.
14. Menus and Icons using resource
A menu is one of the text-based resources. It is created directly in the rc file. As with other
resources, the process of creating a menu depends on the environment you are using. If you
are using Borland C++ Builder, you can open your rc file and manually create your menu.

If you are using Microsoft Visual C++, you can use the built-in menu editor. In this case, the
actual text that defines and describes the menu would be automatically added to the rc file.

But for Microsoft Visual C++ express edition the menu editor is not built in.

Creating menu using resource script.


Example: menu_one.

This is just a small section to show how to add basic


menus to your window. Usually you use a pre-made
menu resource. This will be in an .rc file and will be
compiled and linked into your .exe. This is rather
compiler specific, commercial compilers will have a
resource editor that you can use to create your menus,
but for this example I will show the text of the .rc file
so you can add it in manually. I usually have an .h file as well which is included in both my
.rc file and my .c source files. This file contains the identifiers for controls and menu items
etc.

For this example you can start with the window code from simple_window and add this code
into it as instructed.

First the .h file. Usually called "resource.h"

#define IDR_MYMENU 101


#define IDI_MYICON 201

#define ID_FILE_EXIT 9001


#define ID_STUFF_GO 9002
/*Not much there, but our menu will be pretty simple. The names and values here are up to
you for the choosing. Now we write our .rc file. */
#include "resource.h"

IDR_MYMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_FILE_EXIT
END

POPUP "&Stuff"
BEGIN
MENUITEM "&Go", ID_STUFF_GO
MENUITEM "G&o somewhere else", 0, GRAYED
END
END

IDI_MYICON ICON "menu_one.ico"

You will want to add the .rc file to your project or makefile depending on what tools you are
using.

You also want to #include "resource.h" in your source file (.cpp) so that the menu
command identifiers and the menu resource id will be defined.

The easiest way to attach the menu and icon to your window is to specify them when you
register the window class, like this:

wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU);
wc.hIcon = LoadIcon(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDI_MYICON));
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 16, 16, 0);

Search for an icon file (*.ico) in your file system and copy it to the same directory as where
menu_one.cpp is located and rename as menu_one.ico.

Change that and see what happens. Your window should now have a File and Stuff menu
with the respective items underneath. That is assuming your .rc file was properly compiled
and linked into your program. (again, see compiler notes)

The icon in the top left of the window and on the task bar should now display the small
custom icon that we specified. If you hit Alt-Tab, the large version of the icon should be
displayed in the application list.

I've used LoadIcon() to load the large icon because it's simpler, however it will only load
icons at the default resolution of 32x32, so in order to load the smaller image, we need to use
LoadImage(). Be aware that icon files and resources can contain multiple images, and in this
case the ones I've supplied contain the two sizes that I'm loading.

Defining id numbers

In ―resource .h ― we have the following declaration.

#define IDR_MYMENU 101


#define IDI_MYICON 201

#define ID_FILE_EXIT 9001


#define ID_STUFF_GO 9002

The declaration defines id number for the respective identifier. It can be of any value of the
16 bit number (0 to 65535) but must be unique of each other or else the identifier of the same
id number will be defined as duplicates of each other.
This resource.h file is included in menu_one ,cpp and menu_one,rc, so both of these file will
have the same id declaration.

Processing the Messages generated by menu resources.

In the menu_one .rc file, we have the following definition.


MENUITEM "E&xit", ID_FILE_EXIT
MENUITEM "&Go", ID_STUFF_GO
When we select menu File|Exit windows will be send a message to the message queue with
WS_COMMAND as the as wParam data and the value of ID_FILE_EXIT the LOWORD of
lParam data.
So in Window procedure we add the following code under the awitch (msg

Creating menu using child process.


A child process is a process that is created by another process, called the parent process.

Example: menu_two
An alternative to using a menu resource is to create one on the fly (or when your program
runs). This is a bit more work programming wise, but adds flexibility and is sometimes
necessary.

You can also use icons that aren't stored as resources, you could choose to store your icon as
a separate file and load it at runtime. This would also give you the option of allowing the user
to select an icon of their choice with the common dialogs discussed later, or something to that
effect.

Start again a new empty project name menu_two then create menu_two.cpp and copy the
program SimpleWindow into menu_two.cpp. Do not create the .h or .rc. Now we will handle
the WM_CREATE message and add a menu to our window.

#define ID_FILE_EXIT 9001


#define ID_STUFF_GO 9002

Put these two id's at the top of your .c file this time, underneath your #includes. Next we add
the following code into our WM_CREATE handler.
case WM_CREATE:
{
HMENU hMenu, hSubMenu;
HICON hIcon, hIconSm;

hMenu = CreateMenu();
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File");
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff");
SetMenu(hwnd, hMenu);

hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32,


LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!", "Error",
MB_OK | MB_ICONERROR);
hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16,
LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!", "Error",
MB_OK | MB_ICONERROR);
}
break;

When built, you will get errors. You will need to type cast data (in bold) in the following
statement to remove the errors.

hIcon = (HICON)LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32,


LR_LOADFROMFILE);
hIconSm = (HICON)LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16,
16, LR_LOADFROMFILE);

After successfully compiled, the program creates a menu almost the same as the one we had
in the resource and attaches it to our window.

However you will get a runtime error as follows in sequence:

The program cannot load the ―menu_two.ico‖ file inthe following two instance:
hIcon = (HICON)LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32,
LR_LOADFROMFILE);
hIconSm = (HICON)LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16,
16, LR_LOADFROMFILE);

which cause her to display the two messages. To solve the error you need to create the file
―menu_two.ico‖ in the same directory as the ―.cpp‖ file.

A menu that is assigned to a window is automatically removed when the program terminates,
so we don't need to worry about getting rid of it later. If we did though, we could use
GetMenu() and DestroyMenu().

The code for the icons is pretty simple, we call LoadImage() twice, to load the icon as both a
16x16 size and a 32x32 size. We can't use LoadIcon() at all because it will only load
resources, not files. We specify NULL for the instance handle parameter because we aren't
loading a resource from our module, and instead of a resource ID we pass in the name of the
icon file we want to load. Finally, we pass in the LR_LOADFROMFILE flag to indicate that we
want the function to treat the string we give it as a filename and not a resource name.

If each call succeeds we assign the icon handle to our window with WM_SETICON, and if it
fails we pop up a message box letting us know something went wrong.

NOTE: that the LoadImage() calls will fail if the icon file isn't in the current working
directory of the program. If you are using VC++ and you run the program from the IDE, the
current working directory will be the one the project file is in. However if you run the
program from the Debug or Release directories from explorer or the command shell, then
you'll need to copy the icon file into that directory in order for the program to find it. If all
else fails, specify the full path to the icon, "C:\\Path\\To\\Icon.ico".

Okay now that we have our menu, we need to make it do something. This is pretty simple, all
we need to do is handle the WM_COMMAND message. Also we'll need to check which command
we are getting and act accordingly. Now our WndProc() should look something like this.

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM


lParam)
{
switch(Message)
{
case WM_CREATE:
{
HMENU hMenu, hSubMenu;

hMenu = CreateMenu();

hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu,
"&File");

hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu,
"&Stuff");
SetMenu(hwnd, hMenu);

hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32,


LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!", "Error",
MB_OK | MB_ICONERROR);

hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16,


LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!", "Error",
MB_OK | MB_ICONERROR);
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_STUFF_GO:

MessageBox(NULL,myName,"ID_STUFF",MB_ICONEXCLAMATION | MB_OK);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}

As you can see we've got our WM_COMMAND all set up, and it even has another switch() in it.
This switch()'s on the value of the low word of wParam, which in the case of WM_COMMAND
contains the control or menu id that sent the message.

We obviously want the Exit menu item to close the program. So in the WM_COMMAND,
ID_FILE_EXIT handler you can use the following code to do just that.

PostMessage(hwnd, WM_CLOSE, 0, 0);

To read message from menu item we must the LOWORD(wParam) parameter. So your
WM_COMMAND handler should now look like this:

switch(Message)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_STUFF_GO:

break;
}
break;

I leave it up to you to make the other menu command ID_STUFF_GO do something.


Here is the full program if yours doesn’t work.
#include <windows.h>
#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002

char g_szClassName[] = "Menu_two";//Name of window class


char myName[] = "Zuraimi bin Yahya";
// Step 4: the Window Procedure - This is the brain
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_STUFF_GO:
MessageBox(NULL,myName ,"ID_STUFF"
,MB_ICONEXCLAMATION | MB_OK);
break;
}
break;
case WM_CLOSE:
{
DestroyWindow(hwnd);//sends the WM_DESTROY
message to the window
//(hwnd) getting destroyed,
break;
}
case WM_DESTROY:
PostQuitMessage(0);//This posts the WM_QUIT message to
the message loop
//This window will never receive this message, because it
causes
//GetMessage() to return FALSE
break;
case WM_CREATE:
{
HMENU hMenu, hSubMenu;
HICON hIcon, hIconSm;

hMenu = CreateMenu();

hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT,
"E&xit");
AppendMenu(hMenu, MF_STRING | MF_POPUP,
(UINT)hSubMenu, "&File");

hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO,
"&Go");
AppendMenu(hMenu, MF_STRING | MF_POPUP,
(UINT)hSubMenu, "&Stuff");

SetMenu(hwnd, hMenu);

hIcon = (HICON)LoadImage(NULL, "menu_two.ico",


IMAGE_ICON, 32, 32,
LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG,
(LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!",
"Error",
MB_OK | MB_ICONERROR);

hIconSm = (HICON)LoadImage(NULL, "menu_two.ico",


IMAGE_ICON, 16, 16,
LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL,
(LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small
icon!", "Error",
MB_OK | MB_ICONERROR);
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);//allows
//window to, like be sized, maximised, etc...
}
return 0;
}

//
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) //Entry of program
{
WNDCLASSEX wc; //Create WNDCLASSEX object

//Step 1: Registering the Window Class


wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;// Window Procedure: mandatory
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;// owner of the class: mandatory
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);// optional
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);// optional
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;// Name of window class:mandatory
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wc)) //Registering successful


{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

//Step 2: Creating the Window


HWND hwnd; //Create variable hwnd to store handle of Instance
(process)
hwnd = CreateWindowEx(
WS_EX_APPWINDOW,
g_szClassName,// name of a registered window class
myName,// window caption
WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL ,// window style
CW_USEDEFAULT, CW_USEDEFAULT,//w and y position,
640, 480,// width and height
NULL, // handle to parent window
NULL, // handle to menu
hInstance, // application instance
NULL);// window creation data
if(hwnd==0) //If window creation failed
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);//Show window identified by hwnd using
//parameter passed by the last parameter in WinMain()(nCmdShow)
UpdateWindow(hwnd);//then update it to ensure that it has properly
//redrawn itself on the screen

//Step 3: The Message Loop - This is the heart


MSG Msg; //Create MSG structure to store data for windows message
// Keep pumping messages--they end up in our Window Procedure
while(GetMessage(&Msg, NULL, 0, 0) > 0)//while (message from
// application's message queue)>0)
{
TranslateMessage(&Msg);//do some additional processing on
//keyboard events like generating WM_CHAR messages to
//go along with WM_KEYDOWN messages
DispatchMessage(&Msg);//sends the message out to the
//windows procedure
}
return Msg.wParam;

You may have noticed that the menu_one.exe file and menu_two load an external file for the
icon, the former declares the file name in menu_one.rc as a declaration and the latter is in the
menu_two.cpp in the program code.
With the former the icon cannot be change dynamically by program execution though you
can change it externally by changing icon file with a different image data.
With the later you can modify the program such that the icon file can be selected.
QUESTIONS
1. You may want to write build a window with menu that can be loaded or unload as and
when you need it. Which of the following method will you use?
a) using resource script
b) using child process
2. Base on the above program, give the reason why the following statements will
compiles with no errors but will have not never and cannot find ―case
ID_FILE_EXIT:‖ and ―case ID_STUFF_GO:‖ to be TRUE.
switch(Message)
{
case WM_CLOSE:
switch(LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_STUFF_GO:
break;
}
}

3. The AppendMenu() statements executes a using child process or resource script?


4. Show how you would create a new sub-menu under under the File menu named as
Load/Unload Go!. The menu when selected will toggle to load or unload the Stuff|Go
menu.
5. Show how you would create a new sub-menu under under the File menu named as
Change Icon. The menu when selected will rotate the icon image out of two different
icon file.
6. Modify the program such that after Selecting File|Exit, the following display will
generated:

If [Yes] is selected, the program will exit. If [No] is selected, program will return to
application window.
15. Dialogs Box Using Resource
Example: FirstDialog

There's hardly a windows


program out there that doesn't
use dialog boxes. Just go File ->
Open in any text editor or any
other kind of editor for that
matter and voila, you are
presented with a dialog box,
one that probably allows you to
select a file to be opened.

Dialogs aren't limited to the standard open file ones, they can look like and do whatever you
choose. The attractive point of dialogs is that they provide a quick way to arrange and create
a GUI (Graphic User Interface) and even some default processing, cutting down on the
amount of code you must write.

One thing to remember is that dialogs are just windows. The difference between a dialog
and a "normal" window is that the system does some additional default processing for
dialogs, such as creating and initialising controls, and handling tab order. Nearly all APIs that
are applicable to "normal" windows will work just as well on dialogs, and vice versa!

The first step is to create the dialog resource. As with any resource, how you do this will
depend on your compiler/IDE. Here I will show you the plain text of the dialog in the .rc file
and let you incorporate it into your project.

IDD_ABOUT DIALOG DISCARDABLE 0, 0, 239, 66


STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "My About Box"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "&OK",IDOK,174,18,50,14
PUSHBUTTON "&Cancel",IDCANCEL,174,35,50,14
GROUPBOX "About this program...",IDC_STATIC,7,7,225,52
CTEXT "An example program showing how to use Dialog
Boxes\r\n\r\nby theForger",
IDC_ABOUT,16,18,144,33
END

On this first line, IDD_ABOUTDLG is the id of the resource. DIALOG is the resource type, and the
four number are the Left, Top,8001 Width and Height co-ordinates. These ARE NOT
PIXELS, they are in Dialog Units, which are based on the size of the font used by the system
(and chosen by the user). If you have a large font selected, the dialog will be large, if you use
a smaller font, the dialog will be that much smaller. This is important as it makes sure that all
of the controls are the proper size to display their text in the current font. You can convert
dialog units to pixels at runtime using MapDialogRect(). DISCARDABLE tells the system it
may swap the resource memory to disk when it's not being used in order to conserve system
resources (essentially pointless).

The second line starts with STYLE and follows with the window styles that will be used to
create the dialog. These should be explained under CreateWindow() in your help files. In
order to use the predefined constants you may need to add #include "windows.h" to your
.rc file, or in the case of VC++, winres.h or afxres.h will do. If you use the resource editor
these files will certainly be included automatically if needed.

The CAPTION line should be self explanatory.

The FONT line specifies the size and name of the font you wish to use for this dialog box. This
might not end up exactly the same on each computer as different people will have different
fonts and may have specified different font sizes. You usually don't need to worry about that
though.

Now we have the list of controls to create on the dialog

DEFPUSHBUTTON "&OK",IDOK,174,18,50,14
Here's the line for the OK button. The & in this case like with menus underlines the next
letter "O", so that by pressing Alt+O the user can activate this control (part of the default
processing I mentioned). IDOK is the control identifier. IDOK is pre-defined so we don't need
to #define it ourselves. The four numbers at the end are the left, top, width and height, all in
dialog units.

This information should be purely academic, as you almost always use a resource editor to
create dialogs, but knowing how to do it from text is sometimes necessary, expecially if you
have no visual editor.

Two of the controls have an ID of IDC_STATIC (which is -1), this is used to indicate we never
need to access them, so they have no need of an identifier. However it doesn't hurt to give
them an ID and your resource editor might do so automatically.

The "\r\n" in the text of the static control is a CR-LF pair, the way windows represents a
new line.

So! Having added that to your .rc file we need to write a Dialog Procedure to process
message for this box. Don't worry this is nothing new, it's practicly the same as our main
Window Procedure (but not exactly).

BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM


lParam)
{
switch(Message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
EndDialog(hwnd, IDOK);
break;
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
There are a few important differences between a dialog procedure and window procedure.
One is that you DO NOT call DefWindowProc() for message you don't handle. With dialogs
this is done automatically for you (and will really screw things up if you do it).

Secondly, in general you return FALSE for messages you don't process, and TRUE for
messages you do process, UNLESS the message specifies you return something else. Note
that this is what we do above, the default is to do nothing and return FALSE, while messages
we do handle break the switch() and return TRUE.

Thirdy, You do not call DestroyWindow() to close a dialog, you call EndDialog(). The
second parameter is the value that is returned to whatever code called DialogBox().

Finally, instead of handling WM_CREATE, you handle WM_INITDIALOG to do any processing


that needs to be done before the dialog appears, and then return TRUE to have the keyboard
focus set to the default control. (You can actually handle WM_CREATE as well, but it is sent
BEFORE any of the controls have been created, so you can't access them. In WM_INITDIALOG
the controls have already been created).

Enough chit-chat lets create it....

case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_HELP_ABOUT:
{
int ret = DialogBox(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDlgProc);
if(ret == IDOK){
MessageBox(hwnd, "Dialog exited with IDOK.", "Notice",
MB_OK | MB_ICONINFORMATION);
}
else if(ret == IDCANCEL){
MessageBox(hwnd, "Dialog exited with IDCANCEL.", "Notice",
MB_OK | MB_ICONINFORMATION);
}
else if(ret == -1){
MessageBox(hwnd, "Dialog failed!", "Error",
MB_OK | MB_ICONINFORMATION);
}
}
break;
// Other menu commands...
}
break;

This is the code I used to create my about box, you can probably guess that this is to be
merged into your WM_COMMAND handler, if you aren't clear on this aspect, you might want to
review the section on menus. ID_HELP_ABOUT is the identifier of my Help -> About menu
item.

Since we want the menu on our main window to create the dialog, we obviously want to put
this code in the WndProc() of our main window, not the dialog proc.

Now I stored the return value from the call to DialogBox(), this is just so you can observe
the effects of pressing the two buttons, hitting Esc, Enter etc... from inside the dialog. It also
illustrates how to use the return value from a dialog box to check for success, failure, a users
choice, or whatever other information you choose to send back to the caller from the Dialog
Procedure.

DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUT), hwnd,


AboutDlgProc);
This is the only important part, and you can choose to put it wherever in your code that you
want the dialog to come up. IDD_ABOUT is the id of the dialog resource. hwnd is the handle to
the parent window of the dialog. AboutDlgProc() is of course the dialog procedure to use to
control the dialog.

That's it! Sit IDD_UBU, sit.

A perticularly astute reader might eventually wonder, if DialogBox() doesn't return untill the
dialog closes we can't process messages while it's up, so how does it work? Well the nifty
thing about DialogBox() is that it has it's own message loop, so while the dialog is
displayed, our message loop is out of the picture and the default loop is handled by windows.
This loop also takes care of fun things like moving the keyboard focus from control to control
when you press Tab.

Another effect of using DialogBox is that your main window is disabled untill the dialog is
dismissed. Sometimes this is what we want, and sometimes it isn't, such as when we want to
use a dialog as a floating toolbar. In this case we want to be able to interact with both out
dialog and our main window, and this will be the focus of the next section.
This is the full program:

FirstDialog.cpp:

#include <windows.h>
#include "Resource.h"
const char g_szClassName[] = "myWindowClass";

BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM


lParam)
{
switch(Message)
{
case WM_INITDIALOG:

return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
EndDialog(hwnd, IDOK);
break;
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_HELP_ABOUT:
{
int ret = DialogBox(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_ABOUT), hwnd,
AboutDlgProc);
if(ret == IDOK){
MessageBox(hwnd,
"Dialog exited with IDOK.", "Notice",
MB_OK | MB_ICONINFORMATION);
}
else if(ret == IDCANCEL){
MessageBox(hwnd,
"Dialog exited with IDCANCEL.",
"Notice",
MB_OK | MB_ICONINFORMATION);
}
else if(ret == -1){
MessageBox(hwnd, "Dialog failed!",
"Error",
MB_OK | MB_ICONINFORMATION);
}
}
case ID_FILE_EXIT:
DestroyWindow(hwnd);
break;
case ID_STUFF_GO:
MessageBox(hwnd, "Stuff to go",
"Second Menu", MB_OK );
break;
}
break;
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;
HICON hMyIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDI_MYICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU);;
wc.lpszClassName = g_szClassName;
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 16, 16, 0);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
NULL, NULL, hInstance, NULL);

if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

while(GetMessage(&Msg, NULL, 0, 0) > 0)


{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}

FirstDialog.rc:

#include "Resource.h"
#include <windows.h>
IDI_MYICON ICON "my_icon.ico"
IDR_MYMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_FILE_EXIT
END
POPUP "&Stuff"
BEGIN
MENUITEM "&Go", ID_STUFF_GO
MENUITEM "G&o somewhere else", 0, GRAYED
END
POPUP "Help"
BEGIN
MENUITEM "About", ID_HELP_ABOUT
END
END
IDD_ABOUT DIALOG DISCARDABLE 0, 0, 239, 66
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "My About Box"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "&OK",IDOK,174,18,50,14
PUSHBUTTON "&Cancel",IDCANCEL,174,35,50,14
GROUPBOX "About this program...",IDC_STATIC,7,7,225,52
CTEXT "An example program showing how to use Dialog
Boxes\r\n\r\nto the SEL 4263 Class",
IDD_ABOUT,16,18,144,33
END

Resource.h:

#define IDR_MYMENU 101


#define IDI_MYICON 201

#define ID_FILE_EXIT 9001


#define ID_STUFF_GO 9002
//#define IDOK 9003 //defined in windows.h
//#define IDCANCEL 9005 //defined in windows.h
#define ID_HELP_ABOUT 9005
#define IDC_STATIC 9100
#define IDD_ABOUT 9200
//#define DS_MODALFRAME 8001 //defined in windows.h
16. Modeless Dialogs Using
Resource
Example: dlg_two

Now we take a look at CreateDialog(),


DialogBox()'s sister function. The difference is
that while DialogBox() implements it's own
message loop and does not return untill the dialog is
closed, CreateDialog() acts more like a window
created with CreateWindowEx() in that it returns
immediately and depends on your message loop to
pump the messages as it does for your main
window. This is termed Modeless, whereas
DialogBox() creates Modal dialogs.

You can create the dialog resource just like you did for the last dialog example, you might
also want to set the "Tool window" extended style to give it's title bar the typical smaller
caption of toolbars. The dialog resource I created follows:

IDD_TOOLBAR DIALOGEX 0, 0, 98, 52


STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
EXSTYLE WS_EX_TOOLWINDOW
CAPTION "My Dialog Toolbar"
FONT 8, "MS Sans Serif"
BEGIN
PUSHBUTTON "&Press This Button",IDC_PRESS,7,7,84,14
PUSHBUTTON "&Or This One",IDC_OTHER,7,31,84,14
END

You may notice that the resource editor has replaced DIALOG with DIALOGEX indicating we
want to set an EXSTYLE on our dialog.

Next we want to create the dialog when our program runs, I want the dialog visible right
away so we do this in WM_CREATE. We also want to declare a global variable to hold the
window handle returned from CreateDialog() so that we can use it later. DialogBox()
didn't return a handle to us since when DialogBox() returns the window has been destroyed.

In the global scope


HWND g_hToolbar = NULL;

In Windows Procesure
case WM_CREATE:
g_hToolbar = CreateDialog(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_TOOLBAR),
hwnd, ToolDlgProc);
if(g_hToolbar != NULL)
{
ShowWindow(g_hToolbar, SW_SHOW);
}
else
{
MessageBox(hwnd, "CreateDialog returned NULL", "Warning!",
MB_OK | MB_ICONINFORMATION);
}
break;

We check the return value, which is ALWAYS a good idea, and if it's valid (not NULL) we
show the window with ShowWindow(), with DialogBox() this isn't necessary since the
system calls ShowWindow() for us.

Now we need a dialog procedure for our toolbar.

BOOL CALLBACK ToolDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM


lParam)
{
switch(Message)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_PRESS:
MessageBox(hwnd, "Hi!", "This is a message",
MB_OK | MB_ICONEXCLAMATION);
break;
case IDC_OTHER:
MessageBox(hwnd, "Bye!", "This is also a message",
MB_OK | MB_ICONEXCLAMATION);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}

Most of the same message handling rules apply to dialogs created with CreateDialog() as
with DialogBox(), don't call DefWindowProc(), return FALSE for messages you don't handle
and TRUE for those you do.

One change is that we don't call EndDialog() for modeless dialogs, we can use
DestroyWindow() just like for regular windows. In this case I destroy the dialog when the
main window is destroyed. In the main window's WndProc()...

case WM_DESTROY:
DestroyWindow(g_hToolbar);
PostQuitMessage(0);
break;

Last but not least, we want to be able to display and hide our toolbar whenever we choose so
I've added two commands to my menu to do this, and handled them so:
In Windows Procedure add,
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_DIALOG_SHOW:
ShowWindow(g_hToolbar, SW_SHOW);
break;
case ID_DIALOG_HIDE:
ShowWindow(g_hToolbar, SW_HIDE);
break;
//... other command handlers
}
break;

You should be able to create your own menu using the resource editor or manually, but if not
(as always) take a look at the example project dlg_two provided with the tutorial.

Now when you run the program, you should be able to access both the dialog window, and
main window at the same time.

If you've run the program at this point and tried tabbing between the two buttons, you have
probably noticed it doesn't work, neither does hitting Alt-P or Alt-O to activate the buttons.
Why not? Whereas DialogBox() implements it's own message loop and handles these events
by default, CreateDialog() does not. We can do it ourselves though, by calling
IsDialogMessage() in our message loop which will do the default processing for us.

while(GetMessage(&Msg, NULL, 0, 0))


{
if(!IsDialogMessage(g_hToolbar, &Msg))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}

Here we first pass the message to IsDialogMessage(), if the message is destined for our
toolbar (indicated by the window handle we pass in) the system will perform the default
processing and return TRUE. Is this case the message has already been handled so we don't
want to call TranslateMessage() or DispatchMessage(). If the message is for another
window we process as usual.

It's also worth noting that IsDialogMessage() can also be used with windows that aren't
dialogs in order to to give them dialog-like behaviour. Remember, a dialog is a window, and
most (if not all) dialog APIs will work on any window.
And that is pretty much all there is to modeless dialogs! One issue that may arise is if you
have more than one toolbar... what do you do? Well one possible solution is to have a list
(either an array, an STL std::list, or similar) and loop through it in your message loop
passing each handle to IsDialogMessage() until the right one is found, and if none, do the
regular processing. This is a generic programming problem, not one that is Win32 related,
and is left as an excersize to the reader.

17. Windows Controls


A control is a child window that an application uses in conjunction with another window to
enable user interaction. Controls are most often used within dialog boxes, but they can also
be used in other windows. Controls within dialog boxes provide the user with a way to type
text, choose options, and initiate actions. Controls in other windows provide a variety of
services, such as letting the user choose commands, view status, and view and edit text.

Of course, you are not limited to using only the predefined classes. By calling
RegisterClassEx your program can create its own window classes. However, for most
purposes, using one of the predefined classes is best when creating some sort of control,
especially if one already exists.

Typically, a program uses these classes to create controls. Each class also has a unique set of
window style flags that only apply to it. These unique styles typically control properties
specific to that type of control. Below is a list of some of the window classes predefined in
the Windows API. Note that this list does not (yet) include all classes defined in the Windows
API. Information that applies to all window classes in general appears first in the list.

The common controls overview documentation describes the common controls delivered in
Microsoft Windows and the programming elements used to create and manipulate them.

The following table lists the Windows controls.

Control Description

Animation A window that displays an Audio-Video Interleaved (AVI) clip.

Button Notifies the parent window when the user selects the control.

A combination of a list box and an edit control, enabling the user to select
Combo Box
or add items.

An extension of the combo box control that provides native support for
ComboBoxEx
item images.

Date and Time A simple and intuitive interface through which to exchange date and time
Picker information with a user.
A type of list box that enables the user to drag items from one position to
Drag List Box
another.

Edit A window within the user can view and edit text.

A scroll bar with a more customizable appearance than standard scroll


Flat Scroll Bar
bars.

A window that is usually positioned above columns of text or numbers. It


Header
contains a title for each column, and it can be divided into parts.

A window that enables the user to enter a combination of keystrokes to be


Hot Key
used as a hot key.

A collection of images of the same size, each of which can be referred to


Image Lists
by its index.

A window in which the user can enter an Internet Protocol (IP) address in
IP Address
an easily understood format.

List Box A simple list from which the user can select one or more items.

List-View A list box that provides several ways to arrange and display the items.

Month Calendar A calendar that enables the user to select a date or dates.

A set of arrow buttons that enable the contents of a control window to be


Pager
scrolled.

Progress Bar An animated control that indicates the progress of a lengthy operation.

A dialog box that enables the user to view and edit the properties of an
Property Sheet
item. Pages may be viewed as tabs, or in succession as a wizard.

A container for child windows. An application assigns child windows,


ReBar
which are often other controls, to a rebar control band.

A window in which the user can view and edit text with character and
Rich Edit
paragraph formatting. It can also contain embedded COM objects.

A control that enables the user to choose the direction and distance to
Scroll Bar
scroll information in a related window.
Static Non-interactive text, including labels for other controls.

A horizontal window at the bottom of a parent window in which an


Status Bar
application can display various kinds of status information.

SysLink A hypertext link.

A selectable page, analogous to a divider in a notebook. By using a tab


Tab control, an application can define multiple pages for the same area of a
window or dialog box.

A more powerful alternative to simple message boxes, incorporating


Task Dialog elements such as custom buttons, radio buttons, hyperlinks, and progress
bars.

Toolbar A window that contains one or more buttons and possibly other controls.

A message that appears automatically when the mouse pointer hovers over
Tooltip
a tool.

Trackbar A slider with optional tick marks, used to set a value within a range.

A hierarchical list of items, such as the headings in a document or the files


Tree-View
and directories on a disk.

A pair of arrow buttons that the user can click to increment or decrement a
Up-Down value, such as a scroll position or a number displayed in a companion
control.

Class-Specific Information

Button Control Window Class


The button control's window class describes any type of button control. This includes
command buttons, check boxes, and radio boxes. The button class style assigned to a button
control determines what type of button it is.

The button control's window class is registered automatically when Windows starts. The
name of the class is "BUTTON".

Styles

BS_3STATE
The button is a check box which has three states: checked, grayed, and cleared.
BS_AUTO3STATE
The button is a check box which has three states: checked, grayed, and cleared. The button
automatically changes its state when the user selects it.
BS_AUTOCHECKBOX
The button is a check box whose state toggles when the user selects it.
BS_AUTORADIOBUTTON
The button is a radio button whose state automatically changes to selected (and the state of all
other radio buttons in the group to unselected) when the user selects it.
BS_BITMAP
The button displays a bitmap.
BS_BOTTOM
The button's text appears at the bottom of the button rectangle.
BS_CENTER
The button's text appears centered horizontally within the button rectangle.
BS_CHECKBOX
The button is a check box.
BS_DEFPUSHBUTTON
The button is the default push button in a dialog box, having a heavy black border.
BS_GROUPBOX
The window is a rectangular grouping frame in which other controls can be grouped.
BS_FLAT
The button is flat, not using the default 3D shading.
BS_ICON
The button displays an icon.
BS_LEFT
The text in the button rectangle is left-justified.
BS_LEFTTEXT, BS_RIGHTBUTTON
The radio or check box's text appears to the left of the button instead of to the right.
BS_MULTILINE
The button's text is wrapped across multiple lines if it cannot fit on a single line.
BS_NOTIFY
The button sends focus notification messages to its parent window.
BS_OWNERDRAW
The owner of the window is responsible for manually drawing it whenever it needs to be
redrawn. This window style cannot be combined with any other button styles.
BS_PUSHBUTTON
The button is a push button.
BS_PUSHLIKE
The radio or check button takes on the appearance of a push button. A checked state makes
the button look depressed; a cleared state makes the button look normal.
BS_RADIOBUTTON
The button is a radio button.
BS_RIGHT
The text in the button rectangle is right-justified.
BS_TEXT
The button displays text.
BS_TOP
The text appears at the top of the button rectangle.
BS_USERBUTTON
Obsolete; use BS_OWNERDRAW instead.
BS_VCENTER
The text appears centered vertically within the button rectangle.

Constant Definitions
Const BS_3STATE = &H5
Const BS_AUTO3STATE = &H6
Const BS_AUTOCHECKBOX = &H3
Const BS_AUTORADIOBOX = &H9
Const BS_BITMAP = &H80
Const BS_BOTTOM = &H800
Const BS_CENTER = &H300
Const BS_CHECKBOX = &H2
Const BS_DEFPUSHBUTTON = &H1
Const BS_FLAT = &H8000
Const BS_GROUPBOX = &H7
Const BS_ICON = &H40
Const BS_LEFT = &H100
Const BS_LEFTTEXT = &H20
Const BS_MULTILINE = &H2000
Const BS_NOTIFY = &H4000
Const BS_OWNERDRAW = &HB
Const BS_PUSHBUTTON = &H0
Const BS_PUSHLIKE = &H1000
Const BS_RADIOBUTTON = &H4
Const BS_RIGHT = &H200
Const BS_RIGHTBUTTON = &H20
Const BS_TEXT = &H0
Const BS_TOP = &H400
Const BS_USERBUTTON = &H8
Const BS_VCENTER = &HC00

Combo Box Control Window Class


The combo box control's window class describes an ordinary combo box control.

The button control's window class is registered automatically when Windows starts. The
name of the class is "COMBOBOX".

Styles

CBS_AUTOHSCROLL
Automatically scroll text to the right when the user types a character at the end of the line.
CBS_DISABLENOSCROLL
Show a disable the vertical scroll bar in the drop-down list box if it does not contain enough
items to scroll. If this flag is not specified, such a scroll bar is not displayed.
CBS_DROPDOWN
Display the list box whenever the user clicks the drop-down button.
CBS_DROPDOWNLIST
Display the list box whenever the user click the drop-down button, and do not allow the user
to change the combo box's selection if the list is not dropped down.
CBS_HASSTRINGS
The combo box is drawn manually by the application and contains strings. Either
CBS_OWNERDRAWFICED or CBS_OWNERDRAWVARIABLE must also be specified.
CBS_LOWERCASE
Convert all text in the combo box to lowercase letters.
CBS_NOINTEGRALHEIGHT
Force the combo box to be exactly the size specified by the application, instead of allowing
the operating system to slightly resize it to prevent displaying partial selections.
CBS_OEMCONVERT
Ensure that text in the combo box can readily be converted into the OEM character set.
CBS_OWNERDRAWFIXED
The owner of the combo box is fully responsible for drawing it manually, and all of the items
in the combo box have the same height.
CBS_OWNERDRAWVARIABLE
The owner of the combo box is full responsible for drawing it manually, and the heights of
the items in the combo box have can be different.
CBS_SIMPLE
Display the list box portion of the combo box at all times.
CBS_SORT
Automatically sort the strings added to the combo box.
CBS_UPPERCASE
Convert all text in the combo box to uppercase letters.

Constant Definitions
Const CBS_AUTOHSCROLL = &H40
Const CBS_DISABLENOSCROLL = &H800
Const CBS_DROPDOWN = &H2
Const CBS_DROPDOWNLIST = &H3
Const CBS_HASSTRINGS = &H200
Const CBS_LOWERCASE = &H4000
Const CBS_NOINTEGRALHEIGHT = &H400
Const CBS_OEMCONVERT = &H80
Const CBS_OWNERDRAWFIXED = &H10
Const CBS_OWNERDRAWVARIABLE = &H20
Const CBS_SIMPLE = &H1
Const CBS_SORT = &H100
Const CBS_UPPERCASE = &H2000

Edit Control Window Class


The edit control's window class descibes a regular text edit box.

The button control's window class is registered automatically when Windows starts. The
name of the class is "EDIT".

Styles

ES_AUTOHSCROLL
Automatically scroll the text to the right when the user types a character at the end of the line.
When the user pressed ENTER, scroll the text all the way back to the left.
ES_AUTOVSCROLL
Automatically scroll the text back up when the user presses ENTER on the last line.
ES_CENTER
Center the text horizontally.
ES_LEFT
Left-align the text.
ES_LOWERCASE
Convert all the characters to lowercase as they are typed.
ES_MULTILINE
The edit control displays multiple lines of text. If this flag is not specified, it can only display
a single line of text.
ES_NOHIDESEL
Do not hide the selected text in the edit control even if the control loses focus.
ES_NUMBER
Only allow digits to be entered into the edit control.
ES_OEMCONVERT
Ensure that text in the edit control can readily be converted into the OEM character set.
ES_PASSWORD
Display an asterisk for each character typed into the edit control. This cannot be used with
ES_MULTILINE.
ES_READONLY
Do not allow the user to edit the text in the control.
ES_RIGHT
Right-align the text.
ES_UPPERCASE
Convert all the characters to uppercase as they are typed.
ES_WANTRETURN
Insert a carriage return into a multi-line edit control when the user pressed ENTER, instead of
implementing the default behavior of ENTER.

Constant Definitions
Const ES_AUTOHSCROLL = &H80
Const ES_AUTOVSCROLL = &H40
Const ES_CENTER = &H1
Const ES_LEFT = &H0
Const ES_LOWERCASE = &H10
Const ES_MULTILINE = &H4
Const ES_NOHIDESEL = &H100
Const ES_NUMBER = &H2000
Const ES_OEMCONVERT = &H400
Const ES_PASSWORD = &H20
Const ES_READONLY = &H800
Const ES_RIGHT = &H2
Const ES_UPPERCASE = &H8
Const ES_WANTRETURN = &H1000

IP Address Control's Window Class


The IP Address control's window class describes an IP Address control. This control is
similar to an edit control, but it is specialized for entering IP addresses in "dotted quad"
(a.b.c.d) format. The control performs bounds checking on all inputs, preventing the user
from entering something that is not an IP address.

Before creating windows from this class, your program must first register it by calling
InitCommonControlsEx with the appropriate parameters. The name of the class is
"SysIPAddress32".

Styles

None.

List Box Control Window Class


The list box control's window class describes an ordinary list box.

The button control's window class is registered automatically when Windows starts. The
name of the class is "LISTBOX".

Styles

LBS_DISABLENOSCROLL
Show a disable the vertical scroll bar in the list box if it does not contain enough items to
scroll. If this flag is not specified, such a scroll bar is not displayed.
LBS_EXTENDEDSEL
Allow the user to select multiple items.
LBS_HASSTRINGS
The list box is drawn manually by the application and contains strings. Either
LBS_OWNERDRAWFICED or LBS_OWNERDRAWVARIABLE must also be specified.
LBS_MULTICOLUMN
The list box contains multiple columns which are scrolled horizontally.
LBS_MULTIPLESEL
Toggle the selection of a string every time the user clicks or double-clicks it.
LBS_NODATA
Intended for list boxes having more than 1000 items, do not have the operating system handle
any of the entries. LBS_OWNERDRAWFIXED must also be specified, and neither
LBS_SORT nor LBS_HASSTRINGS can be specified.
LBS_NOINTEGRALHEIGHT
Force the list box to be exactly the size specified by the application, instead of allowing the
operating system to slightly resize it to prevent displaying partial selections.
LBS_NOREDRAW
Do not redraw the list box when changes are made.
LBS_NOSEL
Do not allow the user to select items in the list box.
LBS_NOTIFY
Notify the parent window whenever the user clicks or double-clicks a string in the list box.
LBS_OWNERDRAWFIXED
The owner of the list box is fully responsible for drawing it manually, and all of the items in
the list box have the same height.
LBS_OWNERDRAWVARIABLE
The owner of the list box is full responsible for drawing it manually, and the heights of the
items in the list box have can be different.
LBS_SORT
Automatically sort the strings in the list box alphabetically.
LBS_STANDARD
Automatically sort the strings in the list box alphabetically, notify the parent window
whenever the user clicks or double-clicks a string, and display a border around the list box.
LBS_USETABSTOPS
Allow the list box to expand tab characters when drawing its strings.
LBS_WANTKEYBOARDINPUT
Notify the owner of the list box whenever the user types a key while the list box has the
focus.

Constant Definitions
Const LBS_DISABLENOSCROLL = &H1000
Const LBS_EXTENDEDSEL = &H800
Const LBS_HASSTRINGS = &H40
Const LBS_MULTICOLUMN = &H200
Const LBS_MULTIPLESEL = &H8
Const LBS_NODATA = &H2000
Const LBS_NOINTEGRALHEIGHT = &H100
Const LBS_NOREDRAW = &H4
Const LBS_NOSEL = &H4000
Const LBS_NOTIFY = &H1
Const LBS_OWNERDRAWFIXED = &H10
Const LBS_OWNERDRAWVARIABLE = &H20
Const LBS_SORT = &H2
Const LBS_STANDARD = &HA00006
Const LBS_USETABSTOPS = &H80
Const LBS_WANTKEYBOARDINPUT = &H400

Scroll Bar Control Window Class


The scroll bar control's window class describes a standalone scroll bar (i.e., one that is not
part of another control such as an edit control).

The button control's window class is registered automatically when Windows starts. The
name of the class is "SCROLLBAR".

Styles

SBS_BOTTOMALIGM
Align the bottom edge of the scroll bar with the bottom edge of its rectangle, using its default
height. SBS_HORZ must also be specified.
SBS_HORZ
The scroll bar is a horizontal scroll bar.
SBS_LEFTALIGN
Align the left edge of the scroll bar with the left edge of its rectangle, using its default width.
SBS_VERT must also be specified.
SBS_RIGHTALIGN
Align the right edge of the scroll bar with the right edge of its rectangle, using its default
width. SBS_VERT must also be specified.
SBS_SIZEBOX
The scroll bar is a size box.
SBS_SIZEBOXBOTTOMRIGHTALIGN
Align the lower-right corner of the size box with the lower-right corner of its rectangle, using
its default width and height. SBS_SIZEBOX must also be specified.
SBS_SIZEBOXTOPLEFTALIGN
Align the upper-left corner of the size box with the upper-left corner of its rectangle, using its
default width and hieght. SBS_SIZEBOX must also be specified.
SBS_SIZEGRIP
The scroll bar is a size box with a raised edge.
SBS_TOPALIGN
Align the top edge of the scroll bar with the top edge of its rectangle, using its default height.
SBS_HORZ must also be specified.
SBS_VERT
The scroll bar is a vertical scroll bar.

Constant Definitions
Const SBS_BOTTOMALIGN = &H4
Const SBS_HORZ = &H0
Const SBS_LEFTALIGN = &H2
Const SBS_RIGHTALIGN = &H4
Const SBS_SIZEBOX = &H8
Const SBS_SIZEBOXBOTTOMRIGHTALIGN = &H4
Const SBS_SIZEBOXTOPLEFTALIGN = &H2
Const SBS_SIZEGRIP = &H10
Const SBS_TOPALIGN = &H2
Const SBS_VERT = &H1

Static Control Window Class


The static control's window class describes a static control. Static controls are commonly
used as labels for other controls.

The button control's window class is registered automatically when Windows starts. The
name of the class is "STATIC".

Styles

SS_BITMAP
Display the bitmap specified by the static control's text.
SS_BLACKFRAME
Draw a frame around the static control in the same color as a window frame.
SS_BLACKRECT
Fill the static control with the same color as a window frame.
SS_CENTER
Center the text in the static control.
SS_CENTERIMAGE
If the bitmap or icon is smaller than the size of the static control, fill the rest of the control
with whatever color is at the image's upper-left corner.
SS_ENDELLIPSIS
Windows NT, 2000: Replace the end of the string with an ellipsis if it is too long to fit in the
static control.
SS_ENHMETAFILE
Display the enhanced metafile identified by the static control's text. Scale the enhanced
metafile to fit the static control.
SS_ETCHEDFRAME
Draw the frame of the static control using the etched edge style.
SS_ETCHEDHORZ
Draw only the top and bottom edges of the static control using the etched edge style.
SS_ETCHEDVERT
Draw only the left and right edges of the static control using the etched edge style.
SS_GRAYFRAME
Draw a frame around the static control in the same color as the screen background.
SS_GRAYRECT
Fill the static control with the same color as the screen background.
SS_ICON
Display the icon identified by the static control's text. The static control automatically resizes
to the size of the icon.
SS_LEFT
Left-align the text in the static control.
SS_LEFTNOWORDWRAP
Left-align the text in the static control, but do not word wrap.
SS_NOPREFIX
Do not use an amperstand character in the string to identify an accelerator prefix, instead
displaying the amperstands as regular characters.
SS_OWNERDRAW
The owner of the static control is fully responsible for drawing the control.
SS_PATHELLIPSIS
Windows NT, 2000: Replace characters in the middle of a string holding a path with an
ellipsis if it is too long to fit in the static control.
SS_REALSIZEIMAGE
Clip an image or bitmap if it does not fit inside the static control instead of resizing the
control.
SS_RIGHT
Right-align the text in the static control.
SS_RIGHTJUST
Do not move the lower-right corner of the static control when resizing it to accomodate a
bitmap or icon.
SS_SIMPLE
Draw a simple rectangle and display a single line of left-aligned text in the static control.
SS_SUNKEN
Draw a half-sunken border around the static control.
SS_WHITEFRAME
Draw a frame around the static control in the same color as the window background.
SS_WHITERECT
Fill the static control with the same color as the window background.
SS_WORDELLIPSIS
Windows NT, 2000: Truncate text and add ellipses to text which does not fit into the static
control.

Constant Definitions
Const SS_BITMAP = &HE
Const SS_BLACKFRAME = &H7
Const SS_BLACKRECT = &H4
Const SS_CENTER = &H1
Const SS_CENTERIMAGE = &H200
Const SS_ENDELLIPSIS = &H4000
Const SS_ENHMETAFILE = &HF
Const SS_ETCHEDFRAME = &H12
Const SS_ETCHEDHORZ = &H10
Const SS_ETCHEDVERT = &H11
Const SS_GRAYFRAME = &H8
Const SS_GRAYRECT = &H5
Const SS_ICON = &H3
Const SS_LEFT = &H0
Const SS_LEFTNOWORDWRAP = &HC
Const SS_NOPREFIX = &H80
Const SS_NOTIFY = &H100
Const SS_OWNERDRAW = &HD
Const SS_PATHELLIPSIS = &H8000
Const SS_REALSIZEIMAGE = &H800
Const SS_RIGHT = &H2
Const SS_RIGHTJUST = &H400
Const SS_SIMPLE = &HB
Const SS_SUNKEN = &H1000
Const SS_WHITEFRAME = &H9
Const SS_WHITERECT = &H6
Const SS_WORDELLIPSIS = &HC000
18. Styles Common to All Classes
Base Window Styles
The following window styles are shared by all windows, regardless of their class. They
generally describe the window's general appearance, although many of the styles apply best
to non-control windows (particularly overlapped windows).

Styles
WS_BORDER
The window has a thin-line border.
WS_CAPTION
The window has a title bar.
WS_CHILD, WS_CHILDWINDOW
The window is a child window. These windows cannot have menu bars nor
can have the WS_POPUP style.
WS_CLIPCHILDREN
For a parent window, exclude the areas occupied by child windows when
drawing within the window (to avoid drawing on any child windows).
WS_CLIPSIBLINGS
For a child window, exclude the areas occupied by fellow children of the
window's parent when drawing within the window (to avoid drawing on any
sibling windows).
WS_DISABLED
The window is disabled.
WS_DLGFRAME
The window has a border style typical of dialog boxes. These windows
cannot have a title bar.
WS_GROUP
Identifies the first control in a group of controls. Any controls
following this one are assumed to be part of this control's group, until
a control with the WS_GROUP style is encountered.
WS_HSCROLL
The window has a horizontal scroll bar.
WS_MAXIMIZE
The window is maximized.
WS_MAXIMIZEBOX
The window has a maximize button. This cannot be used on windows having
the WS_EX_CONTEXTHELP extended window style. The WS_SYSMENU window style
must also be specified.
WS_MINIMIZE, WS_ICONIC
The window is minimized.
WS_MINIMIZEBOX
The window has a minimize button. This cannot be used on windows having
the WS_EX_CONTEXTHELP extended window style. The WS_SYSMENU window style
must also be specified.
WS_OVERLAPPED, WS_TILED
The window is an overlapped window, which as a title bar and a border.
WS_OVERLAPPEDWINDOW, WS_TILEDWINDOW
The window is an overlapped window, having a title bar, a sizing border,
a title bar, a system menu, and minimize and maximize boxes.
WS_POPUP
The window is a popup window. This cannot be used with the WS_CHILD
window style.
WS_POPUPWINDOW
The window is a popup window, having a thin-line border and system menu.
The window can only be visible if it also has the WS_CAPTION window
style.
WS_SIZEBOX
Same as WS_THICKFRAME.
WS_SYSMENU
The window has a system menu on its title bar. The WS_CAPTION window
style must also be specified.
WS_TABSTOP
The control can press Tab repeatedly to set the focus to this control.
WS_THICKFRAME
The window has a sizing border.
WS_VISIBLE
The window is visible.
WS_VSCROLL
The window has a vertical scroll bar.

Constant Definitions
Const WS_BORDER = &H800000
Const WS_CAPTION = &HC00000
Const WS_CHILD = &H40000000
Const WS_CHILDWINDOW = &H40000000
Const WS_CLIPCHILDREN = &H2000000
Const WS_CLIPSIBLINGS = &H4000000
Const WS_DISABLED = &H8000000
Const WS_DLGFRAME = &H400000
Const WS_GROUP = &H20000
Const WS_HSCROLL = &H100000
Const WS_ICONIC = &H20000000
Const WS_MAXIMIZE = &H1000000
Const WS_MAXIMIZEBOX = &H10000
Const WS_MINIMIZE = &H20000000
Const WS_MINIMIZEBOX = &H20000
Const WS_OVERLAPPED = &H0
Const WS_OVERLAPPEDWINDOW = &HCF0000
Const WS_POPUP = &H80000000
Const WS_POPUPWINDOW = &H80880000
Const WS_SIZEBOX = &H40000
Const WS_SYSMENU = &H80000
Const WS_TABSTOP = &H10000
Const WS_THICKFRAME = &H40000
Const WS_TILED = &H0
Const WS_TILEDWINDOW = &HCF0000
Const WS_VISIBLE = &H10000000
Const WS_VSCROLL = &H200000

Extended Window Styles


Below is a list of the extended window styles. The main difference between these and regular
window styles is that these are specified as a separate value. (For example, a call to
CreateWindowEx takes one parameter for extended window styles and a second parameter
for "regular" window styles.) These styles can be applied to any type of window, although
many of them apply best to overlapped windows.

Extended Styles
WS_EX_ACCEPTFILES
The window accepts files via a drag-and-drop operation.
WS_EX_APPWINDOW
The window also appears on the taskbar whenever it is visible.
WS_EX_CLIENTEDGE
The window has a border with a sunken edge.
WS_EX_CONTEXTHELP
A context-help button appears on the title bar. This cannot be used with the
WS_MAXIMIZEBOX or WS_MINIMIZEBOX regular window styles.
WS_EX_CONTROLPARENT
The window contains children which can be TABbed through.
WS_EX_DLGMODALFRAME
The window has a modal dialog frame (a double border).
WS_EX_LAYERED
Windows 2000: The window is a layered window.
WS_EX_LAYOUTRTL
Windows 2000: The window's coordinate system places the horizontal origin on the right
side, with increasing x values to the left and decreasing x values to the right.
WS_EX_LEFT
The window has generic left-aligned properties.
WS_EX_LEFTSCROLLBAR
If the language supports reading order alignment, position the vertical scroll bar (if any) to
the left of the client area.
WS_EX_LTRREADING
Display window text using left-to-right reading.
WS_EX_MDICHILD
The window is an MDI child window.
WS_EX_NOACTIVATE
Windows 2000: The window is never brought to the foreground as a result of direct user
action.
WS_EX_NOINHERITLAYOUT
Windows 2000: Do not pass the window's layout to its child windows.
WS_EX_NOPARENTNOTRIFY
If the window is a child window, do not notify the parent window when the child window is
created or destroyed.
WS_EX_OVERLAPPEDWINDOW
The window has the border of a typical overlapped window.
WS_EX_PALETTEWINDOW
The window is a topmost toolbar window with a raised edge, normally used for a floating
palette.
WS_EX_RIGHT
The window has generic right-aligned properties, if the language supports reading order
alignment.
WS_EX_RIGHTSCROLLBAR
Display the vertical scroll bar (if any) to the right of the client area.
WS_EX_RTLREADING
Display window text using right-to-left reading, if the language supports reading order
alignment.
WS_EX_STATICEDGE
The window has a three-dimensional border intended for items which do not accept user
input.
WS_EX_TOOLWINDOW
The window is designed to be a floating toolbar window, having a small title bar area.
WS_EX_TOPMOST
The window appears above all non-topmost windows, even if it is not active.
WS_EX_TRANSPARENT
The window appears transparent because its sibling windows below it are drawn first.
WS_EX_WINDOWEDGE
The window has a border with a raised edge.

Constant Definitions

Note: Some of the values of the extended window style flags are not listed. If you know their
values, please e-mail me about them.

Const WS_EX_ACCEPTFILES = &H10


Const WS_EX_APPWINDOW = &H40000
Const WS_EX_CLIENTEDGE = &H200
Const WS_EX_CONTEXTHELP = &H400
Const WS_EX_CONTROLPARENT = &H10000
Const WS_EX_DLGMODALFRAME = &H1
' Const WS_EX_LAYERED = ???
' Const WS_EX_LAYOUTRTL = ???
Const WS_EX_LEFT = &H0
Const WS_EX_LEFTSCROLLBAR = &H4000
Const WS_EX_LTRREADING = &H0
Const WS_EX_MDICHILD = &H40
Const WS_EX_NOACTIVATE = &H8000000
' Const WS_EX_NOINHERITLAYOUT = ???
Const WS_EX_NOPARENTNOTIFY = &H4
Const WS_EX_OVERLAPPEDWINDOW = &H300
Const WS_EX_PALETTEWINDOW = &H188
Const WS_EX_RIGHT = &H1000
Const WS_EX_RIGHTSCROLLBAR = &H0
Const WS_EX_RTLREADING = &H2000
Const WS_EX_STATICEDGE = &H20000
Const WS_EX_TOOLWINDOW = &H80
Const WS_EX_TOPMOST = &H8
Const WS_EX_TRANSPARENT = &H20
Const WS_EX_WINDOWEDGE = &H100

19. App Part 1: Creating controls at


runtime
Example: app_one
Controls are basic building blocks of a windows application. Controls are called widgets
in UNIX environment.

Static control
The static control displays text and graphics. The static control cannot be selected. It cannot
have keyboard focus.
#include <windows.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,


LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Static Control" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Static control"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 330, 270, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam )
{
static char *lyrics = TEXT("Bring what I said it dont mean shit
now\n\
Bring the presents might as well throw em out\n\
Bring all those kisses, they didn't mean jack\n\
Bring you, you hoe, I dont want you back\n\
\n\
You thought, you could\n\
Keep this shit from me, yeah\n\
Ya burnt bitch, I heard the story\n\
Ya played me, ya even gave him head\n\
Now ya askin for me back\n\
Ya just another act, look elsewhere\n\
Cuz ya done with me\n\
");

switch(msg)
{
case WM_CREATE:
{
CreateWindow(TEXT("STATIC"), lyrics,
WS_CHILD | WS_VISIBLE | SS_LEFT,
20, 20, 300, 230,
hwnd, (HMENU) 1, NULL, NULL);
return 0;
}

case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
CreateWindow(TEXT("STATIC"), lyrics,
WS_CHILD | WS_VISIBLE | SS_LEFT,
20, 20, 300, 230,
hwnd, (HMENU) 1, NULL, NULL);
Here we create the static control. We display text. It is aligned to the
left.

Static control

Button
A button is a simple control. It has a text label. It is used to trigger an action.
#include <windows.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,


LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Buttons" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Buttons"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
150, 150, 230, 150, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam )
{

switch(msg)
{
case WM_CREATE:
{
CreateWindow(TEXT("button"), TEXT("Beep"),
WS_VISIBLE | WS_CHILD ,
20, 50, 80, 25,
hwnd, (HMENU) 1, NULL, NULL);

CreateWindow(TEXT("button"), TEXT("Quit"),
WS_VISIBLE | WS_CHILD ,
120, 50, 80, 25,
hwnd, (HMENU) 2, NULL, NULL);
break;
}

case WM_COMMAND:
{
if (LOWORD(wParam) == 1) {
Beep(40, 50);
}

if (LOWORD(wParam) == 2) {
PostQuitMessage(0);
}

break;
}

case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example we have created two buttons. On button will beep. The
other one will close the window.
case WM_COMMAND:
{
if (LOWORD(wParam) == 1) {
Beep(40, 50);
}
if (LOWORD(wParam) == 2) {
PostQuitMessage(0);
}
break;
}
The control id is in the LOWORD of the wParam. Depending on the control id, we call the
Beep() function or the PostQuitMessage() function.

Button controls

Check box
A check box control is a box that you can click to turn an option on or off.
#include <windows.h>

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

static char *title = TEXT("Check Box");

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,


LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Check Box" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, title,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
150, 150, 230, 150, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam )
{

switch(msg)
{
case WM_CREATE:
{
CreateWindow(TEXT("button"), TEXT("Show Title"),
WS_VISIBLE | WS_CHILD | BS_CHECKBOX,
20, 20, 185, 35,
hwnd, (HMENU) 1, ((LPCREATESTRUCT)lParam)-
>hInstance, NULL);
CheckDlgButton(hwnd, 1, BST_CHECKED);
break;
}

case WM_COMMAND:
{
BOOL checked = IsDlgButtonChecked(hwnd, 1);
if (checked) {
CheckDlgButton(hwnd, 1, BST_UNCHECKED);
SetWindowText(hwnd, TEXT(""));
} else {
CheckDlgButton(hwnd, 1, BST_CHECKED);
SetWindowText(hwnd, title);
}
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example, we show or hide the window title depending on the state of the check box.
On windows, check box is a special kind of a button. We create a check box, if we set a
specific style to the button class. In our example, it is BS_CHECKBOX.
BOOL checked = IsDlgButtonChecked(hwnd, 1);
We determine the state of the check box using the IsDlgButtonChecked() function.
CheckDlgButton(hwnd, 1, BST_UNCHECKED);
We check and uncheck the check box using the CheckDlgButton() function.
SetWindowText(hwnd, TEXT(""));
The SetWindowText() function sets the title of the window.

Edit Control
Edit control is a rectangular child window. Edit control is used to enter and edit text. It can
be single line or multiline.
#include <windows.h>

#define ID_EDIT 1
#define ID_BUTTON 2

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM


lParam)
{

static HWND hwndEdit;


static HWND hwndButton;
static int len;
static TCHAR text[30];

switch(msg)
{
case WM_CREATE:
hwndEdit = CreateWindow(TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE |
WS_BORDER,
50, 50, 150, 20, hwnd, (HMENU) ID_EDIT,
NULL, NULL);

hwndButton = CreateWindow(
TEXT("button"), TEXT("Set Title"),
WS_VISIBLE | WS_CHILD,
50, 100, 80, 25,
hwnd, (HMENU) ID_BUTTON, NULL, NULL);

break;

case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
len = GetWindowTextLength(hwndEdit) + 1;
GetWindowText(hwndEdit, text, len);
SetWindowText(hwnd, text);
}
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,


LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Edit Control" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Edit control"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
220, 220, 280, 200, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}

In our example, we have an edit control and a button. We can put some text into the edit
control. If we click on the button, the entered text will be displayed in the titlebar of the
main window.
if (HIWORD(wParam) == BN_CLICKED) {
len = GetWindowTextLength(hwndEdit) + 1;
GetWindowText(hwndEdit, text, len);
SetWindowText(hwnd, text);
}
The GetWindowTextLength() returns the text length entered. Notice, that we add 1 to the
length. This is to include the zero terminator. Try to omit it and see, what happens. The
GetWindowText() receives the text from the edit control. We use hwndEdit as an id. The
SetWindowText() sets the text for the window. In this context, it is a title of the main
window.

Figure: Edit control

Radio buttons and GroupBox


Here we introduce two controls. A group box is a rectangle that surrounds a set of controls.
These are often radio buttons. A group box has a label, that describes the control. The
purpose of this control is to group controls, that are somehow related. A radio button is a
special kind of button, that can be selected by the user, but not cleared. It allows the user to
select a single exclusive choice from a group of options.

#include <windows.h>

#define ID_BLUE 1
#define ID_YELLOW 2
#define ID_ORANGE 3

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

HINSTANCE g_hinst;
COLORREF g_color;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT("GroupBox");
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);

g_hinst = hInstance;

RegisterClass(&wc);
hwnd = CreateWindow(wc.lpszClassName, TEXT("GroupBox"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 170, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam )
{
HDC hdc;
PAINTSTRUCT ps;
HBRUSH hBrush, holdBrush;
HPEN hPen, holdPen;

switch(msg)
{
case WM_CREATE:
CreateWindow(TEXT("button"), TEXT("Choose Color"),
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL);
CreateWindow(TEXT("button"), TEXT("Blue"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 30, 100, 30, hwnd, (HMENU)ID_BLUE , g_hinst, NULL);
CreateWindow(TEXT("button"), TEXT("Yellow"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 55, 100, 30, hwnd, (HMENU)ID_YELLOW , g_hinst,
NULL);
CreateWindow(TEXT("button"), TEXT("Orange"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 80, 100, 30, hwnd, (HMENU)ID_ORANGE , g_hinst,
NULL);

break;

case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case ID_BLUE:
g_color = RGB(0, 76, 255);
break;
case ID_YELLOW:
g_color = RGB(255, 255, 0);
break;
case ID_ORANGE:
g_color = RGB(255, 123, 0);
break;
}
InvalidateRect(hwnd, NULL, TRUE);
}
break;

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hBrush = CreateSolidBrush(g_color);
hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
holdPen = SelectObject(hdc, hPen);
holdBrush = (HBRUSH) SelectObject(hdc, hBrush);

Rectangle(hdc, 160, 20, 260, 120);

SelectObject(hdc, holdBrush);
SelectObject(hdc, holdPen);
DeleteObject(hPen);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

In our example, we have a group box with three radio buttons. By clicking on the radio
button, we select a background color for the rectangle on the right.

CreateWindow(TEXT("button"), TEXT("Choose Color"),


WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL);

A group box is a special kind of a button with BS_GROUPBOX style.

CreateWindow(TEXT("button"), TEXT("Blue"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 30, 100, 30, hwnd, (HMENU)ID_BLUE , g_hinst, NULL);

A radiobutton is also only a special kind of a button with BS_AUTORADIOBUTTON style.

case ID_BLUE:
g_color = RGB(0, 76, 255);
break;

If we click on the radio button, we fill a global variable with a selected color. This variable
will be used to create a brush, that will fill the rectangle.

InvalidateRect(hwnd, NULL, TRUE);

We invalidate the rectangle (in this case whole window), which will cause the client area to
be redrawn. This will launch a WM_PAINT message. During the WM_PAINT message, we
draw the rectangle. Drawing is explained in GDI chapter in more detail.
Figure: GroupBox, Radio boxes

ComboBox
A combo box is a combination of an edit box or static text and a list. A combo box is used
when we need to select an item from a list of available options.

#include <windows.h>

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

HINSTANCE g_hinst;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT("Application");
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);

g_hinst = hInstance;

RegisterClass(&wc);
hwnd = CreateWindow(wc.lpszClassName, TEXT("Combo Box"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 270, 170, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam )
{
static HWND hwndCombo, hwndStatic;
const TCHAR *items[] = { TEXT("FreeBSD"), TEXT("OpenBSD"),
TEXT("Ubuntu"), TEXT("Solaris") };
int i;
LRESULT sel = 0;

switch(msg)
{
case WM_CREATE:
hwndCombo = CreateWindow(TEXT("combobox"), NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
10, 10, 120, 110, hwnd, NULL, g_hinst, NULL);

CreateWindow(TEXT("button"), TEXT("Drop down"),


WS_CHILD | WS_VISIBLE,
150, 10, 90, 25, hwnd, (HMENU)1, g_hinst, NULL);

hwndStatic = CreateWindow(TEXT("static"), TEXT(""),


WS_CHILD | WS_VISIBLE,
150, 80, 90, 25, hwnd, NULL, g_hinst, NULL);

for ( i = 0; i < 4; i++ ) {


SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)
items[i]);
}

break;

case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE,
0);
}

if ( HIWORD(wParam) == CBN_SELCHANGE) {
sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
SetWindowText(hwndStatic, items[sel]);
SetFocus(hwnd);
}

break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

In our example, we put three controls on the window. A combo box, a button and a static text.
The static text displays the currently selected item from the combo box. It is used to
demonstrate the CBN_SELCHANGE combo box message. The button programatically opens
the combo box.

hwndCombo = CreateWindow(TEXT("combobox"), NULL,


WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
10, 10, 120, 110, hwnd, NULL, g_hinst, NULL);
To create a combo box, we use the combobox string. We use the CBS_DROPDOWN flag.

for ( i = 0; i < 4; i++ ) {


SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
}

We fill the combo box with items. To add a string to the combo box, we send a
CB_ADDSTRING message.

If we select an item from the combo box, the window procedure receives the
WM_COMMAND message with the notification message CBN_SELCHANGE in the high-
order word of the wParam parameter.

sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);


SetWindowText(hwndStatic, items[sel]);
SetFocus(hwnd);

We figure out the currently selected item. We send a CB_GETCURSEL message to the
combo box. The function returns the index of the currently selected item. We set the static
text to the currently selected string. Finally, we set focus to the main window. By default the
combo box has the focus. But it looks ugly, so I changed programatically the focus.

Figure: Combo box

Progress bar
A progress bar is a control that is used, when we process lengthy tasks. It is animated so that
the user knows, that our task is progressing.

#include <windows.h>
#include <commctrl.h>

#define ID_BUTTON 1
#define ID_TIMER 2

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


HINSTANCE g_hinst;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow)
{

HWND hwnd;
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT("Application");
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);

g_hinst = hInstance;

RegisterClass(&wc);
hwnd = CreateWindow(wc.lpszClassName, TEXT("Progress bar"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 260, 170, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam )
{

static HWND hwndPrgBar;


static int i = 1;

INITCOMMONCONTROLSEX InitCtrlEx;

InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
InitCtrlEx.dwICC = ICC_PROGRESS_CLASS;
InitCommonControlsEx(&InitCtrlEx);

switch(msg)
{
case WM_CREATE:
hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL,
WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
30, 20, 190, 25, hwnd, NULL, g_hinst, NULL);

CreateWindow(TEXT("button"), TEXT("Start"),
WS_CHILD | WS_VISIBLE,
85, 90, 80, 25, hwnd, (HMENU) 1, g_hinst, NULL);

SendMessage(hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150));


SendMessage(hwndPrgBar, PBM_SETSTEP, 1, 0 );
break;

case WM_TIMER:
SendMessage( hwndPrgBar, PBM_STEPIT, 0, 0 );
i++;
if ( i == 150 )
KillTimer(hwnd, ID_TIMER);
break;
case WM_COMMAND:
i = 1;
SendMessage( hwndPrgBar, PBM_SETPOS, 0, 0 );
SetTimer(hwnd, ID_TIMER, 5, NULL);
break;

case WM_DESTROY:
KillTimer(hwnd, ID_TIMER);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

In our example, we have a progress bar and a button. The button (re)starts to progress bar
control. We use a timer to update the progress bar.

hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL,


WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
30, 20, 190, 25, hwnd, NULL, g_hinst, NULL);

We create a progress bar control with PROGRESS_CLASS class name and PBS_SMOOTH
style.

SendMessage( hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150));


SendMessage( hwndPrgBar, PBM_SETSTEP, 1, 0 );

We set the range of the progress bar and it's step.

i = 1;
SendMessage( hwndPrgBar, PBM_SETPOS, 0, 0 );
SetTimer(hwnd, ID_TIMER, 5, NULL);

When we press the start button, we set the i value to 1, set the initial position of the progress
bar and start the timer. The timer will send periodically a WM_TIMER message to the
window procedure. Until it is killed.

case WM_TIMER:
SendMessage( hwndPrgBar, PBM_STEPIT, 0, 0 );
i++;
if ( i == 150 )
KillTimer(hwnd, ID_TIMER);
break;

During the WM_TIMER message, we update the progress bar by one step sending the
PBM_STEPIT message. We kill the timer, when the progress bar stops processing.
Figure: Progress bar

20. App Part 2: Using files and the


common dialogs
Example: app_two

1. Create an empty project using Multi-Byte Character Set named app_two.


2. Create two.cpp, app_two.rc and resource.h under their relevant folder in the
solution explorer.
3. copy menu_one.cpp source code into app_two.cpp
4. copy menu_one.rc source code into app_two.rc.
5. copy resource.cpp from the menu_one header files folder into
resource.cpp from the app_two.rc header files folder.
6. Create sub menu New, Open, Read and Close under File menu by modifying
app_two.rc. Give unique id for Open, Read and Close sub-menu. The original
sub-menu Exit will be the lowest sub menu.
7. In app_two.cpp, create the case selection for Open, Read and Close sub-menu.
Test the logic by using Messagebox
8. Create the following structure and routine in app_two.cpp.
9.
The Open File Dialogs
The first step to opening or saving files is finding out the filename to use... of course you
could always hard code the name of the file
into your program, but honestly that doesn't
make for very useful programs most of the
time.

Since this is such a common task, there are


predefined system dialogs that you can use
to allow the user to select a file name. The
most common open and save file dialogs
are accessed through GetOpenFileName()
and GetSaveFileName() respectively, both
of which take an OPENFILENAME struct.

OPENFILENAME ofn;
char szFileName[MAX_PATH] = "";

ZeroMemory(&ofn, sizeof(ofn));

ofn.lStructSize = sizeof(ofn); // SEE NOTE BELOW


ofn.hwndOwner = hwnd;
ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files
(*.*)\0*.*\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = "txt";

if(GetOpenFileName(&ofn))
{
// Do something usefull with the filename stored in szFileName
}

Note that we call ZeroMemory() on the struct in order to initialise it to 0. This is generally a
wise practice, as some APIs are very picky about members that you don't use being set to
NULL. This way you don't need to explicitely set each member that you don't use.

You can easily find out the meanings of the various members by looking them up in your
documentation. The lpstrFilter value points to a double-NULL terminated string, and you
can see from the example that there are several "\0" throughout it, including one at the end...
the compiler will add the second one at the end as it always does with string constants (that's
what you generally don't need to put them in yourself). The NULLs in this string break it up
into filters, each one is two parts. The first filter has the description "Text Files (*.txt)",
the wildcard isn't required here I just put it in because I felt like it. The next part is the actual
wildcard for the first filter, "*.txt". We do the same thing with the second filter except that
this is a generic filter for all files. You can add as many different filters as you'd like.

The lpstrFile points to the buffer we have allocated to store the name of the file, since
filenames can't be larger than MAX_PATH this is the value that I've chosen for the buffer size.
The flags indicate that the dialog should only allow the user to enter filenames that already
exist (since we want to open them, not create them) and to hide the option to open the file in
readonly mode, which we aren't going to support. Finally we provide a default extention, so if
the user types in "foo" and the file is not found, it will try to open "foo.txt" before finally
giving up.

To select a file for saving instead of opening, the code is nearly the same, except for calling
GetSaveFileName() we need only change the flags member to options more suitable for
saving.

ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY |


OFN_OVERWRITEPROMPT;

In this case we no longer want to require the file exist, but we do want the directory to exist
since we aren't going to try and create it first. We'll also prompt the user if they select an
existing file to make sure they want to overwrite it.

NOTE: MSDN States the following for the lStructSize member:

lStructSize
Specifies the length, in bytes, of the structure.

Windows NT 4.0: In an application that is compiled with WINVER and


_WIN32_WINNT >= 0x0500, use OPENFILENAME_SIZE_VERSION_400 for this
member.

Windows 2000/XP: Use sizeof (OPENFILENAME) for this parameter.

Basically what this means is that as of Windows 2000 they added some members to this
struct, and so it's size changed. If the code above doesn't work for you it's possibly because
the size that your compiler used and the size that your operating system (ie. Windows 98,
Windows NT4) expected were different and so the call failed. If this happens, try using
OPENFILENAME_SIZE_VERSION_400 instead of sizeof(ofn). Thanks to people that pointed
this out to me.

Reading and Writing Files


In windows you have a few options as to how you want to access files. You can use the old
io.h open()/read()/write(), you can use stdio.h fopen()/fread()/fwrite(), and if
you are in C++ use can use iostreams.

However in windows all of these methods ultimately call the Win32 API functions, which are
what I will use here. If you are already comfortable using file IO with another method it
should be fairly easy to pick up, or if you want simply use your method of choice to access
files.

To open files, you can use OpenFile() or CreateFile(). MS recommends using only
CreateFile() as OpenFile() is now "obsolete". CreateFile() is a much more versatile
function and provides a great deal of control over the way you open files.
Reading

Say for example you have allowed the user to select a file using GetOpenFileName()...

BOOL LoadTextFileToEdit(HWND hEdit, LPCTSTR pszFileName)


{
HANDLE hFile;
BOOL bSuccess = FALSE;

hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,


OPEN_EXISTING, 0, NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
DWORD dwFileSize;

dwFileSize = GetFileSize(hFile, NULL);


if(dwFileSize != 0xFFFFFFFF)
{
LPSTR pszFileText;

pszFileText = GlobalAlloc(GPTR, dwFileSize + 1);


if(pszFileText != NULL)
{
DWORD dwRead;

if(ReadFile(hFile, pszFileText, dwFileSize, &dwRead, NULL))


{
pszFileText[dwFileSize] = 0; // Add null terminator
if(SetWindowText(hEdit, pszFileText))
bSuccess = TRUE; // It worked!
}
GlobalFree(pszFileText);//
}
}
CloseHandle(hFile);
}
return bSuccess;
}

There is a complete function to read a text file into an edit control. It takes as paramters the
handle to the edit control and the name of the file to read in. This perticular function has a fair
bit of error checking, file IO is one place where a lot of things can go wrong, and so you need
to be on the lookout for errors.

Note the variable dwRead. We don't use it except as a paramter in ReadFile(). This
parameter MUST be provided, the call will fail without it.

In the call to CreateFile() GENERIC_READ means we only want read access.


FILE_SHARE_READ means it's okay if other programs open the file at the same time we do, but
ONLY if they want to read as well, we don't want them writing to the file while we are
reading it. And OPEN_EXISTING means only open the file if it already exists, don't create it,
and don't overwrite it.

Once we've opened the file and chacked to see that CreateFile() succeeded, we check the
size of the file so we'll know how much memory we need to allocate in order to read the
entire thing. We then allocate the memory, check to make sure the allocation succeeded, and
then call ReadFile() to load the contents from disk into our memory buffer. The API file
functions have no concept of Text Files so they won't do things like read a single line of text,
or add NULL terminators to the end of our strings. This is why we've allocated an extra byte
and after we read in the file we add the NULL ourselves so that we can then pass the memory
buffer as a string to SetWindowText().

Once all that has succeeded we set out success variable to TRUE, and clean up as we reach the
end of the function, freeing the memory buffer and closing the file handle before finally
returning to the caller.

Writing
BOOL SaveTextFileFromEdit(HWND hEdit, LPCTSTR pszFileName)
{
HANDLE hFile;
BOOL bSuccess = FALSE;

hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL,


CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
DWORD dwTextLength;

dwTextLength = GetWindowTextLength(hEdit);
// No need to bother if there's no text.
if(dwTextLength > 0)
{
LPSTR pszText;
DWORD dwBufferSize = dwTextLength + 1;

pszText = GlobalAlloc(GPTR, dwBufferSize);


if(pszText != NULL)
{
if(GetWindowText(hEdit, pszText, dwBufferSize))
{
DWORD dwWritten;

if(WriteFile(hFile, pszText, dwTextLength, &dwWritten,


NULL))
bSuccess = TRUE;
}
// GlobalFree(pszText);
}
}
CloseHandle(hFile);
}
return bSuccess;
}

Very similar to reading files, the function to write files has a few changes. First of all when
we call CreateFile() we specify that we want Read access, that the file should always be
created new (and if it exists it will be erased as it's opened) and that if it doesn't exist, it will
be created with the normal file attributes.

Next we get the length of the memory buffer needed from the edit control, since this is the
source of the data. Once we've allocated the memory, we request the string from the edit
control using GetWindowText() and then write it to the file with WriteFile(). Again, like
with ReadFile() the parameter that returns how much was actually written is required, even
though we don't use it.

The full program


//resource.h
#define IDR_MYMENU 101
#define IDI_MYICON 201

#define ID_FILE_EXIT 9001


#define ID_STUFF_GO 9002
#define ID_FILE_DISPLAY 9003
#define ID_CLEAN_DISPLAY 9004
#define ID_FILE_READ 9005
#define ID_FILE_WRITE 9006
#define ID_EDITCHILD 9007
#define MAXPATH 300
//app_two.rc
#include "resource.h"

IDR_MYMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Display", ID_FILE_DISPLAY
MENUITEM "&Clear Display", ID_CLEAN_DISPLAY
MENUITEM "&Read", ID_FILE_READ
MENUITEM "&Write", ID_FILE_WRITE
MENUITEM "E&xit", ID_FILE_EXIT
END

POPUP "&Stuff"
BEGIN
MENUITEM "&Go", ID_STUFF_GO
MENUITEM "G&o somewhere else", 0, GRAYED
END
END

IDI_MYICON ICON "app_two.ico"

//app_two.cpp
#include <windows.h>
#include "resource.h"
char g_szClassName[] = "myWindowClass";//Name of window class
char szFileName[MAX_PATH] = "";
char szFileNameWrite[MAX_PATH] = "";
DWORD dwFileSize;
HANDLE hFile;
//LPCTSTR pszFileNameWrite;
//LPCTSTR pszFileName;
LPSTR pszFileText;
OPENFILENAME ofn;
static HWND hwndEdit;
BOOL SaveTextFileFromEdit(HWND hEdit, LPCTSTR pszFileName)
{
// HANDLE hFile;
BOOL bSuccess = FALSE;
int error;
// GlobalFree((HGLOBAL)pszFileNameWrite);
ZeroMemory(&ofn, sizeof(ofn));

ofn.lStructSize = sizeof(ofn); // SEE NOTE BELOW


ofn.hwndOwner = hEdit;
ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = szFileNameWrite;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = "txt";

if(GetSaveFileName(&ofn))
{
hFile = CreateFile(szFileNameWrite, GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
DWORD dwTextLength;

dwTextLength = GetWindowTextLength(hEdit);
// No need to bother if there's no text.
if(dwTextLength > 0)
{
LPSTR pszText;
DWORD dwBufferSize = dwTextLength + 1;

pszText = (LPSTR)GlobalAlloc(GPTR, dwBufferSize);


if(pszText != NULL)
{
if(GetWindowText(hEdit, pszText, dwBufferSize))
{
DWORD dwWritten;

if(WriteFile(hFile, pszText, dwTextLength, &dwWritten,


NULL))
bSuccess = TRUE;
}
// GlobalFree(pszText);
}
}
CloseHandle(hFile);
return bSuccess;
}
else
{
error=(GetLastError());
return error;
}
}
}

BOOL LoadTextFileToEdit(HWND hEdit, LPCTSTR pszFileName)


{
//Initialize OPENFILENAME structure data ofn
// HANDLE hFile;
BOOL bSuccess = FALSE;
ZeroMemory(&ofn, sizeof(ofn));

ofn.lStructSize = sizeof(ofn); // SEE NOTE BELOW


ofn.hwndOwner = hEdit;
ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = "txt";
//Use dialog box to get the filename into szFileName
if(GetOpenFileName(&ofn))
{
//Create the file
hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
// DWORD dwFileSize;
//Get file size
dwFileSize = GetFileSize(hFile, NULL);
//If there is no error
if(dwFileSize != 0xFFFFFFFF)
{
//Allocate heap for file data pszFileText
pszFileText = (LPSTR)GlobalAlloc(GPTR, dwFileSize + 1);
//If heap allocation is successful
if(pszFileText != NULL)
{
DWORD dwRead;
//Read the file into pszFileText
if(ReadFile(hFile, pszFileText, dwFileSize, &dwRead, NULL))
{
pszFileText[dwFileSize] = 0; // Add null terminator
// if(SetWindowText(hEdit, pszFileText))
bSuccess = TRUE; // It worked!
}
// GlobalFree(pszFileText);
}
}
else
bSuccess = FALSE;
CloseHandle(hFile);
}
}
return bSuccess;
}
// Step 4: the Window Procedure - This is the brain

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
// OPENFILENAME ofn;
switch(msg)
{
case WM_CREATE:
//This create the Edit Class Window
hwndEdit = CreateWindow("EDIT", // predefined class
NULL, // no window title
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
0, 0, 0, 0, // set size in WM_SIZE message
hwnd, // parent window
(HMENU) ID_EDITCHILD, // edit control ID
(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
NULL); // pointer not needed
case WM_SIZE:
// Make the edit control the size of the window's client area.

MoveWindow(hwndEdit,
0, 0, // starting x- and y-
coordinates
LOWORD(lParam), // width of client area
HIWORD(lParam), // height of client area
TRUE); // repaint window
return 0;
case WM_COMMAND:
switch(wParam)
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_STUFF_GO:
MessageBox(NULL,"Go Exit Selection"
,"ID_STUFF" ,MB_ICONEXCLAMATION | MB_OK);
break;
case ID_FILE_DISPLAY:

// Add text in pszFileText to the window.


SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)
pszFileText);
break;
case ID_CLEAN_DISPLAY:
{
// int count=0;
// while (count <dwFileSize)
// {
// pszFileText[count] = (char)" ";
// count++;
// }
// EDIT::UpdateData(FALSE);
// SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)
pszFileText);
break;
}
case ID_FILE_READ:
//This Read the file into EDIT window
if(!LoadTextFileToEdit( hwnd, szFileName))
MessageBox(NULL,szFileName ,"Read
Error" ,MB_ICONEXCLAMATION | MB_OK);
break;
case ID_FILE_WRITE:
if(!SaveTextFileFromEdit(hwndEdit,
szFileNameWrite))
MessageBox(NULL,szFileNameWrite ,"Write
Error" ,MB_ICONEXCLAMATION | MB_OK);
break;
}
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 0, 0, "Hello, Windows!", 15);
EndPaint(hwnd, &ps);
return 0L;
// Process other messagescase WM_CLOSE:
DestroyWindow(hwnd);//sends the WM_DESTROY message to the
window
//(hwnd) getting destroyed,
break;
case WM_DESTROY:
PostQuitMessage(0);//This posts the WM_QUIT message to the
message loop
//This window will never receive this message,
because it causes
//GetMessage() to return FALSE
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
// default window procedure to provide default processing
for any window
//messages that an application does not process
}
return 0;
}

//
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) //Entry of program
{
WNDCLASSEX wc; //Create WNDCLASSEX object

//Step 1: Registering the Window Class


wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;// Window Procedure: mandatory
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;// owner of the class: mandatory
wc.hIcon = LoadIcon(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDI_MYICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);// optional
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);// optional
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU);
wc.lpszClassName = g_szClassName;// Name of window class:mandatory
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 16, 16, 0);

if(!RegisterClassEx(&wc)) //Registering successful


{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

//Step 2: Creating the Window


HWND hwnd; //Create variable hwnd to store handle of Instance
(process)
hwnd = CreateWindowEx(
WS_EX_APPWINDOW,
g_szClassName,// name of a registered window class
szFileName,// window caption
WS_OVERLAPPEDWINDOW,// window style
CW_USEDEFAULT, CW_USEDEFAULT,//w and y position,
640, 480,// width and height
NULL, // handle to parent window
NULL, // handle to menu
hInstance, // application instance
NULL);// window creation data
if(hwnd==0) //If window creation failed
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);//Show window identified by hwnd using
//parameter passed by the last parameter in WinMain()(nCmdShow)
UpdateWindow(hwnd);//then update it to ensure that it has properly
//redrawn itself on the screen

//Step 3: The Message Loop - This is the heart


MSG Msg; //Create MSG structure to store data for windows message
// Keep pumping messages--they end up in our Window Procedure
while(GetMessage(&Msg, NULL, 0, 0) > 0)//while (message from
// application's message queue)>0)
{
TranslateMessage(&Msg);//do some additional processing on
//keyboard events like generating WM_CHAR messages to
//go along with WM_KEYDOWN messages
DispatchMessage(&Msg);//sends the message out to the
//windows procedure
}
return Msg.wParam;

In this section of the Winapi C tutorial, we will talk about more advanced windows controls.

ListBox
A list box is a simple list control, from which user can select one or more items. Selected
items are marked.
#include <windows.h>
#include <strsafe.h>

#define IDC_LIST 1
#define IDC_STATIC 2

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

HINSTANCE g_hinst;

typedef struct
{
TCHAR name[30];
TCHAR job[20];
int age;

} Friends;

Friends friends[] =
{
{TEXT("Erika"), TEXT("waitress"), 18},
{TEXT("Thomas"), TEXT("programmer"), 25},
{TEXT("George"), TEXT("police officer"), 26},
{TEXT("Michael"), TEXT("producer"), 38},
{TEXT("Jane"), TEXT("steward"), 28},
};

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Application" );
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);

g_hinst = hInstance;

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("List Box"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 340, 200, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

static HWND hwndList, hwndStatic;


int i, sel;
TCHAR buff[100];

switch(msg) {

case WM_CREATE:

hwndList = CreateWindow(TEXT("listbox") , NULL, WS_CHILD |


WS_VISIBLE | LBS_NOTIFY,
10, 10, 150, 120, hwnd,(HMENU) IDC_LIST, g_hinst, NULL);

hwndStatic = CreateWindow(TEXT("static") , NULL, WS_CHILD |


WS_VISIBLE,
200, 10, 120, 45, hwnd,(HMENU) IDC_STATIC, g_hinst, NULL);

for (i = 0; i < ARRAYSIZE(friends); i++) {


SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)
friends[i].name);
}

break;

case WM_COMMAND:
if (LOWORD(wParam) == IDC_LIST) {
if (HIWORD(wParam) == LBN_SELCHANGE) {
sel = (int) SendMessage(hwndList, LB_GETCURSEL, 0, 0);
StringCbPrintf(buff, ARRAYSIZE(buff), TEXT("Job:
%s\nAge: %d"),
friends[sel].job, friends[sel].age);
SetWindowText(hwndStatic, buff);
}
}
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
In this example, we display a list box control and a static text control. By selecting a person
from a list box, we display his job and age in the static control.
hwndList = CreateWindow(TEXT("listbox") , NULL, WS_CHILD | WS_VISIBLE |
LBS_NOTIFY,
10, 10, 150, 120, hwnd,(HMENU) IDC_LIST, g_hinst, NULL);
A basic list box is created with listbox window class and LBS_NOTIFY message.
for (i = 0; i < ARRAYSIZE(friends); i++) {
SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) friends[i].name);
}
We fill the list box. To do this, we send multiple LB_ADDSTRING messages to the list box
control.
if (HIWORD(wParam) == LBN_SELCHANGE) {
sel = (int) SendMessage(hwndList, LB_GETCURSEL, 0, 0);
StringCbPrintf(buff, ARRAYSIZE(buff), TEXT("Job: %s\nAge: %d"),
friends[sel].job, friends[sel].age);
SetWindowText(hwndStatic, buff);
}
If we select an item from a list box, the window procedure receives a LBN_SELCHANGE
message. First we determine the currently selected item by sending a LB_GETCURSEL
message to the list box. Then we copy the job name and age from the friends structure to the
buff variable. Finally, we set the static text with SetWindowText() function call.

Figure: List Box


21. Creating Custom Controls
Here we will demostrate, how to create our own custom controls. The winapi has a collection
of various prebuilt controls. More specific controls have to be created manually. We use the
GDI to create custom controls.

The Burning control


This control can be found in various media burning applications, like Nero Burning ROM.
#include <windows.h>
#include <commctrl.h>

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


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

HINSTANCE g_hinst;
LRESULT g_pos = 150;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow)
{

HWND hwnd;
MSG msg ;
WNDCLASS wc = {0};

wc.lpszClassName = TEXT("Application");
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

g_hinst = hInstance;

RegisterClass(&wc);
hwnd = CreateWindow(wc.lpszClassName, TEXT("Burning control"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
100, 100, 400, 250, 0, 0, hInstance, 0);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam
)
{

static HWND hwndTrack, hwndBurn;


WNDCLASS rwc = {0};
INITCOMMONCONTROLSEX InitCtrlEx;

InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
InitCtrlEx.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&InitCtrlEx);

switch(msg)
{
case WM_CREATE:

rwc.lpszClassName = TEXT( "BurningControl" );

rwc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
rwc.style = CS_HREDRAW;
rwc.lpfnWndProc = PanelProc;
rwc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&rwc);

hwndBurn = CreateWindowEx(WS_EX_STATICEDGE ,
TEXT("BurningControl"), NULL,
WS_CHILD | WS_VISIBLE, 0, 330, 490, 30, hwnd, (HMENU)1, NULL,
NULL);

hwndTrack = CreateWindowEx(0, TRACKBAR_CLASS, NULL,


WS_CHILD | WS_VISIBLE | TBS_FIXEDLENGTH | TBS_NOTICKS,
40, 25, 150, 25, hwnd, (HMENU) 2, g_hinst, NULL);

SendMessage(hwndTrack, TBM_SETRANGE, TRUE, MAKELONG(0, 750));


SendMessage(hwndTrack, TBM_SETPAGESIZE, 0, 20);
SendMessage(hwndTrack, TBM_SETTICFREQ, 20, 0);
SendMessage(hwndTrack, TBM_SETPOS, TRUE, 150);
break;

case WM_SIZE:
SetWindowPos(hwndBurn, NULL, 0, HIWORD(lParam)-30,
LOWORD(lParam), 30, SWP_NOZORDER);
break;

case WM_HSCROLL:
g_pos = SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
InvalidateRect(hwndBurn, NULL, TRUE);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK PanelProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam )
{
HBRUSH hBrushYellow, hBrushRed, holdBrush;
HPEN hPen, holdPen;
HFONT hFont, holdFont;
PAINTSTRUCT ps;
RECT rect, rect2;
TCHAR *cap[] = { TEXT("75"), TEXT("150"), TEXT("225"), TEXT("300"),
TEXT("375"), TEXT("450"), TEXT("525"), TEXT("600"), TEXT("675")};

HDC hdc;
int till;
int step, full;
int i;

switch(msg)
{

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);

GetClientRect(hwnd, &rect);

till = (rect.right / 750.0) * g_pos;


step = rect.right / 10.0;
full = (rect.right / 750.0) * 700;

hBrushYellow = CreateSolidBrush(RGB(255, 255, 184));


hBrushRed = CreateSolidBrush(RGB(255, 110, 110));

hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));


holdPen = SelectObject(hdc, hPen);

hFont = CreateFont(13, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0, 0, 0, 0, 0,


TEXT("Tahoma"));
holdFont = SelectObject(hdc, hFont);

if(till > full) {


SelectObject(hdc, hBrushYellow);
Rectangle(hdc, 0, 0, full, 30);
holdBrush = SelectObject(hdc, hBrushRed);
Rectangle(hdc, full, 0, till, 30);
} else {
holdBrush = SelectObject(hdc, hBrushYellow);
Rectangle(hdc, 0, 0, till, 30);
}

SelectObject(hdc, holdPen);

for ( i = 1; i < 10; i++) {

MoveToEx(hdc, i*step, 0, NULL);


LineTo(hdc, i*step, 7);

rect2.bottom = 28;
rect2.top = 8;
rect2.left = i*step-10;
rect2.right = i*step+10;

SetBkMode(hdc, TRANSPARENT) ;
DrawText(hdc, cap[i-1], strlen(cap[i-1]), &rect2, DT_CENTER);
}

SelectObject(hdc, holdBrush);
DeleteObject(hBrushYellow);
DeleteObject(hBrushRed);
DeleteObject(hPen);

SelectObject(hdc, holdFont);
DeleteObject(hFont);

EndPaint(hwnd, &ps);
break;

}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example, we display a trackbar control and our custom burning control. The trackbar
control is used to control the state of the burning control.
The burning control is a simple window. It is placed on the bottom of the parent window. It is
completely drawn during the WM_PAINT message. The lines, text and background is drawn
using the GDI function calls.

Figure: Burning control

22. The GDI


The GDI (Graphics Device Interface) is an interface for working with graphics. It is used to
interact with graphic devices such as monitor, printer or a file. The GDI allows programmers
to display data on a screen or printer without having to be concerned about the details of a
particular device. The GDI insulates the programmer from the hardware. From the
programmer's point of view, the GDI is a group of classes and methods for working with
graphics. The GDI consists of 2D Vector Graphics, Fonts and Images. To begin drawing
graphics, we must obtain a device context (DC) object.
Basic Graphics Primitives
Pixels
The simplest object in GDI is the pixel. It is a single spot on the monitor screen.
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Simple" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Simple"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 150, NULL, NULL, hInstance, NULL);

while( GetMessage(&msg, NULL, 0, 0)) {


TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam)
{

HDC hdc;
PAINTSTRUCT ps;
RECT rect;
static int w=250, h=150;
int x, y;
int i;

switch(msg)
{

case WM_SIZE:
GetClientRect(hwnd, &rect);
w = LOWORD(lParam) + 1;
h = HIWORD(lParam) + 1;
InvalidateRect(hwnd, &rect, TRUE);
break;

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
for (i = 0; i<1000; i++) {
x = (rand() % w) + 1;
y = (rand() % h) + 1;
SetPixel(hdc, x, y, RGB(255, 0, 0));
}

EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example we display randomly 1000 red pixels on the client area of the window. If we
resize the window, the pixels are redrawn.

Figure: pixels

Rectangle
To draw a rectangle, we use the Rectangle() function.
#include <windows.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Rectangle" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Rectangle"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 200, NULL, NULL, hInstance, NULL);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
Rectangle(hdc, 50, 50, 200, 100);
EndPaint(hwnd, &ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
The outline of the rectangle is drawn using the current pen. The background is drawn using
the current brush.
BOOL Rectangle( HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect );
The rectangle is drawn using the Rectangle function. We draw the rectangle using two points.
Top left point and bottom right point.

Figure: rectangle

Pen
Pen is an elementary graphics object. It is used to draw lines, curves and outlines of
rectangles, ellipses, polygons or other shapes.
HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);
The CreatePen() function creates a logical pen with a specified style, width and color.
#include <windows.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Lines" );
wc.hInstance = hInstance ;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Lines"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 180, NULL, NULL, hInstance, NULL);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HPEN holdPen;
HPEN hPen1;
HPEN hPen2;
HPEN hPen3;
HPEN hPen4;
HPEN hPen5;

switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hPen1 = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
hPen2 = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
hPen3 = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
hPen4 = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0));
hPen5 = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0));

holdPen = SelectObject(hdc, hPen1);


MoveToEx(hdc, 50, 30, NULL);
LineTo(hdc, 200, 30);

SelectObject(hdc, hPen2);
MoveToEx(hdc, 50, 50, NULL);
LineTo(hdc, 200, 50);

SelectObject(hdc, hPen2);
MoveToEx(hdc, 50, 70, NULL);
LineTo(hdc, 200, 70);

SelectObject(hdc, hPen3);
MoveToEx(hdc, 50, 90, NULL);
LineTo(hdc, 200, 90);

SelectObject(hdc, hPen4);
MoveToEx(hdc, 50, 110, NULL);
LineTo(hdc, 200, 110);

SelectObject(hdc, holdPen);
DeleteObject(hPen1);
DeleteObject(hPen2);
DeleteObject(hPen3);
DeleteObject(hPen4);
DeleteObject(hPen5);

EndPaint(hwnd, &ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example, we draw 5 different lines. Using 5 different pen styles.
SelectObject(hdc, hPen1);
To activate a pen, we call the SelectObject() function.
MoveToEx(hdc, 50, 30, NULL);
LineTo(hdc, 200, 30);
To draw lines, we use MoveToEx() and LineTo() functions.
DeleteObject(hPen1);
DeleteObject(hPen2);
DeleteObject(hPen3);
DeleteObject(hPen4);
DeleteObject(hPen5);

In the end, we clean up resources.

Figure: lines

Brush
Brush is an elementary graphics object. It is used to paint the background of graphics shapes,
such as rectangles, ellipses or polygons. A brush can be a solid colour a hatch or a custom
bitmap pattern.
In the following example, we create 4 rectangles filled with 4 different solid colors.
#include <windows.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Brush" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Solid Brush"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 220, 240, NULL, NULL, hInstance, NULL);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HPEN holdPen, hPen;
HBRUSH holdBrush, hBrush1, hBrush2, hBrush3, hBrush4;

switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
holdPen = SelectObject(hdc, hPen);

hBrush1 = CreateSolidBrush(RGB(121, 90, 0));


hBrush2 = CreateSolidBrush(RGB(240, 63, 19));
hBrush3 = CreateSolidBrush(RGB(240, 210, 18));
hBrush4 = CreateSolidBrush(RGB(9, 189, 21));

holdBrush = SelectObject(hdc, hBrush1);


Rectangle(hdc, 30, 30, 100, 100);
SelectObject(hdc, hBrush2);
Rectangle(hdc, 110, 30, 180, 100);
SelectObject(hdc, hBrush3);
Rectangle(hdc, 30, 110, 100, 180);
SelectObject(hdc, hBrush4);
Rectangle(hdc, 110, 110, 180, 180);

SelectObject(hdc, holdPen);
SelectObject(hdc, holdBrush);

DeleteObject(hPen);
DeleteObject(hBrush1);
DeleteObject(hBrush2);
DeleteObject(hBrush3);
DeleteObject(hBrush4);

EndPaint(hwnd, &ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
hBrush1 = CreateSolidBrush(RGB(121, 90, 0));
Here we create a solid color brush.

Figure: solid brush

Hatch brushes
There are six predefined hatch brushes availabel. In our example, we show all of them.
#include <windows.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Brush" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Hatch brush"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 220, NULL, NULL, hInstance, NULL);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HPEN holdPen, hPen;
HBRUSH holdBrush, hBrush1, hBrush2, hBrush3, hBrush4, hBrush5, hBrush6;
switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
holdPen = SelectObject(hdc, hPen);

hBrush1 = CreateHatchBrush(HS_BDIAGONAL, RGB(0, 0, 0));


hBrush2 = CreateHatchBrush(HS_FDIAGONAL, RGB(0, 0, 0));
hBrush3 = CreateHatchBrush(HS_CROSS, RGB(0, 0, 0));
hBrush4 = CreateHatchBrush(HS_HORIZONTAL, RGB(0, 0, 0));
hBrush5 = CreateHatchBrush(HS_DIAGCROSS, RGB(0, 0, 0));
hBrush6 = CreateHatchBrush(HS_VERTICAL, RGB(0, 0, 0));

holdBrush = SelectObject(hdc, hBrush1);


Rectangle(hdc, 30, 30, 100, 80);
SelectObject(hdc, hBrush2);
Rectangle(hdc, 110, 30, 180, 80);
SelectObject(hdc, hBrush3);
Rectangle(hdc, 190, 30, 260, 80);
SelectObject(hdc, hBrush4);
Rectangle(hdc, 30, 110, 100, 160);
SelectObject(hdc, hBrush5);
Rectangle(hdc, 110, 110, 180, 160);
SelectObject(hdc, hBrush6);
Rectangle(hdc, 190, 110, 260, 160);

SelectObject(hdc, holdPen);
SelectObject(hdc, holdBrush);

DeleteObject(hPen);
DeleteObject(hBrush1);
DeleteObject(hBrush2);
DeleteObject(hBrush3);
DeleteObject(hBrush4);
DeleteObject(hBrush5);
DeleteObject(hBrush6);

EndPaint(hwnd, &ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
This example is very similar to the previous one. We only use a new function call
CreateHatchBrush()

Figure: Hatch brush

Shapes
Shapes are more sophisticated geometrical objects. We will draw various geometrical shapes
in the following example.
#include <windows.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Shapes" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Shapes"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 390, 230, NULL, NULL, hInstance, NULL);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
const POINT polygon[10] = { 30, 145, 85, 165, 105, 110, 65, 125, 30, 105 };
const POINT bezier[4] = {280, 160, 320, 160, 325, 110, 350, 110};

switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);

Ellipse(hdc, 30, 30, 120, 90);


RoundRect(hdc, 150, 30, 240, 90, 15, 20);
Chord(hdc, 270, 30, 360, 90, 270, 45, 360, 45);
Polygon(hdc, polygon, 5);
Rectangle(hdc, 150, 110, 230, 160);
PolyBezier(hdc, bezier, 4);

EndPaint(hwnd, &ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

In our example, we have created an ellipse, a rounded rectangle, a chord, a polygon, a


rectangle and a bezier curve.

Figure: Shapes

Fonts
Text can be drawn on the window using various fonts. A font is a set of type characters of a
particular typeface design and size. (answers.com) Various typefaces include Helvetica,
Georgia, Times or Verdana.
#include <windows.h>

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

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR


lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Sonnet 55" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);

RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Sonnet 55"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 390, 350, NULL, NULL, hInstance, NULL);

while( GetMessage(&msg, NULL, 0, 0)) {


DispatchMessage(&msg);
}
return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam)
{
HDC hdc;
PAINTSTRUCT ps;

DWORD color;

HFONT hFont, holdFont;

static TCHAR *verse1 = TEXT("Not marble, nor the gilded monuments");


static TCHAR *verse2 = TEXT("Of princes, shall outlive this powerful
rhyme;");
static TCHAR *verse3 = TEXT("But you shall shine more bright in these
contents");
static TCHAR *verse4 = TEXT("Than unswept stone, besmear'd with sluttish
time.");
static TCHAR *verse5 = TEXT("When wasteful war shall statues overturn,");
static TCHAR *verse6 = TEXT("And broils root out the work of masonry,");
static TCHAR *verse7 = TEXT("Nor Mars his sword, nor war's quick fire
shall burn");
static TCHAR *verse8 = TEXT("The living record of your memory.");
static TCHAR *verse9 = TEXT("'Gainst death, and all oblivious enmity");
static TCHAR *verse10 = TEXT("Shall you pace forth; your praise shall
still find room ");
static TCHAR *verse11 = TEXT("Even in the eyes of all posterity");
static TCHAR *verse12 = TEXT("That wear this world out to the ending
doom.");
static TCHAR *verse13 = TEXT("So, till the judgment that yourself
arise,");
static TCHAR *verse14 = TEXT("You live in this, and dwell in lovers'
eyes.");

switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);

color = GetSysColor(COLOR_BTNFACE);
SetBkColor(hdc, color);

hFont = CreateFont(15, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0, 0, 0, 0,


0, TEXT("Georgia"));
holdFont = SelectObject(hdc, hFont);
TextOut(hdc, 50, 20, verse1, lstrlen(verse1));
TextOut(hdc, 50, 40, verse2, lstrlen(verse2));
TextOut(hdc, 50, 60, verse3, lstrlen(verse3));
TextOut(hdc, 50, 80, verse4, lstrlen(verse4));
TextOut(hdc, 50, 100, verse5, lstrlen(verse5));
TextOut(hdc, 50, 120, verse6, lstrlen(verse6));
TextOut(hdc, 50, 140, verse7, lstrlen(verse7));
TextOut(hdc, 50, 160, verse8, lstrlen(verse8));
TextOut(hdc, 50, 180, verse9, lstrlen(verse9));
TextOut(hdc, 50, 200, verse10, lstrlen(verse10));
TextOut(hdc, 50, 220, verse11, lstrlen(verse11));
TextOut(hdc, 50, 240, verse12, lstrlen(verse12));
TextOut(hdc, 50, 260, verse13, lstrlen(verse13));
TextOut(hdc, 50, 280, verse14, lstrlen(verse14));

SelectObject(hdc, holdFont);
DeleteObject(hFont);

EndPaint(hwnd, &ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

color = GetSysColor(COLOR_BTNFACE);
SetBkColor(hdc, color);

By default, if we draw some text on the client area of the window, the background is set to
white color. We can change this by setting the backround color using the SetBkColor()
function. I used the typical Windows greyish color. The GetSysColor function is used to get
the system colors used in buttons, title or backround of window controls.
hFont = CreateFont(15, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0, 0, 0, 0, 0,
TEXT("Georgia"));
Here we create a font object. There are 14 parameters. We don't have to specify all of them. I
used only the font size, font weight and fontface parameters.
TextOut(hdc, 50, 20, verse1, lstrlen(verse1));
The text is drawn onto the window using the TextOut() function.
Figure: Fonts

23. Timers and Animation


Example: anim_one

Setting up
Before we get things animated, we need to set up a
structure to store the position of the ball between updates.
This struct will store the current position and size of the
ball, as well as the delta values, how much we want it to
move each frame.

Once we have the structure type declared, we also declare


a global instance of the struct. This is ok since we only have one ball, if were were going to
animate a bunch of them, you'd probably want to use an array or other container (such as a
linked list in C++) to store them in a more convenient way.

const int BALL_MOVE_DELTA = 2;

typedef struct _BALLINFO


{
int width;
int height;
int x;
int y;

int dx;
int dy;
}BALLINFO;

BALLINFO g_ballInfo;

We've also defined a constant BALL_MOVE_DELTA which is how far we want the ball to move
on each update. The reason we store deltas in the BALLINFO structure as well is that we want
to be able to move the ball left or right and up and down independantly, BALL_MOVE_DELTA is
just a handy name to give the value so we can change it later if we want.

Now we need to initialize this structure after we load our bitmaps:

BITMAP bm;
GetObject(g_hbmBall, sizeof(bm), &bm);

ZeroMemory(&g_ballInfo, sizeof(g_ballInfo));
g_ballInfo.width = bm.bmWidth;
g_ballInfo.height = bm.bmHeight;

g_ballInfo.dx = BALL_MOVE_DELTA;
g_ballInfo.dy = BALL_MOVE_DELTA;

The ball starts off in the top left corner, moving to the right and down according to the dx and
dy members of BALLINFO.

Setting the Timer


The easiest way to add a simple timer into a window program is with SetTimer(), it's not the
best, and it's not recommended for real multimedia or full games, however it's good enough
for simple animations like this. When you need something better take a look at
timeSetEvent() in MSDN; it's more accurate.
const int ID_TIMER = 1;
ret = SetTimer(hwnd, ID_TIMER, 50, NULL);
if(ret == 0)
MessageBox(hwnd, "Could not SetTimer()!", "Error", MB_OK |
MB_ICONEXCLAMATION);
Here we've declared a timer id so that we can refer to it later (to kill it) and then set the timer
in the WM_CREATE handler of our main window. Each time the timer elapses, it will send a
WM_TIMER message to the window, and pass us back the ID in wParam. Since we only have
one timer we don't need the ID, but it's useful if you set more than one timer and need to tell
them apart.

We've set the timer to elapse every 50 milliseconds, which results in approximately 20 frames
per second. Approximately because like I said, SetTimer() is a little inaccurate, but this isn't
critical code, and a few milliseconds here or there won't kill us.
Animating in WM_TIMER
Now when we get WM_TIMER we want to calculate the new position for the ball and draw it's
updated position.
case WM_TIMER:
{
RECT rcClient;
HDC hdc = GetDC(hwnd);

GetClientRect(hwnd, &rcClient);

UpdateBall(&rcClient);
DrawBall(hdc, &rcClient);

ReleaseDC(hwnd, hdc);
}
break;

I've put the code for updating and drawing the ball in their own functions. This is good
practice, and it lets us draw the ball from either WM_TIMER or WM_PAINT without duplicating
code, note that the method we use to get the HDC in each case is different, so it's best to leave
this code in the message handlers and pass the result into the DrawBall() function.

void UpdateBall(RECT* prc)


{
g_ballInfo.x += g_ballInfo.dx;
g_ballInfo.y += g_ballInfo.dy;

if(g_ballInfo.x < 0)
{
g_ballInfo.x = 0;
g_ballInfo.dx = BALL_MOVE_DELTA;
}
else if(g_ballInfo.x + g_ballInfo.width > prc->right)
{
g_ballInfo.x = prc->right - g_ballInfo.width;
g_ballInfo.dx = -BALL_MOVE_DELTA;
}

if(g_ballInfo.y < 0)
{
g_ballInfo.y = 0;
g_ballInfo.dy = BALL_MOVE_DELTA;
}
else if(g_ballInfo.y + g_ballInfo.height > prc->bottom)
{
g_ballInfo.y = prc->bottom - g_ballInfo.height;
g_ballInfo.dy = -BALL_MOVE_DELTA;
}
}
All this does is some basic math, we add the delta value to the x position to move the ball. If
the ball goes outside the client area, move it back in range and change the delta value to the
opposite direction so that the ball "bounces" off the sides.
void DrawBall(HDC hdc, RECT* prc)
{
HDC hdcBuffer = CreateCompatibleDC(hdc);
HBITMAP hbmBuffer = CreateCompatibleBitmap(hdc, prc->right, prc-
>bottom);
HBITMAP hbmOldBuffer = SelectObject(hdcBuffer, hbmBuffer);

HDC hdcMem = CreateCompatibleDC(hdc);


HBITMAP hbmOld = SelectObject(hdcMem, g_hbmMask);

FillRect(hdcBuffer, prc, GetStockObject(WHITE_BRUSH));

BitBlt(hdcBuffer, g_ballInfo.x, g_ballInfo.y, g_ballInfo.width,


g_ballInfo.height, hdcMem, 0, 0, SRCAND);

SelectObject(hdcMem, g_hbmBall);
BitBlt(hdcBuffer, g_ballInfo.x, g_ballInfo.y, g_ballInfo.width,
g_ballInfo.height, hdcMem, 0, 0, SRCPAINT);

BitBlt(hdc, 0, 0, prc->right, prc->bottom, hdcBuffer, 0, 0, SRCCOPY);

SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);

SelectObject(hdcBuffer, hbmOldBuffer);
DeleteDC(hdcBuffer);
DeleteObject(hbmBuffer);
}
This is essentially the same drawing code as the past few examples, with the exception that it
gets the position and dimentions of the ball from the BALLINFO structure. There is however
one important difference...

Double Buffering
When doing your drawing directly to the HDC of the window, it's entirely possible that the
screen will get updated before you're done... for example after you draw the mask and before
you draw the colour image over top, the user might see a flicker of the back background
before your program has a chance to draw over it in colour. The slower your computer and
the more drawing operations that you do, the more flicker will be apparent and eventually it
will look like a big jumbled mess.

This is terribly distracting, and we can solve it simply by doing all the drawing in memory
first, and then copying the completed masterpiece to the screen in a single BitBlt() so that
the screen is updated directly from the old image, to the complete new image with none of the
individual operations visible.

To do this, we create a temporary HBITMAP in memory that is the exact size of the area we are
going to draw to on the screen. We also need an HDC so that we can BitBlt() to the bitmap.

HDC hdcBuffer = CreateCompatibleDC(hdc);


HBITMAP hbmBuffer = CreateCompatibleBitmap(hdc, prc->right, prc->bottom);
HBITMAP hbmOldBuffer = SelectObject(hdcBuffer, hbmBuffer);

Now that we have a place to draw to in memory, all of the drawing operations use hdcBuffer
instead of hdc (the window) and the results are stored on the bitmap in memory untill we are
complete. We can now copy the whole thing over to the window in one shot.
BitBlt(hdc, 0, 0, prc->right, prc->bottom, hdcBuffer, 0, 0, SRCCOPY);

That's it, and we clean up our HDCs and HBITMAPs as usual.

Faster Double Buffering

In this example I am creating and destroying the bitmap used for double buffering each
frame, I did this basically because I wanted to be able to size the window so it's easier to just
always create a new buffer than to track when the window position changes and resize the
buffer. It would be more efficient to create a global double buffer bitmap and either not allow
the window to resize or only resize the bitmap when the window resized, instead of creating
it and destroying it all the time. It's up to you to implement this if you want to optimize the
drawing for a game or something.

Killing the Timer


When our window is destroyed, it's a good idea to release all resources we used, and in this
case that includes the timer we set. To stop it, we simply call KillTimer() and pass in the ID
that we used when we created it.
KillTimer(hwnd, ID_TIMER);

Loading Fonts
The Win32 GDI has some remarkable capabilites for dealing with vastly different typefaces,
styles, languages and characters sets. One of the drawbacks of this is that dealing with fonts
can look rather intimidating to the newcomer. CreateFont(), the primary API when it comes
to fonts, has 14 parameters for specifying height, style, weight, family, and various other
attributes.

Fortunately, it's not really has hard as it might appear, and a large portion of the work
involved is taken care of my sensible default values. All but 2 of the parameters to
CreateFont() can be set to 0 or NULL, and the system will simply use a default value giving
you a plain ordinary font.

CreateFont() creates an HFONT, a handle to a Logical Font in memory. The data held by this
handle can be retreived into a LOGFONT structure using GetObject() just as a BITMAP struct
can be filled from an HBITMAP.

The members of the LOGFONT are identical to the parameters to CreateFont() and for
convenience you can create a font directly from an existing LOGFONT structure using
CreateFontIndirect(). This is very handy, since it makes it simple to create a new font
from an existing font handle when you only want to alter certain aspects of it. Use
GetObject() to fill a LOGFONT, alter the members that you wish, and create a new font with
CreateFontIndirect().

HFONT hf;
HDC hdc;
long lfHeight;

hdc = GetDC(NULL);
lfHeight = -MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
ReleaseDC(NULL, hdc);

hf = CreateFont(lfHeight, 0, 0, 0, 0, TRUE, 0, 0, 0, 0, 0, 0, 0,
"Times New Roman");

if(hf)
{
DeleteObject(g_hfFont);
g_hfFont = hf;
}
else
{
MessageBox(hwnd, "Font creation failed!", "Error", MB_OK |
MB_ICONEXCLAMATION);
}

This is the code used to create the font in the example image. This is Times New Roman at 12
Point with the Italics style set. The italics flag is the 6th parameter to CreateFont() which
you can see we have set to TRUE. The name of the font we want to use is the last parameter.

The one bit of trickery in this code is the value used for the size of the font, the lfHeight
parameter to CreateFont(). Usually people are used to working with Point sizes, Size 10,
Size 12, etc... when dealing with fonts. CreateFont() however doesn't accept point sizes, it
wants Logical Units which are different on your screen than they are on your Printer, and
even between Printers and screens.

The reason this situation exists is because the resolution of different devices is so vastly
different... Printers can easily display 600 to 1200 pixels per inch, while a screen is lucky to
get 200... if you used the same sized font on a printer as on a screen, you likely wouldn't even
be able to see individual letters.

All we have to do is convert from the point size we want, into the appropriate logical size for
the device. In this case the device is the screen, so we get the HDC to the screen, and get the
number of logical pixels per inch using GetDeviceCaps() and slap this into the formula so
generously provided in MSDN which uses MulDiv() to convert from our pointsize of 12 to
the correct logical size that CreateFont() expects. We store this in lfHeight and pass it as
the first parameter to CreateFont().

Default Fonts

When you first call GetDC() to get the HDC to your window, the default font that is selected
into it is System, which to be honest isn't all that attractive. The simplest way to get a
reasonable looking font to work with (without going through the CreateFont() hassle) is to
call GetStockObject() and ask for the DEFAULT_GUI_FONT.

This is a system object and you can get it as many times as you want without leaking
memory, and you can call DeleteObject() on it which won't do anything, which is good
because now you don't need to keep track of whether your font is one from CreateFont() or
GetStockObject() before trying to free it.

Drawing Text
Now that we have a handy-dandy font, how do we get some text on the screen? This is
assuming that we don't just want to use an Edit or Static control.

Your basic options are TextOut() and DrawText(). TextOut() is simpler, but has less
options and doesn't do word wrapping or alignment for you.

char szSize[100];
char szTitle[] = "These are the dimensions of your client area:";
HFONT hfOld = SelectObject(hdc, hf);

SetBkColor(hdc, g_rgbBackground);
SetTextColor(hdc, g_rgbText);

if(g_bOpaque)
{
SetBkMode(hdc, OPAQUE);
}
else
{
SetBkMode(hdc, TRANSPARENT);
}

DrawText(hdc, szTitle, -1, prc, DT_WORDBREAK);

wsprintf(szSize, "{%d, %d, %d, %d}", prc->left, prc->top, prc->right,


prc->bottom);
DrawText(hdc, szSize, -1, prc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

SelectObject(hdc, hfOld);

First thing we do is use SelectObject() to get the font we want to use into our HDC and
ready for drawing. All future text operations will use this font untill another one is selected
in.

Next we set the Text and Background colours. Setting the background colour doesn't actually
make the whole background this colour, it only affects certain operations (text being one of
them) that use the background colour to draw with. This is also dependant on the current
Background Mode. If it is set to OPAQUE (the default) then any text drawn is filled in behing
with the background colour. If it is set to TRANSPARENT then text is drawn without a
background and whatever is behind will show through and in this case the background colour
has no effect.

Now we actually draw the text using DrawText(), we pass in the HDC to use and the string to
draw. The 3rd parameter is the length of the string, but we've passed -1 because DrawText()
is smart enough that it will figure out how long the text is itself. In the 4th parameter we pass
in prc, the pointer to the client RECT. DrawText() will draw inside this rectangle based on the
other flags that you give it.

In the first call, we specify DT_WORDBREAK, which defaults to aligned to the top left, and will
wrap the text it draws automatically at the edge of the rectangle... very useful.

For the second call, we're only printing a single line without wrapping, and we want it to be
centered horizontally as well as vertically (which DrawText() will do only when drawing a
single line).

Client Redraw

Just a note about the example program... when the WNDCLASS is registered I have set the
CS_VREDRAW and CS_HREDRAW class styles. This causes the entire client area to be redrawn if
the window is resized, whereas the default is to only redraw the parts that have changed. That
looks really bad since the centered text moves around when you resize and it doesn't update
like you'd expect.

Choosing Fonts
In general, any program that deals with fonts will want to let the user choose their own font,
as well as the colour and style attribute to use when displaying it.

Like the common dialogs for getting open and save file names, there is a common dialog for
choosing a font. This is, oddly enough, called ChooseFont() and it works with the
CHOOSEFONT structure for you to set the defaults it should start with as well as returning the
final result of the users selection.

HFONT g_hfFont = GetStockObject(DEFAULT_GUI_FONT);


COLORREF g_rgbText = RGB(0, 0, 0);
void DoSelectFont(HWND hwnd)
{
CHOOSEFONT cf = {sizeof(CHOOSEFONT)};
LOGFONT lf;

GetObject(g_hfFont, sizeof(LOGFONT), &lf);

cf.Flags = CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;


cf.hwndOwner = hwnd;
cf.lpLogFont = &lf;
cf.rgbColors = g_rgbText;

if(ChooseFont(&cf))
{
HFONT hf = CreateFontIndirect(&lf);
if(hf)
{
g_hfFont = hf;
}
else
{
MessageBox(hwnd, "Font creation failed!", "Error", MB_OK |
MB_ICONEXCLAMATION);
}

g_rgbText = cf.rgbColors;
}
}
The hwnd in this call is simply the window you want to use as the parent for the font dialog.

The easiest way to use this dialog is in conjunction with an existing LOGFONT structure, which
is most likely from whichever HFONT you are currently using. We set the lpLogFont member
of the structure to point to the LOGFONT that we just filled with our current information and
also added the CF_INITTOLOGFONTSTRUCT flag so that ChooseFont() knows to use this
member. The flag CF_EFFECTS tells ChooseFont() to allow the user to select a colour, as
well as Underline and Strikeout attributes.

Oddly enough, the Bold and Italics styles don't count as effects, they are considered part of
the font itself and in fact some fonts only come in Bold or Italics. If you want to check or
prevent the user from selecting a bold or italic font you can check the lfWeight and
lfItalic members of the LOGFONT respectively, after the user has made their selection. You
can then prompt the user to make another selection or something change the members before
calling CreateFontIndirect().

The colour of a font is not associated with an HFONT, and therefor must be stored seperately,
the rgbColors member of the CHOOSEFONT struct is used both to pass in the initial colour and
retreive the new colour afterward.

CF_SCREENFONTS indicates that we want fonts designed to work on the screen, as opposed to
fonts that are designed for printers. Some support both, some only one or the other.
Depending on what you're going to be using the font for, this and many other flags can be
found in MSDN to limit exactly which fonts you want the user to be able to select.

Choosing Colours
In order to allow the user to change just the colour of the font, or to let them pick a new
colour for anything at all, there is the ChooseColor() common dialog. This is the code used
to allow the user to select the background colour in the example program.
COLORREF g_rgbBackground = RGB(255, 255, 255);
COLORREF g_rgbCustom[16] = {0};
void DoSelectColour(HWND hwnd)
{
CHOOSECOLOR cc = {sizeof(CHOOSECOLOR)};

cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR;


cc.hwndOwner = hwnd;
cc.rgbResult = g_rgbBackground;
cc.lpCustColors = g_rgbCustom;

if(ChooseColor(&cc))
{
g_rgbBackground = cc.rgbResult;
}
}

This is fairly straightforward, again we're using the hwnd parameter as the parent to the
dialog. The CC_RGBINIT parameter says to start off with the colour we pass in through the
rgbResult member, which is also where we get the colour the user selected when the dialog
closes.

The g_rgbCustom array of 16 COLORREFs is required to store any values the user decides to
put into the custom colour table on the dialog. You could potentially store these values
somewhere like the registry, otherwise they will simply be lost when your program is closed.
This parameter is not optional.

Control Fonts
Something else you might want to do at some point is change the font on the controls on your
dialog or window. This is usually the case when using CreateWindow() to create controls as
we've done in previous examples. Controls like windows use System by default, so we used
WM_SETFONT to set a new font handle (from GetStockObject()) for the control to use. You
can use this method with fonts you create from CreateFont() as well. Simply pass the font
handle as wParam and set lParam to TRUE to make the control redraw.

I've done this in previous examples, but it makes sense to mention it here because it's relevant
and very short:

SendDlgItemMessage(hwnd, IDC_OF_YOUR_CONTROL, WM_SETFONT,


(WPARAM)hfFont, TRUE);

Where hfFont is of course the HFONT you want to use, and IDC_OF_YOUR_CONTROL is the ID
of whichever control you want to change the font of.

Books
If you expect anyone online to treat you with respect while you are learning, you NEED to
get a good book to learn from. We're here to provide direction and explain things that need
explaining, not to be your librarian or teach you step by step.

You can find more recommended books and links to buy at the #Winprog Store.

Programming Windows
by Charles Petzold. The book to get on Win32 API. If you want to write programs
using just the API (which is what this tutorial covers), you need this book.
Programming Windows with MFC
by Jeff Prosise. If you want to venture into MFC (AFTER becoming fully
accustomed to using the Win32 API), this is the book for you. If you don't like MFC
but intend on getting a job doing windows developement, get this anyway, it's better
to know than not.
Programming Applications for Windows
by Jeffrey Richter. Not for newbies, if you want to be up on managing processes and
threads, dlls, windows memory management, exception handling, and hooking into
the system, then this is the book for you.
Visual C++ Windows Shell Programming
by Dino Esposito. For anyone interested in the visual and user-friendly aspects of
windows, this book covers writing extentions to the windows shell, working
efficiently with files and drag and drop, customizing the taskbar and windows
explorer, and numerous other tricks. Well worthwhile for anyone writing GUI apps in
windows.
Network Programming for Microsoft Windows
Up to date information on network programming, including NetBIOS, mailslots and
pipes, and of course the ever important windows sockets, complete with winsock2 and
raw sockets. Also contains specific information on the various windows platforms
including 2000 and CE.

Links
MSDN Online
This site has references for all imaginable Microsoft technologies, including full
Win32 API and MFC documentation. If this didn't come with your compiler (ie.
VC++) then the completely free online site will provide you with the required
information. People will get really pissed off if you ask questions you could answer
by doing a simple search on MSDN.
#winprog homepage
See FAQ and Store

Getting It
After version 2.0 of the tutorial was written, Microsoft has subsequently made it so easy to
download their C++ development environment that it's no longer even worth writing
instructions for.

Download Free VC++ 2008

This should far and away be your first choice for a Win32 development environment. It's
free, it's complete, it is superbly documented by Microsoft and third party authors, and is
widely supported by the Win32 community.

From the Visual Studio Express FAQ: You are even allowed to use it for commercial
purposes.

24. Solutions to Common Errors


 Error LNK2001: unresolved external symbol _main
 Error C2440: cannot convert from 'void*' to 'HICON__ *' (or similar)
 Fatal error RC1015: cannot open include file 'afxres.h'
 Error LNK2001: unresolved external symbol InitCommonControls
 Dialog does not display when certain controls are added

Error LNK2001: unresolved external symbol _main


An unresolved external occurs when some code has a call to a function in another module and
the linker can't find that function in any of the modules or libraries that you are currently
linking to.

In this specific case, it means one of two things. Either you are trying to write a Win32 GUI
application (or non-console application) and accidently compiled it as a Console application...
or you really are trying to compile a console application and didn't write or properly compile
in a main() function.

Generally the first is the most common, if you specify Win32 Console as the project type in
VC++ when you create your project you will get this error. You will also likely get it if you
try to compile from the command line using BC++ but you neglect to specify the correct
parameters to tell it to make a Win32 GUI application instead of a console app which is the
default.

Fixing

If you're using VC++ re-create your project and select the Win32 Application project type
(NOT "Console").

If you're using BC++ command line compiler, use -tW to specify a windows application.

Error C2440: cannot convert from 'void*' to 'HICON__ *'


(or similar)
If you're compiling the code from this tutorial, it means that you are trying to compile it as
C++ code. The code is written for the bcc32 and VC++ C compilers, and as such may not
compile exactly the same under C++ since C++ has much stricter rules about converting
types. C will just let it happen, C++ wants to you to make it explicit.

VC++ (and most compilers) will automatically compile a file with a .cpp extension as C++
code, and a file with a .c extension as C code. If you have added the tutorial code to a .cpp
file, this is the most likely reason of getting this error.

If you're compiling code not from this tutorial, I can't guarantee that it's correct and therefor it
may actually be an error that needs resolving. You'll have to use your own judgement to
determine if it's safe to cast the value and remove the error, or if you are actually trying to
make a variable be something it's not.

Fixing
If you want to use C, simply rename your file from .cpp to .c. Otherwise, simply add a cast,
all of the code in the tutorial will work without any other changes when compiled as C++.

For example, in C this will work:

HBITMAP hbmOldBuffer = SelectObject(hdcBuffer, hbmBuffer);

But in C++ requires a cast:

HBITMAP hbmOldBuffer = (HBITMAP)SelectObject(hdcBuffer, hbmBuffer);

Fatal error RC1015: cannot open include file 'afxres.h'.


Oddly enough, VC++ adds afxres.h to resource files even when you aren't using an MFC
project, and yet the file may only be installed if you install MFC. This perticular file isn't
actually required, so to fix the error you can edit the .rc file in notepad and replace both
occurances of "afxres.h" with "winres.h" (note that there should be two of them, and you
need to change both).

Error LNK2001: unresolved external symbol


InitCommonControls
You aren't linking to comctl32.lib which this API is defined in. This library is not included by
default so you will either need to add it to the libraries on your command line, or add it in
your VC++ project settings on the Link tab.

Dialog does not display when certain controls are added


Controls such as the ListView, TreeView, Hotkey, Progress Bar, and others are classified as
Common Controls, as they were added to windows in comctl32.dll and were not available
prior to Windows 95. Controls such as BUTTON, EDIT, LISTBOX, etc... while no doubt
being common, are not "Common Controls" and I generally refer to them as "Standard
Controls".

If you add a Common Control to a dialog and it fails to display, you most likely failed to call
InitCommonControls() before running your dialog, or perhaps at all. The best place to call
it is first thing in WinMain(). Calling it in WM_INITDIALOG is too late, since the dialog will
fail before it reaches this point and it will never get called.

Some people and documentation may tell you that InitCommonControls() is deprecated and
you should use InitCommonControlsEx(). Feel free to do this if you want,
InitCommonControls() is just simpler and there's nothing wrong with using it.
25. Why you should learn the API
before MFC
The Controversy
Too many people come on to IRC and ask "What is better, MFC or API?" and too many
people are willing to say "MFC sucks" or "API sucks" either because of traumatic events
involving one or the other in early childhood, or because everyone else is saying it.

The standard arguments are:

 API is too hard


 MFC is too confusing
 API is too much code
 MFC is bloated
 API doesn't have wizards
 MFC is badly designed
 API isn't Object Oriented
 MFC kicked my dog
 API stole my girlfriend

And so on...

My Answer
My opinion, although by no means the only one, is that you should use the right framework
for the right job.

First of all a clarification on what the API and MFC are. API is a generic term meaning
Application Programming Interface, however in the context of Windows programming, it
means specifically the Windows API, which is the lowest level of interaction between
applications and the windows operating system. Drivers of course have even lower levels,
and different sets of function calls to work with, but for the vast majority of windows
development this is not an issue. MFC is a Class Library, it's a bunch of C++ classes that
have been written to reduce the amount of work it takes to do certain things with the API. It
also introduces an (arguably) Object Oriented framework into the application that you can
either take advantage of or ignore, which is what most beginners do since the framework isn't
really aimed at writing MP3 players, IRC clients or games.

Every program, whether it is written with MFC, Delphi, Visual Basic, perl, or any other
wacked out language or framework you can think of, is eventually built upon the API. In
many cases this interaction is hidden, so you don't deal directly with the API, the runtime and
support libraries do it for you. Some people ask, "MFC can do Blah Blah Blah, can the API?"
The answer is that MFC can only do what the API can do, because it's built on top of it.
However doing things yourself with the API may take considerably more code than using the
pre-written MFC classes.
So what is the right framework? For starters, for people that are just learning to program, I
strongly believe that you should work with the API untill you are comfortable with the way
windows applications work and you understand all of the basic mechanics behind things like
the message loop, GDI, controls, and maybe even multithreading and sockets. This way you
will understand the fundamental building blocks of all windows applications, and can apply
this common knowledge to MFC, Visual Basic, or whatever other framework you choose to
work with later. It's also important because these other frameworks don't support everything
that the API does, simply because it does a whole lot and they can't necessarily support all of
the arcane little things that most people won't use. So when you finally do need to use them
you need to add it yourself, you can't rely on the framework to do it for you and if you don't
understand the API this could be quite the chore.

But isn't MFC easier? In a certain sense it's easier in that many common tasks are done for
you, thus reducing the amount of code that you need to actually type. However, less code
does not mean "easier" when you don't understand the code you DO need to write, or how all
of the code that is there to support you actually works. Generally beginners who use the
wizards to start there applications have no idea what most of the generated code does, and
spend a great deal of time trying to figure out where to add things, or what changes to make
to acheive a certain result. If you start your programs from scratch, either in the API or with
MFC, then you know where everything is because you put it there, and you will only use
features that you understand.

Another important factor is that most people that are learing the Win32 API for the first time
don't already have a strong base in C++. To try and comprehend windows programming with
MFC and learn C++ at the same time can be a monumental task. Although it's not impossible,
it will take you considerably longer to become productive than if you already knew either
C++ or the API.

So basically...
What it comes down to is that I think you should learn the API untill you feel comfortable
with it, and then try out MFC. If it seems like it's making sense to you and saving you time,
then by all means use it.

However, and this is important... if you work with MFC without understanding the API and
then ask for help with something, and the answer you get is stated using the api (such as "Use
the HDC provided in the WM_CTLCOLORSTATIC message") and you say "huh?" because
you don't know how to translate an API subject into MFC on your own, then you are in
trouble and people will get frustrated with you for not learning what you need to know before
you try and use MFC.

I personally prefer to work with the API, it just suits me better, but if I were to write a
database frontend, or a host for a set of ActiveX controls I would seriously consider using
MFC, as it would eliminate a lot of code that I would need to reinvent otherwise.

Copyright © 1998-2008, Brook Miles (forgey). All rights reserved.

Вам также может понравиться