Вы находитесь на странице: 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

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

Practical

Box Creation

65

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

14.

Menus and Icons using resource

86

Creating menu using resource script

86

Defining id numbers

87

Processing the Messages generated by menu

88

Creating menu using child

88

Example: menu_two

88

QUESTIONS

95

15. Dialogs Box Using Resource

96

16. Dialogs Using Resource

Modeless

104

17. Controls

Windows

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

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

in the appendix to provide some notes on using the compilers I have knowledge of.

)

I will however take a few moments

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

Names of WindowsParts
Names of WindowsParts
The simplest Win32 program If you are a complete beginner lets make sure you are
The simplest Win32 program If you are a complete beginner lets make sure you are
The simplest Win32 program If you are a complete beginner lets make sure you are

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

Set select Use Muti- Byte Character Set. Solution #1 Solution #2 #include <windows.h> int WINAPI

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

MessageBoxAstatement.

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.

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:

Flag (Boolean, logical). If qualifier is used, it should describe the true state of the flag. Exception: the constants fTrue and fFalse.

Word with arbitrary contents.

lp: long integer pointer

wcx: WNDCLASSEX structure.

ch: Character, usually in ASCII text.

b:

f:

w:

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:

Hpp: heap structure

Pointer to a string. First byte is the count of characters cch.

unsigned integer

u:

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.

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

Run the program at the command prompt with a parameter Computer System & Multimedia as above. The output will be as follow.

& Multimedia as above. The output will be as follow. Question 1. Is Hungarian Notation mandatory

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

}

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

WndClsEx.style

WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0; WndClsEx.hInstance = hInstance;

= sizeof(WNDCLASSEX); = CS_HREDRAW | CS_VREDRAW;

}

Window Styles

WS_BORDER

WS_CAPTION

Creates a window that has a border.

Creates a window that has a title bar (implies the WS_BORDER style). Cannot be

used with the WS_DLGFRAME style.

WS_CHILD

WS_CHILDWINDOW

WS_CLIPCHILDREN

Creates a child window. Cannot be used with the WS_POPUP style.

Same as the WS_CHILD style.

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

WS_DLGFRAME

WS_GROUP

Creates a window that is initially disabled.

Creates a window with a double border but no title.

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

WS_ICONIC

WS_MAXIMIZE

WS_MAXIMIZEBOX

WS_MINIMIZE

Creates a window that has a horizontal scroll bar.

Creates a window that is initially minimized. Same as the WS_MINIMIZE style.

Creates a window of maximum size.

Creates a window that has a Maximize button.

Creates a window that is initially minimized. For use with the WS_OVERLAPPED

style only.

WS_MINIMIZEBOX

WS_OVERLAPPED and a border.

WS_OVERLAPPEDWINDOW

Creates a window that has a Minimize button.

Creates an overlapped window. An overlapped window usually has a caption

Creates an overlapped window with the WS_OVERLAPPED,

WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles.

WS_POPUP

WS_POPUPWINDOW

Creates a pop-up window. Cannot be used with the WS_CHILD style.

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

WS_SYSMENU

Creates a window that has a sizing border. Same as the WS_THICKFRAME style.

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

WS_TILED

Creates a window with a thick frame that can be used to size the window.

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

WS_VSCROLL

Creates a window that is initially visible.

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

WndClsEx.style

= sizeof(WNDCLASSEX); = 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_APPLICATION

IDI_INFORMATION

IDI_INFORMATION

IDI_ASTERISK

IDI_ASTERISK

IDI_QUESTION

IDI_QUESTION

IDI_WARNING

IDI_WARNING

IDI_EXCLAMATION

IDI_EXCLAMATION

IDI_HAND

IDI_HAND

IDI_ERROR

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

WndClsEx.style

= sizeof(WNDCLASSEX); = 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

WndClsEx.style

= sizeof(WNDCLASSEX); = 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

IDC_APPSTARTING Used to show that something undetermined is going on or the application is not stable

