Win32_API_tutorials

July 20, 2017 | Author: Sam Xin Min | Category: Computer Programming, Computer Data, Software, Software Engineering, Technology
Share Embed Donate


Short Description

Download Win32_API_tutorials...

Description

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 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 MutiByte Character Set. Solution #1

Solution #2 #include 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 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 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 #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 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-toplatform, 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 #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. ii. iii. iv. v. vi.

a. b. c. d. e. f. g. h.

hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped szFileName

Which indentifier are long integer pointers? Which indentifier(s) is (are) meant to functions as counter(s)? Which indentifier(s) is (are) meant to function()s to hold a handle of a file? Which identifier is a pointer to a zero terminated string: Which identifier(s) is (are) a pointer to a filename string? Which identifier(s) is (are) a variable(s)? Which identifier(s) is (are) an integer variable? 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 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;

LPCTSTR

lpszClassName;

HICON

hIconSm;

} WNDCLASS, *PW

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

= hInstance;

}

Window Styles  

WS_BORDER

  

WS_CHILD



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_HSCROLL

 

WS_MINIMIZEBOX



WS_OVERLAPPEDWINDOW Creates an overlapped window with the WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles.

 

WS_POPUP

 

WS_SIZEBOX



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

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_ICONIC

Creates a window that has a horizontal scroll bar. Creates a window that is initially minimized. Same as the WS_MINIMIZE style.

WS_MAXIMIZE

Creates a window of maximum size.

WS_MAXIMIZEBOX WS_MINIMIZE style only.

Creates a window that has a Maximize button.

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

WS_OVERLAPPED and a border.

Creates a window that has a Minimize button. Creates an overlapped window. An overlapped window usually has a caption

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

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_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 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 nullterminated 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 nullterminated 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 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. Say that you have successfully registerd a window class. Can the classname be reuse again in the program when you create a child window. .

3.

4. 5. 6. 7.

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, Will GetMessage() pick up the message generated when you left-click on the close button while running SimpleWindow above? Ans: Yes Will TranslateMessage() pick up the message generated when you left-click on the close button while running SimpleWindow above? Ans: No Will if any of these event be processed by the TranslateMessage function: a. Any mouse button click b. Any key press. Ans: Yes

8. 9. 10. 11. 12. 13. 14. 15. 16.

4.

Is WM_KEYDOWN and and WM_KEYUP message generated as the windows system-defined message.Ans: Yes Is the WM_CLOSE message a system-defined message. Ans: Yes Is the WM_COMMAND message a system-defined message. Ans: Yes Is the message created by sendmessage() a system-defined message. Ans: No Is the message created by sendmessage() a application-defined message. Ans: Yes 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 How do Window Procedure retrieve windows system-defined message. Ans: The wParam of the MSG structure How do Window Procedure retrieve windows system-defined message. Ans: The wParam of the MSG structure On what message do PostQuitMessage function response to? Ans: WM_DESTROY message.

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 o o

WM_LBUTTONDBLCLK WM_LBUTTONDOWN WM_LBUTTONUP

o o o o o o o

WM_MBUTTONDBLCLK WM_MBUTTONDOWN WM_MBUTTONUP WM_MOUSEMOVE WM_RBUTTONDBLCLK WM_RBUTTONDOWN 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: // 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 keystate 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 o o o o







 



CB_GETLBTEXTLEN CB_INSERTSTRING CB_RESETCONTENT CB_SETCURSEL 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 o o o o

 

7.

WM_MBUTTONUP WM_MOUSEMOVE WM_RBUTTONDBLCLK WM_RBUTTONDOWN WM_RBUTTONUP Timers o WM_TIMER Windows o WM_CLOSE o WM_GETTEXT o WM_GETTEXTLENGTH o WM_HELP o WM_SETTEXT

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 nullterminated 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 doublequotes. 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 MB_OK MB_OKCANCEL MB_ABORTRETRYIGNORE MB_YESNOCANCEL MB_YESNO MB_RETRYCANCEL MB_CANCELTRYCONTINUE MB_HELP

Buttons

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 MB_ICONWARNING

Warning the user of an action performed on the application

MB_ICONINFORMATION MB_ICONASTERISK

Informing the user of a non-critical situation

MB_ICONQUESTION

MB_ICONSTOP MB_ICONERROR MB_ICONHAND

Asking a question that expects a Yes or No, or a Yes, No, or Cancel answer A critical situation or error has occurred. This icon is appropriate when informing the user of a 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: Value

If the message box has more than one button, 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 //--------------------------------------------------------------------------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 11.

