Professional Documents
Culture Documents
The Winapi is the source code interface that is used to create windows applications. In order to create Windows applications, we must download the platform sdk. The sdk (software development kit) contains header files, libraries, samples, documentation and tools that use the Winapi to develop applications. The Windows API is created for C and C++ programming languages. It is the most direct way to create Windows applications. The Windows API has four basic components.
The base services provide access to the fundamental resources on Windows. These include file systems, devices, processes, threads, registry or error handling. 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 User Interface provides functionality to create windows and controls. The Network services provide access to the network capabilities of the Windows OS.
Fundamentally, computers just deal with numbers. They store letters and other characters by assigning a number for each one. Before Unicode was invented, there were hundreds of different encoding systems for assigning these numbers. No single encoding could contain enough characters: for example, the European Union alone requires several different encodings to cover all its languages. Even for a single language like English no single encoding was adequate for all the letters, punctuation, and technical symbols in common use. These encoding systems also conflict with one another. That is, two encodings can use the same number for two different characters, or use different numbers for the same character. Any given computer (especially servers) needs to support many different encodings; yet whenever data is passed between different encodings or platforms, that data always runs the risk of corruption.
Program instance definition An instance of a program is a copy of an executable version of the program that has been written to the computer's memory. A program is a sequence of instructions that indicates which operations the computer should perform on a set of data. An executable version of a program, also called an executable program, is a version of a program that is understandable by a computer's central processing unit (CPU) and that is ready to run as soon as it is copied from storage (usually a hard disk drive) into memory. (This contrasts with the source code version of a program, which is the version as originally written by humans using a programming language, and before it has been translated by a specialized program called a compiler into object code.) The CPU is the main logic unit of a computer. An instance of a program is typically created by a user clicking on an icon (i.e., small image) on a GUI (graphical user interface) or by entering a command at the command line and then pressing the ENTER key. Instances of programs can also be created by other programs. Multitasking, which allows multiple programs to run seemingly simultaneously on the same computer, also allows multiple instances of a single program to run simultaneously, if the program allows it. However, sometimes it is desired that there be only a single instance of a program on a computer, and programming languages provide techniques that can be used to enforce this. An example of a situation in which it can be useful to run multiple instances of a single program is having several multiple Nautilus (i.e., the official file manager for the Gnome desktop in
Linux) windows open simultaneously in order to compare the contents of different directories. Another example is a remote server from which multiple users are accessing a single program. Each instance of a program is, by definition, a separate process, as a process is merely an instance of a program, and thus each instance has a unique PID (process identification number). The number of instances of a program existing on a computer at the current time can be easily confirmed with commands such as ps and pstree, which show processes and their PIDs. Finding all of the instances of a program can be useful in some situations, particularly when the program crashes (i.e., freezes or stops operating as expected) and thus needs to be terminated. The word instance also has other meanings in a computer context, and these are likewise consistent with its broader definition as one particular incarnation of an object or situation. For example, in object-oriented programming (e.g., Java and C++) every object is an instance of some class and it is created by instantiating a class. In Java programs, an instance of a class is created using the new operator followed by the class name. For example, an object called my cat or felix could be created as an instance of the class cat. Perhaps the best way to summarize the difference between programs and instances of them is the statement that programs reside on disk and instances reside in memory. An executable file, also called an executable or a binary, is the ready-to-run (i.e., executable) form of a program. A program is a sequence of instructions understandable by a computer's CPU (central processing unit) that indicates which operations the computer should perform on a set of data. A file is a named collection of related data that appears to the user as a single, contiguous block of data and that is retained in storage. Storage refers to computer devices or media which can retain data for relatively long periods of time (e.g., years or decades), such as disk drives and magnetic tape. This contrasts with memory, which which retains its contents only briefly and which physically consists of RAM (random access memory) chips. Executable files consist of instructions that have been translated from their original source code into machine code, also called machine language or object code through the use of a specialized program called a compiler so that the CPU can use them directly and without further translation. Machine code consists entirely of zeros and ones, which represent the off and on states of the CPU logic circuits and memory cells. The object code files and any other necessary files (e.g., library files) are then linked together using a linker to create the executable. Linkers are generally included in compilers, and the linking is performed automatically. Executable files are usually stored in one of several standard directories on the hard disk drive (HDD) on Unix-like operating systems, including /bin, /sbin, /usr/bin, /usr/sbin and /usr/local/bin. Although it is not necessary for them to be in these locations in order to be
operable, it is often more convenient. When a program is launched, its executable file is copied into memory by the operating system so that its machine code will be immediately available to the CPU. In operating systems in which the type of file is indicated by appending an extension after its name, executables are indicated by extensions such as .exe, .com or .bin. Such extensions are generally not necessary in Unix-like operating systems.
Handle
A handle can be anything from an integer index to a pointer to a resource in kernel space. The idea is that they provide an abstraction of a resource, so you don't need to know much about the resource itself to use it. For instance, the HWND in the Win32 API is a handle for a Window. By itself it's useless: you can't glean any information from it. But pass it to the right API functions, and you can perform a wealth of different tricks with it. Internally you can think of the HWND as just an index into the GUI's table of windows (which may not necessarily be how it's implemented, but it makes the magic make sense).
A handle is most definitely not an int on Windows. Windows doesn't use signed (ie, int) values for handles. A handle on a 64-bit version of Windows will be a 64-bit unsigned value, whereas upon 32-bit versions, it will be 32-bits unsigned. As far as an app is concerned, a Win32 handle is just a void *. In a nutshell, a Win32 handle is (typically) a pointer to a struct that Windows doesn't want you to directly manipulate. It's a struct that is meant to be passed to, and manipulated by, only operating system functions. For example, you call CreateWindow() and it returns a handle to a window. Your app refers to it as an HWND, which is typedef'ed to a void * in Windows.h for the benefit of apps. A pointer to what? You don't know. Only the Windows operating system knows. It's a "window struct". What's that? You don't know. What members are in this struct? You don't know. Only the operating system knows. What do you do with this handle? You don't do anything at all with it, except to pass it to a great many other operating system functions that require you to pass it, definitely know what it really is, what its members are, and directly read/write those members. What's the point of this? First of all, the operating system needs to allocate some sort of "window struct" (ie, memory area) to store your own app's settings. After all, you've got your own window, distinct from any other app's windows. Your own window has its own distinct width and height, and title bar text, and lots of other settings that are unique to your own window. There has to be somewhere to store these personal "window settings" of yours. So the windows operating system allocates this "window struct" to store your own window settings. Then, the operating system (ie, CreateWindow) returns a pointer to this struct. It's telling you "Save this pointer to something. Don't you dare directly access its contents. I'm not going to even tell you what is in this struct. Just save this pointer somewhere.
Then when you call some other OS function, such as SetWindowText to change your window's title bar text, pass this pointer to SetWindowText, and I, the operating system, can know which window we're talking about, and I can change whatever member stores the title bar text of your window". The other point of this is that: If in a future version of Windows, Microsoft decides they need to change the layout of this "window struct", it won't break older apps. After all, your program never actually touches the members of this struct. So what do you care if, tomorrow someone runs your app on a new version of Windows that redefines the contents of that window struct. You never touch its contents. All you do is pass it around to different OS functions. What concern is it of yours that now, every single OS function you pass this pointer to, is operating upon a slightly different "window struct" than those functions were yesterday? An HINSTANCE is really a pointer to an "instance struct". What are its members? Only Microsoft knows. All we know is that it has something to do with storing settings about your EXE. An HWND is really a pointer to a "window struct". Only MS knows what it really is. All we know is that it's something to do with storing settings about a particular window. An HFONT is a pointer to a "font struct". Only MS knows what it really is. All we know is that it's something to do with storing settings about a particular font. Etc. As far as you're concerned, they are all a void *. Hell, you could typedef HSOMETHING to be a void *, and call them all an HSOMETHING. So why didn't MS do that, instead of having hundreds of these H...whatevers? Because then you'd really get confused. You don't want to mistakenly pass that "font struct" (HFONT) to SetWindowText (in lieu of a pointer to a "window struct"). And you don't want to mistakenly pass a HWND to GetModuleFilename() (in lieu of passing a pointer to an "instance struct"). So think of all these H...whatever as MS's way of helping you distinguish these various pointers, without giving you any more information about them than you need -- specifically, not any information about the actual content of these structs.
One of the first things that is going to strike many first-time programmers of the Win32 API is that there are tons and tons of old data types to deal with. Sometimes, just keeping all the correct data types in order can be more difficult than writing a nice program. This page will talk a little bit about some of the data types that a programmer will come in contact with.
[edit] LPVOID
LPVOID data types are defined as being a "pointer to a void object". This may seem strange to some people, but the ANSI-C standard allows for generic pointers to be defined as "void*" types. This means that LPVOID pointers can be used to point to different types of objects, without creating a compiler error. However, the burden is on the programmer to keep track of what type of object is being pointed to. Also, some Win32 API functions may have arguments labeled as "LPVOID lpReserved". These reserved data members should never be used in your program, because they either depend on functionality that hasn't yet been implemented by Microsoft, or else they are only used in certain applications. If you see a function with an "LPVOID lpReserved" argument, you must always pass a NULL value for that parameter - some functions will fail if you do not do so. LPVOID objects frequently do not have prefixes, although it is relatively common to prefix an LPVOID variable with the letter "p", as it is a pointer.
always 32 bits long. Because of this strict definition, DWORDS are very common and popular on 32-bit machines, but are less common on 16-bit and 64-bit machines. WORDs (Single WORDs) are defined strictly as unsigned 16-bit values, regardless of what machine you are programming on. BYTEs are defined strictly as being unsigned 8-bit values. QWORDs (Quad WORDs), although rare, are defined as being unsigned 64-bit quantities. Putting a "P" in front of any of these identifiers indicates that the variable is a pointer. putting two "P"s in front indicates it's a pointer to a pointer. These variables may be unprefixed, or they may use any of the prefixes common with DWORDs. Because of the differences in compilers, the definition of these data types may be different, but typically these definitions are used:
#include <stdint.h>
Notice that these definitions are not the same in all compilers. It is a known issue that the GNU GCC compiler uses the long and short specifiers differently from the Microsoft C Compiler. For this reason, the windows header files typically will use conditional declarations for these data types, depending on the compiler being used. In this way, code can be more portable. As usual, we can define pointers to these types as:
#include <stdint.h>
uint8_t * PBYTE; uint16_t * PWORD; uint32_t * PDWORD; uint64_t * PQWORD; uint8_t ** PPBYTE; uint16_t ** PPWORD; uint32_t ** PPDWORD; uint64_t ** PPQWORD;
DWORD variables are typically prefixed with "dw". Likewise, we have the following prefixes:
Data Type Prefix BYTE WORD "b" "w"
DWORD QWORD
"dw" "qw"
LONG notation LONG variables are typically prefixed with an "l" (lower-case L). UINT notation UINT variables are typically prefixed with an "i" or a "ui" to indicate that it is an integer, and that it is unsigned. CHAR, UCHAR notation These variables are usually prefixed with a "c" or a "uc" respectively.
If the size of the variable doesn't matter, you can use some of these integer types. However, if you want to exactly specify the size of a variable, so that it has a certain number of bits, use the BYTE, WORD, DWORD, or QWORD identifiers, because their lengths are platformindependent and never change.
Most programmers will not define a variable as a STR, opting instead to define it as a character array, because defining it as an array allows the size of the array to be set explicitly. Also, creating a large string on the stack can cause greatly undesirable stack-overflow problems. LPSTR stands for "Long Pointer to a STR", and is essentially defined as such:
#define STR * LPSTR;
LPSTR can be used exactly like other string objects, except that LPSTR is explicitly defined as being ASCII, not unicode, and this definition will hold on all platforms. LPSTR variables will usually be prefixed with the letters "lpsz" to denote a "Long Pointer to a String that is Zeroterminated". The "sz" part of the prefix is important, because some strings in the Windows world (especially when talking about the DDK) are not zero-terminated. LPSTR data types, and variables prefixed with the "lpsz" prefix can all be used seamlessly with the standard library <string.h> functions.
[edit] TCHAR
TCHAR data types, as will be explained in the section on Unicode, are generic character data types. TCHAR can hold either standard 1-byte ASCII characters, or wide 2-byte Unicode characters. Because this data type is defined by a macro and is not set in stone, only character data should be used with this type. TCHAR is defined in a manner similar to the following (although it may be different for different compilers):
#ifdef UNICODE #define TCHAR WORD #else #define TCHAR BYTE #endif
These strings can be either UNICODE or ASCII, depending on the status of the UNICODE macro. LPTSTR data types are long pointers to generic strings, and may contain either ASCII strings or Unicode strings, depending on the environment being used. LPTSTR data types are also prefixed with the letters "lpsz".
[edit] HANDLE
HANDLE data types are some of the most important data objects in Win32 programming, and also some of the hardest for new programmers to understand. Inside the kernel, Windows
maintains a table of all the different objects that the kernel is responsible for. Windows, buttons, icons, mouse pointers, menus, and so on, all get an entry in the table, and each entry is assigned a unique identifier known as a HANDLE. If you want to pick a particular entry out of that table, you need to give Windows the HANDLE value, and Windows will return the corresponding table entry. HANDLEs are defined as being unsigned 32-bit quantities in <windows.h>, but HANDLEs should never be used like integers. They are unique identifiers, and if you edit them, or use them in arithmetic, then they can never be used to get the table entry that they correspond to. In other words, HANDLEs should be stored, but they should never be changed by the programmer. HANDLEs are generally prefixed with an "h". Below are a few special handles that are worth discussing: A handle is actually a pointer to a pointer to a memory location.Handles are unsigned integers that Windows uses internally to keep track of objects in memory. Windows moves objects like memory blocks in memory to make room, if the object is moved in memory, the handles table is updated.
[edit] HWND
HWND data types are "Handles to a Window", and are used to keep track of the various objects that appear on the screen. To communicate with a particular window, you need to have a copy of the window's handle. HWND variables are usually prefixed with the letters "hwnd", just so the programmer knows they are important. Canonically, main windows are defined as:
HWND hwnd;
Although you are free to name these variables whatever you want in your own program, readability and compatibility suffer when an idiosyncratic naming scheme is chosen - or worse, no scheme at all.
[edit] HINSTANCE
HINSTANCE variables are handles to a program instance. Each program gets a single instance variable, and this is important so that the kernel can communicate with the program. If you want to create a new window, for instance, you need to pass your program's HINSTANCE variable to the kernel, so that the kernel knows where the new window belongs to. If you want to communicate with another program, it is frequently very useful to have a copy of that program's
instance handle. HINSTANCE variables are usually prefixed with an "h", and furthermore, since there is frequently only one HINSTANCE variable in a program, it is canonical to declare that variable as such:
HINSTANCE hInstance;
It is usually a benefit to make this HINSTANCE variable a global value, so that all your functions can access it when needed.
[edit] HMENU
If your program has a drop-down menu available (as most visual Windows programs do), that menu will have an HMENU handle associated with it. To display the menu, or to alter its contents, you need to have access to this HMENU handle. HMENU handles are frequently prefixed with simply an "h".
The last argument, nCmdShow, controls how the window you are building will be displayed. An object that displays on your screen is called a window. Because there can be various types of windows in your programs, your first responsibility is to control them, know where they are, what they are doing, why, and when. The first control you must exercise on these different windows is to host them so that all windows of your program belong to an entity called the main window. This main window is created using an object that can be called a class (strictly, a structure). The Win32 library provides two classes for creating the main window and you can use any one of them. They are WNDCLASS and WNDCLASSEX. The second adds only a slight feature to the first. Therefore, we will mostly use the WNDCLASSEX structure for our lessons. The WNDCLASS and the WNDCLASSEX classes are defined as follows: typedef struct _WNDCLASS { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS, *PW typedef struct _WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX, *PWNDCLASSEX;
To create a window, you must "fill out" this class, which means you must provide a value for each of its members so the operating system would know what your program is expected to do. The first thing you must do in order to create an application is to declare a variable of either WNDCLASS or WNDCLASSEX type. Here is an example of a WNDCLASSEX variable: INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClsEx; return 0; }
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) {
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 #define WM_COMMAND #define WM_LBUTTONDOWN 0x0110 0x0111 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 sends 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.
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. 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.
HIWORD()
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.
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(). If you wanted, you could use GetWindowLong() on the window handle that the message is destined for to look up the window's procedure and call it directly!
while(GetMessage(&Msg, NULL, 0, 0) > 0) { WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC); fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam); }
I tried this with the previous example code, and it does work, however there are various issues such as Unicode/ANSI translation, calling timer callbacks and so forth that this method will not account for, and very likely will break all but trivial applications. So do it to try it, but don't do it in real code :) Notice that we use GetWindowLong() to retreive the window procedure associated with the window. Why don't we just call our WndProc() directly?
Well our message loop is responsible for ALL of the windows in our program, this includes things like buttons and list boxes that have their own window procedures, so we need to make sure that we call the right procedure for the window. Since more than one window can use the same window procedure, the first parameter (the handle to the window) is used to tell the window procedure which window the message is intended for. As you can see, your application spends the majority of it's 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 certian 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.
the message queue using the GetMessage() function and dispatch them to the window procedure using the DispatchMessage() call.
while(GetMessage(&Msg, NULL, 0, 0)>0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam;
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.
GetMessage()
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 sytem 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.
TranslateMessage()
This function is declared with a return type of LRESULT CALLBACK. The LRESULT type is used by Windows to declare a long integer, and CALLBACK is a calling convention used with functions that are called by Windows. The Windows Procedure is a function pointer, which allows you to call it whatever you
want because the function's address will be assigned as a function pointer upon creation of the window class. hwnd - Only important if you have several windows of the same class open at one time. This is used to determine which window hwnd pointed to before deciding on an action. message - The actual message identifier that WndProc will be handling. wParam(word-length parameter) and lParam(long length parameter) Extensions of the message parameter. Used to give more information and point to specifics that message cannot on its own. Message parameters often contain information in both their low-order and high-order words. There are several macros an application can use to extract information from the message parameters. The LOWORD macro, for example, extracts the low-order word (bits 0 through 15) from a message parameter. Other macros include HIWORD, LOBYTE, and HIBYTE. Message Processing
The name of the window procedure we reviewed in the previous lesson must be assigned to the lpfnWndProc member variable of the WNDCLASS or WNDCLASSEX variable. This can be defined as follows: #include <windows.h> LRESULT WndProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClsEx; WndClsEx.cbSize WndClsEx.style WndClsEx.lpfnWndProc WndClsEx.cbClsExtra WndClsEx.cbWndExtra WndClsEx.hInstance return 0; } LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { switch(Msg) { case WM_CLOSE: = = = = = = sizeof(WNDCLASSEX); CS_HREDRAW | CS_VREDRAW; WndProcedure; 0; 0; hInstance;
DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(WM_QUIT); break; default: return DefWindowProc(hWnd, Msg, wParam, lParam); } return 0; }
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. 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. Destroys the specified window.
WM_CLOSE
The function sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus from it. The function also destroys the window's menu, flushes the thread message queue, destroys timers, removes clipboard ownership, and breaks the clipboard viewer chain (if the window is at the top of the viewer chain).If the specified window is a parent or owner window, DestroyWindow automatically destroys the associated child or owned windows when it destroys the parent or owner window. The function first destroys child or owned windows, and then it destroys the parent or owner window. DestroyWindow also destroys modeless dialog boxes created by the CreateDialog function. PostQuitMessage().The application exit code. This value is used as the wParam
parameter of the WM_QUIT message.This function does not return a value. The PostQuitMessage function posts a WM_QUIT message to the thread's message queue and returns immediately; the function simply indicates to the system that the thread is requesting to quit at some time in the future.
When the thread retrieves the WM_QUIT message from its message queue, it should exit its message loop and return control to the system. The exit value returned to the system must be the wParam parameter of the WM_QUIT message. 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. This function uses a switch control to list all necessary messages and process each one in turn. This processes only the messages that you ask it to. If you have left-over messages, and you will always have un-processed messages, you can call the DefWindowProc() function at the end to take over. The most basic message you can process is to make sure a user can close a window after using it. This can be done with a function called PostQuitMessage(). Its syntax is: VOID PostQuitMessage(int nExitCode) This function takes one argument which is the value of the LPARAM argument. To close a window, you can pass the argument as WM_QUIT.
for your application. These icons have identification names that you can pass to the LoadIcon() function as the lpIconName argument. The icons are: ID IDI_APPLICATION IDI_INFORMATION IDI_ASTERISK IDI_QUESTION IDI_WARNING IDI_EXCLAMATION IDI_HAND IDI_ERROR If you designed your own icon (you should make sure you design a 32x32 and a 16x16 versions, even for convenience), to use it, specify the hInstance argument of the LoadIcon() function to the instance of your application. Then use the MAKEINTRESOURCE macro to convert its identifier to a null-terminated string. This can be done as follows: WndCls.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_STAPLE)); The icon can be specified by its name, which would be a null-terminated string passed as lpszResourceName. If you had designed your icon and gave it an ID, you can pass this identifier to the LoadIcon() method. The LoadIcon() member function returns an HICON object that you can assign to the hIcon member variable of your WNDCLASS object. Besides the regular (32x32) icon, the WNDCLASSEX structure allows you to specify a small icon (16x16) to use in some circumstances. You can specify both icons as follows: INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClsEx; WndClsEx.cbSize WndClsEx.style WndClsEx.lpfnWndProc WndClsEx.cbClsExtra WndClsEx.cbWndExtra WndClsEx.hIcon WndClsEx.hInstance WndClsEx.hIconSm return 0; } = = = = = = = = sizeof(WNDCLASSEX); CS_HREDRAW | CS_VREDRAW; WndProcedure; 0; 0; LoadIcon(NULL, IDI_APPLICATION); hInstance; LoadIcon(NULL, IDI_APPLICATION); Picture
Introduction to Cursors
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 IDC_APPSTARTING IDC_ARROW IDC_CROSS IDC_HAND Picture Description Used to show that something undetermined is going on or the application is not stable This standard arrow is the most commonly used cursor The crosshair cursor is used in various circumstances such as drawing TheHand 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. The combined arrow and question mark cursor is used when providing help on a specific item on a window object The I-beam cursor is used on text-based object to show the position of the caret This cursor is not used anymore This cursor can be used to indicate an unstable situation This cursor is not used anymore 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 The northeast and southwest arrow cursor can be used when resizing an object on both the length and the height The north - south arrow pointing cursor can be used when shrinking or heightening an object The northwest - southeast arrow pointing cursor can be used when resizing an object on both the length and the height The west - east arrow pointing cursor can be used when narrowing or enlarging an object The vertical arrow cursor can be used to indicate the presence of the mouse or the caret The Hourglass cursor is usually used to indicate that a window or the application is not ready.
The LoadCursor() member function returns an HCURSOR value. You can assign it to the hCursor member variable of your WNDCLASS object. Here is an example: INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClsEx; WndClsEx.cbSize WndClsEx.style WndClsEx.lpfnWndProc WndClsEx.cbClsExtra WndClsEx.cbWndExtra WndClsEx.hIcon WndClsEx.hCursor WndClsEx.hInstance WndClsEx.hIconSm return 0; } = = = = = = = = = sizeof(WNDCLASSEX); CS_HREDRAW | CS_VREDRAW; WndProcedure; 0; 0; LoadIcon(NULL, IDI_APPLICATION); LoadCursor(NULL, IDC_ARROW); hInstance; LoadIcon(NULL, IDI_APPLICATION);
= = = =
Registerig window
Before we can create a window, we must register it within the Windows. All windows must be registered. Later on we will see, that we do not register a window, when we create a button, static text etc. This is because these controls are predefined. They have already been registered. Programming in C/winapi means a lot of working with structures. To register a window, we must create and fill an WNDCLASS structure. We set the window style, extra allocation bytes, window class name, handle of the program instance, background brush, optional menu name, window procedure, handle of the cursor and icon. Finally, we call the RegisterClass() function. you can 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
Window Registration
After initializing the window class, you must make it available to the other controls that will be part of your application. This process is referred to as registration. To register the window class, call the RegisterClass() for a WNDCLASS variable. If you created your window class using the WNDCLASSEX structure, call the RegisterClassEx() function. Their syntaxes are: ATOM RegisterClass(CONST WNDCLASS *lpWndClass); ATOM RegisterClassEx(CONST WNDCLASSEX *lpwcx); The function simply takes as argument a pointer to a WNDCLASS or WNDCLASSEX. This call can be done as follows: INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WndClsEx; . . . RegisterClassEx(&WndClsEx); return 0; }
MESSAGEBOX Displays a modal dialog box that contains a system icon, a set of buttons, and a brief applicationspecific message, such as status or error information. The message box returns an integer value that indicates which button the user clicked.
Syntax
int WINAPI MessageBox( __in_opt HWND hWnd, __in_opt LPCTSTR lpText, __in_opt LPCTSTR lpCaption, __in UINT uType );
Parameters
hWnd [in, optional]
Type: HWND A handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no owner window.
lpText [in, optional]
Type: LPCTSTR The message to be displayed. If the string consists of more than one line, you can separate the lines using a carriage return and/or linefeed character between each line.
lpCaption [in, optional]
Type: LPCTSTR The dialog box title. If this parameter is NULL, the default title is Error.
uType [in]
Type: UINT The contents and behavior of the dialog box. This parameter can be a combination of flags from the following groups of flags. To indicate the buttons displayed in the message box, specify one of the following values.
Meaning
The message box contains three push buttons: Abort, Retry, and Ignore.
MB_CANCELTRYCONTINUE The message box contains three push buttons: Cancel, Try 0x00000006L MB_HELP 0x00004000L MB_OK 0x00000000L MB_OKCANCEL 0x00000001L MB_RETRYCANCEL 0x00000005L MB_YESNO 0x00000004L MB_YESNOCANCEL 0x00000003L
Again, Continue. Use this message box type instead of MB_ABORTRETRYIGNORE. Adds a Help button to the message box. When the user clicks the Help button or presses F1, the system sends a WM_HELP message to the owner. The message box contains one push button: OK. This is the default.
The message box contains two push buttons: Retry and Cancel.
The message box contains two push buttons: Yes and No.
The message box contains three push buttons: Yes, No, and Cancel.
To display an icon in the message box, specify one of the following values.
Value MB_ICONEXCLAMATION 0x00000030L MB_ICONWARNING Meaning
An icon consisting of a lowercase letter i in a circle appears in the message box. An icon consisting of a lowercase letter i in a circle appears in the message box. A question-mark icon appears in the message box. The questionmark message icon is no longer recommended because it does not clearly represent a specific type of message and because the phrasing of a message as a question could apply to any message type. In addition, users can confuse the message symbol question mark with Help information. Therefore, do not use this question mark message symbol in your message boxes. The system continues to support its inclusion only for backward compatibility. A stop-sign icon appears in the message box.
MB_ICONQUESTION 0x00000020L
MB_DEFBUTTON1 The first button is the default button. 0x00000000L MB_DEFBUTTON2 0x00000100L
MB_DEFBUTTON1 is the default unless MB_DEFBUTTON2, MB_DEFBUTTON3, or MB_DEFBUTTON4 is specified. The second button is the default button.
To indicate the modality of the dialog box, specify one of the following values.
Value Meaning
The user must respond to the message box before continuing work in the window identified by the hWnd parameter. However, the user can move to the windows of other threads and work in those windows.
MB_APPLMODAL 0x00000000L
Depending on the hierarchy of windows in the application, the user may be able to move to other windows within the thread. All child windows of the parent of the message box are automatically disabled, but pop-up windows are not.
MB_APPLMODAL is the default if neither MB_SYSTEMMODAL nor MB_TASKMODAL is specified. Same as MB_APPLMODAL except that the message box has the MB_SYSTEMMODAL WS_EX_TOPMOST style. Use system-modal message boxes to notify the user of serious, potentially damaging errors that require 0x00001000L immediate attention (for example, running out of memory). This flag has no effect on the user's ability to interact with windows other than those associated with hWnd. Same as MB_APPLMODAL except that all the top-level windows MB_TASKMODAL belonging to the current thread are disabled if the hWnd parameter is NULL. Use this flag when the calling application or library does not 0x00002000L have a window handle available but still needs to prevent input to other windows in the calling thread without suspending other threads.
If the current input desktop is not the default desktop, MessageBox does not return until the user switches to the default desktop. The text is right-justified.
Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems. The message box becomes the foreground window. Internally, the system calls the SetForegroundWindow function for the message box. The message box is created with the WS_EX_TOPMOST window style. The caller is a service notifying the user of an event. The function displays a message box on the current active desktop, even if there is no user logged on to the computer. Terminal Services: If the calling thread has an impersonation token, the function directs the message box to the session specified in the impersonation token. If this flag is set, the hWnd parameter must be NULL. This is so that the message box can appear on a desktop other than the desktop corresponding to the hWnd. For information on security considerations in regard to using this flag, see Interactive Services. In particular, be aware that this flag can produce interactive content on a locked desktop and should therefore be used for only a very limited set of scenarios, such as resource exhaustion.
MB_SERVICE_NOTIFICATION 0x00200000L
Return value
Type: int
If a message box has a Cancel button, the function returns the IDCANCEL value if either the ESC key is pressed or the Cancel button is selected. If the message box has no Cancel button, pressing ESC has no effect. If the function fails, the return value is zero. To get extended error information, call GetLastError. If the function succeeds, the return value is one of the following menu-item values.
Return code/value IDABORT 3 IDCANCEL 2 IDCONTINUE 11 IDIGNORE 5 IDNO 7 IDOK 1 IDRETRY 4 IDTRYAGAIN 10 IDYES 6 Description
Remarks
The following system icons can be used in a message box by setting the uType parameter to the corresponding flag value.
Icon Flag values MB_ICONHAND, MB_ICONSTOP, or MB_ICONERROR
MB_ICONQUESTION
MB_ICONEXCLAMATION or MB_ICONWARNING
MB_ICONASTERISK or MB_ICONINFORMATION
Adding two right-to-left marks (RLMs), represented by Unicode formatting character U+200F, in the beginning of a MessageBox display string is interpreted by the MessageBox rendering engine so as to cause the reading order of the MessageBox to be rendered as right-to-left (RTL). When you use a system-modal message box to indicate that the system is low on memory, the strings pointed to by the lpText and lpCaption parameters should not be taken from a resource file because an attempt to load the resource may fail. If you create a message box while a dialog box is present, use a handle to the dialog box as the hWnd parameter. The hWnd parameter should not identify a child window, such as a control in a dialog box.
Examples
In the following example, the application displays a message box that prompts the user for an action after an error condition has occurred. The message box displays the message that describes the error condition and how to resolve it. The MB_CANCELTRYCONTINUE style directs MessageBox to provide three buttons with which the user can choose how to proceed. The MB_DEFBUTTON2 style sets the default focus on the second button of the message box, in this case, the Try Again button.
The following image shows the output from the preceding code example:
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), this 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. 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. The menu is NULL for now since we don't have one yet. 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. 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. 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.
MSG Msg; HWND hWnd; WNDCLASSEX WndClsEx; // Create the application window 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 = (HBRUSH)GetStockObject(WHITE_BRUSH); WndClsEx.lpszMenuName = NULL; WndClsEx.lpszClassName = ClsName; WndClsEx.hInstance = hInstance; WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // Register the application RegisterClassEx(&WndClsEx); // Create the window object hWnd = CreateWindow(ClsName, WndName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); // Find out if the window was created if( !hWnd ) // If the window was not created, return 0; // stop the application // Display the window to the user ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); // Decode and treat the messages // as long as the application is running while( GetMessage(&Msg, NULL, 0, 0) ) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch(Msg) { // If the user wants to close the application case WM_DESTROY: // then close it PostQuitMessage(WM_QUIT); break; default: // Process the left-over messages return DefWindowProc(hWnd, Msg, wParam, lParam); } // If something was not done, let it go return 0; } 2. To execute the program, if you are using Borland C++ Builder, press F9 If you are using Microsoft Visual C++, press Ctrl + F5 and click Yes
http://comrade.ownz.com/docs/msvc6.html#f1help