Professional Documents
Culture Documents
by Johnnie Rose, Jr. If you're a Windows programming novice, retrieve your favorite beverage, sit back, and take a quick trip over to MSDN or download the code for the entire tutorial. You're about to become a Windows developer.
As such, you are placed in the peculiar position of holding the Instant power to directly interact with the operating system via your Messaging programs. The first function with which you must become The Window comfortable is WinMain(). Windows passes control to this function Procedure after program code and data have been loaded from the More Tutorials and Links executable, paging tables readied, memory reserved and allocated, etc. Window-creation code is traditionally placed within or "close to" WinMain(), though not every Windows program has a See Also: Johnnie's main window. Before we get into specifics, let's see a structural Winsock outline:
Tutorial
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) hInstance is a handle to this application's executable used to load resources into the program's memory space, an operation you'll witness later on. hPrevInstance is a handle to the previous instance of the program, usually NULL. As its name might suggest, lpCmdLine is a string specifying the name and arguments sent to the program. Finally, nCmdShow specifies the current show state of the window. To generate something visually appealing, we need to put some code in WinMain() to create a window. This involves telling the system about the window, displaying the window, and arranging the system such that the window can receive messages.
Bookmark
(MSIE only)
View
// // // // //
Any combination of many styles Address of this window's procedure Extra bytes (not required) Extra bytes (not required) This window's instance
//
// // //
Background color Name of the menu we would use User-defined, custom name for the class
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { We need to define one of these structures in WinMain(): WNDCLASS wc; wc.style = CS_VREDRAW | CS_HREDRAW; // Basically repaint the window anytime its size changes wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Default application icon wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Plain, default cursor wc.hbrBackground = (HBRUSH)(COLOR_SCROLLBAR+1); // Grey background wc.lpszMenuName = 0; wc.lpszClassName = "MyWin32App";
lpszClassName can be any name you like; here it's the descriptive "MyWin32App." lpfnWndProc is a very important option. This function receives all the messages for the window, and is generally responsible for keeping it up to date with the rest of the operating system. (More about messages later.) style denotes all the possible class styles you can type to customize how your class acts. As you might have noticed, a trick to get two or more styles into one field is to separate them with the OR operator | (also known as the vertical pipe), which will be used frequently in Windows programming. The remainder of the fields are set to default values. Had we wanted to attach a menu to our window, we would have specified the menu's name in the lpszMenuName field. The next step is to register the class with Windows so we can use it, with a little error checking:
HWND hwnd; hwnd = CreateWindow("MyWin32App", registered class "Johnnie's Win32 Example", WS_OVERLAPPEDWINDOW, of many window styles CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, window's parent NULL, hInstance, hInstance NULL); parameter to window if (hwnd == NULL) creation failed return 0; // // // // // // // // // // // // // Name of Window name/title Any combination X coordinate Y coordinate Width Height Handle to this Handle to a menu Handle to Optional If window Exit
Using WS_OVERLAPPEDWINDOW ensures that the window is created with all the common attributes, including a minimize and maximize button, title bar, and distinct border. CW_USEDEFAULT keeps us from having to determine the initial position and size of the window, letting the system do the grunt work instead. Both the Y coordinate and height fields are set to 0 because CreateWindow() ignores these parameters when CW_USEDEFAULT is implemented. Finally, the window is displayed on screen using the fourth argument to WinMain():
// //
Instant Messaging
Microsoft being the naturally innovative company that it is (consider that last statement sarcastic if you like), it had to come up with new management since programs now had to appear as though they could process user input and respond to system events simultaneously. (In case you were wondering, that's not actually what happens. As with all multi-tasking, only one thing can really happen at a time, and so the CPU allots specific amounts of processing time to each thread. Even then, time is only given to "important" threads.) A messaging system was created as a result of this new requirement, where the system and other windows communicate with your window via messages. For example, clicking the close button on your browser window sends a WM_DESTROY message, the browser performs any final tasks it needs to, and gives up its CPU time share, formally ending its execution. Since you're creating a window, you can control how these messages affect your program. All this logic is done inside a function called a window procedure, which was
specified when the window class was created in the lpfnWndProc field. Thankfully, there's a DefWindowProc() that will handle messages we don't process so we don't have to implement every possible message. Whenever a message is sent to a window, the command is added to the window's queue, and those entries must be translated and sent to the window procedure to be of any use. This loop of getting a message, translating it, and dispatching to a procedure is called the message loop, and usually continues until the program ends. There are many variations, but the following is the most basic and will appear in our WinMain():
MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { into MSG variable TranslateMessage(&msg); keyboard-specific messages DispatchMessage(&msg); the window procedure } } // // // Put a message Translate Send it off to
That process continues until the program signals that it's ready to be destroyed and posts a quit message. So, what's this window procedure look like?
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) hwnd is the window we're talking about. uMsg, of course, is the identifier telling the function what message has been sent. As their names might suggest, wParam and lParam are additional parameters containing information dependent on the message. DefWindowProc() has essentially the same definition, making it extremely easy to pass it un-processed messages for default action. The most common practice for coding window procs is to have a massive switch statement at the base with case statements representing possible messages. With that in mind, let's start off our MainWndProc():
LRESULT WINAPI MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { default: return DefWindowProc(hwnd, msg, wParam, lParam); // We don't deal with it, let somebody else do it break;
The obscenely important reason why procedures are introduced so early is because that call to CreateWindow() will fail if the function sees a WM_CREATE message isn't intercepted. Interestingly enough, we could run our window just off of the above code and let DefWindowProc() handle everything for us. To process a message, add a case statement with the message identifier as the switch value and appropriate code below. For example, if we wanted the program to tell us when it's created (and ease whining from CreateWindow()):
case WM_CREATE: // If the message is WM_CREATE, display alert MessageBox(NULL, "The sweet smell of success.", "Indication", MB_OK); break;
Note:
MessageBox() displays a dialog box with an OK button and message. Here, the dialog box is not directly associated with our window. WM_CREATE, by the way, is a very good time to initialize any other graphical elements you may have. A rather large list of messages that can be sent to a window can be found here. Now let's make sure the close button (X) will work and the user can exit without using Ctrl+Alt+Del:
case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: imminent PostQuitMessage(0); share/give OS control break; } }
// // // //
And the finished procedure covers three messages; the program at a lean 63 lines.