9.

GTest the application

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

Combo Box

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

ComboBoxEx

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

Date and Time Picker

A simple and intuitive interface through which to exchange date and time information with a user.

Drag List Box

A type of list box that enables the user to drag items from one position to another.

Edit

A window within the user can view and edit text.

Flat Scroll Bar

A scroll bar with a more customizable appearance than standard scroll bars.

Header

A window that is usually positioned above columns of text or numbers. It contains a title for each column, and it can be divided into parts.

Hot Key

A window that enables the user to enter a combination of keystrokes to be used as a hot key.

Image Lists

A collection of images of the same size, each of which can be referred to by its index.

IP Address

A window in which the user can enter an Internet Protocol (IP) address in 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. Pager

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

Progress Bar

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

Property Sheet

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

ReBar

A container for child windows. An application assigns child windows, which are often other controls, to a rebar control band.

Rich Edit

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

Scroll Bar

A control that enables the user to choose the direction and distance to scroll information in a related window.

Static

Non-interactive text, including labels for other controls.

Status Bar

A horizontal window at the bottom of a parent window in which an application can display various kinds of status information.

SysLink

A hypertext link.

Tab

A selectable page, analogous to a divider in a notebook. By using a tab control, an application can define multiple pages for the same area of a window or dialog box.

Task Dialog

A more powerful alternative to simple message boxes, incorporating elements such as custom buttons, radio buttons, hyperlinks, and progress bars.

Toolbar

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

Tooltip

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

Trackbar

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

Tree-View

A hierarchical list of items, such as the headings in a document or the files and directories on a disk.

Up-Down

A pair of arrow buttons that the user can click to increment or decrement a 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 Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const

BS_3STATE = &H5 BS_AUTO3STATE = &H6 BS_AUTOCHECKBOX = &H3 BS_AUTORADIOBOX = &H9 BS_BITMAP = &H80 BS_BOTTOM = &H800 BS_CENTER = &H300 BS_CHECKBOX = &H2 BS_DEFPUSHBUTTON = &H1 BS_FLAT = &H8000 BS_GROUPBOX = &H7 BS_ICON = &H40 BS_LEFT = &H100 BS_LEFTTEXT = &H20 BS_MULTILINE = &H2000 BS_NOTIFY = &H4000 BS_OWNERDRAW = &HB BS_PUSHBUTTON = &H0 BS_PUSHLIKE = &H1000 BS_RADIOBUTTON = &H4

Const Const Const Const Const Const

BS_RIGHT = &H200 BS_RIGHTBUTTON = &H20 BS_TEXT = &H0 BS_TOP = &H400 BS_USERBUTTON = &H8 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 Const Const Const Const Const Const Const Const Const Const Const Const

CBS_AUTOHSCROLL = &H40 CBS_DISABLENOSCROLL = &H800 CBS_DROPDOWN = &H2 CBS_DROPDOWNLIST = &H3 CBS_HASSTRINGS = &H200 CBS_LOWERCASE = &H4000 CBS_NOINTEGRALHEIGHT = &H400 CBS_OEMCONVERT = &H80 CBS_OWNERDRAWFIXED = &H10 CBS_OWNERDRAWVARIABLE = &H20 CBS_SIMPLE = &H1 CBS_SORT = &H100 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 Const Const Const Const Const Const Const Const Const Const Const Const

ES_AUTOHSCROLL = &H80 ES_AUTOVSCROLL = &H40 ES_CENTER = &H1 ES_LEFT = &H0 ES_LOWERCASE = &H10 ES_MULTILINE = &H4 ES_NOHIDESEL = &H100 ES_NUMBER = &H2000 ES_OEMCONVERT = &H400 ES_PASSWORD = &H20 ES_READONLY = &H800 ES_RIGHT = &H2 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 Const Const Const Const Const Const Const Const Const Const Const

LBS_DISABLENOSCROLL = &H1000 LBS_EXTENDEDSEL = &H800 LBS_HASSTRINGS = &H40 LBS_MULTICOLUMN = &H200 LBS_MULTIPLESEL = &H8 LBS_NODATA = &H2000 LBS_NOINTEGRALHEIGHT = &H100 LBS_NOREDRAW = &H4 LBS_NOSEL = &H4000 LBS_NOTIFY = &H1 LBS_OWNERDRAWFIXED = &H10 LBS_OWNERDRAWVARIABLE = &H20

Const Const Const Const

LBS_SORT = &H2 LBS_STANDARD = &HA00006 LBS_USETABSTOPS = &H80 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 Const Const Const Const Const Const Const Const Const