Used to show that something undetermined is going on or the application is not stable

IDC_ARROW

IDC_ARROW This standard arrow is the most commonly used cursor

This standard arrow is the most commonly used cursor

IDC_CROSS

IDC_CROSS The crosshair cursor is used in various circumstances such as drawing

The crosshair cursor is used in various circumstances such as drawing

IDC_HAND

IDC_HAND The Hand is standard only in Windows 2000. If you are using a previous operating

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

IDC_HELP The combined arrow and question mark cursor is used when providing help on a specific

The combined arrow and question mark cursor is used when providing help on a specific item on a window object

IDC_IBEAM

IDC_IBEAM The I-beam cursor is used on text-based object to show the position of the caret

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

IDC_NO This cursor can be used to indicate an unstable situation

This cursor can be used to indicate an unstable situation

IDC_SIZE

 

This cursor is not used anymore

IDC_SIZEALL

IDC_SIZEALL The four arrow cursor pointing north, south, east, and west is highly used to indicate

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

IDC_SIZENESW The northeast and southwest arrow cursor can be used when resizing an object on both

The northeast and southwest arrow cursor can be used when resizing an object on both the length and the height

IDC_SIZENS

IDC_SIZENS The north - south arrow pointing cursor can be used when shrinking or heightening an

The north - south arrow pointing cursor can be used when shrinking or heightening an object

IDC_SIZENWSE

IDC_SIZENWSE The northwest - southeast arrow pointing cursor can be used when resizing an object on

The northwest - southeast arrow pointing cursor can be used when resizing an object on both the length and the height

IDC_SIZEWE

IDC_SIZEWE The west - east arrow pointing cursor can be used when narrowing or enlarging an

The west - east arrow pointing cursor can be used when narrowing or enlarging an object

IDC_UPARROW

IDC_UPARROW The vertical arrow cursor can be used to indicate the presence of the mouse or

The vertical arrow cursor can be used to indicate the presence of the mouse or the caret

IDC_WAIT

IDC_WAIT The Hourglass cursor is usually used to indicate that a window or the application is

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

WndClsEx.style

WndClsEx.lpfnWndProc = WndProcedure; WndClsEx.cbClsExtra = 0;

= sizeof(WNDCLASSEX); = CS_HREDRAW | CS_VREDRAW;

WndClsEx.cbWndExtra = 0;

}

WndClsEx.hIcon

WndClsEx.hCursor

WndClsEx.hInstance

WndClsEx.hIconSm

return 0;

= LoadIcon(NULL, IDI_APPLICATION); = LoadCursor(NULL, IDC_ARROW); = hInstance; = LoadIcon(NULL, IDI_APPLICATION);

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

WndClsEx.style

WndClsEx.lpfnWndProc

WndClsEx.cbClsExtra

WndClsEx.cbWndExtra

WndClsEx.hIcon

= sizeof(WNDCLASSEX); = CS_HREDRAW | CS_VREDRAW; = WndProcedure; = 0; = 0; = LoadIcon(NULL, IDI_APPLICATION);

}

WndClsEx.hCursor

WndClsEx.hbrBackground = GetStockObject(WHITE_BRUSH);

WndClsEx.hInstance

WndClsEx.hIconSm

= LoadCursor(NULL, IDC_ARROW);

= hInstance; = 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

WndClsEx.style

= sizeof(WNDCLASSEX); = 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

WndClsEx.style

= sizeof(WNDCLASSEX); = 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

shortcuts, and this parameter is how the choice is carried out.

You will find options for these in the properties of windows

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

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

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 nonGUI 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.

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

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

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.

ask the user to save files etc. before exiting the program. or types Alt-F4. This will

or types Alt-F4. This will cause

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,

hInstance, // application instance

NULL);// window creation data

// handle to menu

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.

windows class e. Has a horizontal and vertical scroll bar. 2. And when you exit use

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

RegisterClassEx Function.

WNDCLASS structure registered by

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