SBS_BOTTOMALIGN = &H4 SBS_HORZ = &H0 SBS_LEFTALIGN = &H2 SBS_RIGHTALIGN = &H4 SBS_SIZEBOX = &H8 SBS_SIZEBOXBOTTOMRIGHTALIGN = &H4 SBS_SIZEBOXTOPLEFTALIGN = &H2 SBS_SIZEGRIP = &H10 SBS_TOPALIGN = &H2 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 Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const

SS_BITMAP = &HE SS_BLACKFRAME = &H7 SS_BLACKRECT = &H4 SS_CENTER = &H1 SS_CENTERIMAGE = &H200 SS_ENDELLIPSIS = &H4000 SS_ENHMETAFILE = &HF SS_ETCHEDFRAME = &H12 SS_ETCHEDHORZ = &H10 SS_ETCHEDVERT = &H11 SS_GRAYFRAME = &H8 SS_GRAYRECT = &H5 SS_ICON = &H3 SS_LEFT = &H0 SS_LEFTNOWORDWRAP = &HC SS_NOPREFIX = &H80 SS_NOTIFY = &H100 SS_OWNERDRAW = &HD SS_PATHELLIPSIS = &H8000 SS_REALSIZEIMAGE = &H800 SS_RIGHT = &H2 SS_RIGHTJUST = &H400 SS_SIMPLE = &HB SS_SUNKEN = &H1000 SS_WHITEFRAME = &H9 SS_WHITERECT = &H6 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 Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const Const

WS_BORDER = &H800000 WS_CAPTION = &HC00000 WS_CHILD = &H40000000 WS_CHILDWINDOW = &H40000000 WS_CLIPCHILDREN = &H2000000 WS_CLIPSIBLINGS = &H4000000 WS_DISABLED = &H8000000 WS_DLGFRAME = &H400000 WS_GROUP = &H20000 WS_HSCROLL = &H100000 WS_ICONIC = &H20000000 WS_MAXIMIZE = &H1000000 WS_MAXIMIZEBOX = &H10000 WS_MINIMIZE = &H20000000 WS_MINIMIZEBOX = &H20000 WS_OVERLAPPED = &H0 WS_OVERLAPPEDWINDOW = &HCF0000 WS_POPUP = &H80000000 WS_POPUPWINDOW = &H80880000 WS_SIZEBOX = &H40000 WS_SYSMENU = &H80000 WS_TABSTOP = &H10000 WS_THICKFRAME = &H40000 WS_TILED = &H0 WS_TILEDWINDOW = &HCF0000 WS_VISIBLE = &H10000000 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 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 = wc.hInstance = wc.hbrBackground = wc.lpfnWndProc = wc.hCursor =

TEXT( "Static Control" ); hInstance ; GetSysColorBrush(COLOR_3DFACE); WndProc ; 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 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 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 #define ID_EDIT 1 #define ID_BUTTON 2 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static static static static

HWND hwndEdit; HWND hwndButton; int len; 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 #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 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 highorder 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 #include #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 = wc.hInstance = wc.hbrBackground = wc.lpfnWndProc = wc.hCursor =

TEXT("Application"); hInstance ; GetSysColorBrush(COLOR_3DFACE); WndProc ; 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. 2. 3. 4. 5. 6. 7. 8. 9.

Create an empty project using Multi-Byte Character Set named app_two. Create two.cpp, app_two.rc and resource.h under their relevant folder in the solution explorer. copy menu_one.cpp source code into app_two.cpp copy menu_one.rc source code into app_two.rc. copy resource.cpp from the menu_one header files folder into resource.cpp from the app_two.rc header files folder. 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. In app_two.cpp, create the case selection for Open, Read and Close sub-menu. Test the logic by using Messagebox Create the following structure and routine in app_two.cpp.

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 #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), HIWORD(lParam), TRUE);

// width of client area // height of client area // 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 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 #include #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 #include 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 wc.hInstance wc.hbrBackground wc.lpfnWndProc wc.hCursor

= = = = =

TEXT("Application"); hInstance ; GetSysColorBrush(COLOR_3DFACE); WndProc ; 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, SendMessage(hwndTrack, SendMessage(hwndTrack, SendMessage(hwndTrack, break;

TBM_SETRANGE, TRUE, MAKELONG(0, 750)); TBM_SETPAGESIZE, 0, 20); TBM_SETTICFREQ, 20, 0); TBM_SETPOS, TRUE, 150);

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 int int int

hdc; till; step, full; 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 #include #include 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 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.

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF