The Atoms of a Windows Window

 < Free Open Study > 



Developers who spend time building C++ Win32 applications are most likely to make use of a Windows framework, such as MFC. This is a smart choice, as a solid application framework can help bootstrap your efforts with numerous CASE tools, utility classes, and whatnot. That's the good news. The bad news is that a framework such as MFC can encapsulate the underlying details to such a degree that you might have little idea what is happening under the hood. So then, before we see what ATL will do to hide numerous details of a raw Win32 application, let's remember our roots and assemble a main window by hand. This is by no means a comprehensive review of Win32 development, but is intended to dust off any current cobwebs. If you wish to follow along, fire up Visual Studio and select a new Win32 Application (an empty project) named Win32Window.

 On the CD   You will find the Win32Window application on your companion CD under the Labs\Chapter 13\Win32Window subdirectory.

Every Win32 EXE has at minimum three major players: a WNDCLASSEX structure used to describe the characteristics of the window, WinMain() which serves as the application's entry point, and an associated windows procedure (a.k.a. WndProc) used to process incoming messages for a given window. Beyond driving the logic behind the application itself, the most significant task of WinMain() is to establish your application's message pump.

Constructing a Windows Message Pump

A message pump is responsible for grabbing incoming system (as well as user-defined) messages sent to a registered window and routing them to the corresponding WndProc function for processing. To begin coding our Win32 window, insert a new file (mainWnd.cpp) into your project and define a message loop as so:

// Our initial crack at WinMain() creates a message pump for this window. #include <windows.h>                    // The keeper of all things Windows. int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,                 PSTR szCommand, int iShow) {      // Create the windows message pump.      MSG msg;      while(GetMessage(&msg, NULL, 0, 0))      {           TranslateMessage(&msg);         // Process message...           DispatchMessage(&msg);          // Send to WinProc...      }      return msg.wParam; }

The GetMessage() API call is used to fill a MSG structure with information pertaining to the current message in the queue. TranslateMessage() and DispatchMessage() format and forward the current message to the registered WinProc for processing. Note that the GetMessage() call is under control of a while loop, which will fail only when a WM_QUIT message is plucked from the messages queue, at which time the window will be shut down. As we will see, WM_QUIT is posted using PostQuitMessage().

Building the WNDCLASSEX Structure

Now that we have a message pump on the lookout for incoming messages, we need to tell the operating system a bit about the window itself. Under Win32, a window is described within the context of a WNDCLASSEX structure. This structure contains fields that describe such things as the icons, cursor, and menu to use in conjunction with this window. More importantly, the WNDCLASSEX structure establishes the name of the WndProc function that will be receiving the messages sent from the message pump. Here is a peek at WNDCLASSEX, as defined in <winuser.h>:

// This Win32 structure is used to describe numerous aspects of the window you are // busy constructing. typedef struct tagWNDCLASSEX{      UINT           cbSize;        // How big is this structure?      UINT           style;         // Any number of CS_ flags.      WNDPROC      lpfnWndProc;     // Function to route messages to.      int           cbClsExtra;     // Any extra bytes to pack after this struct.      int           cbWndExtra;     // Any extra bytes to pack after the instance.      HINSTANCE      hInstance;     // Who owns this WNDCLASSEX?      HICON      hIcon;             // Large icon to use for this window.      HCURSOR      hCursor;         // Mouse cursor to use for this window.      HBRUSH      hbrBackground;    // Color of client area for this window.      LPCWSTR      lpszMenuName;    // Any menu to attach to this window?      LPCWSTR      lpszClassName;   // A string name for this type of window.      HICON      hIconSm;           // Small icon to use this window. } WNDCLASSEX; 

Every single Win32 window will need to fill this structure and register it with the system via the Win32 RegisterClassEx() function. As you can imagine, each field of the WNDCLASSEX structure can consist of a number of flags (or combination of flags), all of which are documented within online help.

To keep things simple, assume the window under construction does not have a menu, and will make use of system-defined large and small icon bitmaps. Furthermore, we will use a solid white background used to paint the window's client area and a standard system arrow icon as our mouse cursor. To set these options, we will need to make use of some further Win32 API calls such as LoadIcon() and LoadCursor(). Finally, we will assign the class name of this window to MyWin32Window. Here is our updated WinMain() imple- mentation:

// Here we fill a WNDCLASSEX structure to define our window. int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,                  PSTR szCommand, int iShow) {      // We need to create a WNDCLASSEX structure to describe this window.      WNDCLASSEX myWindow;      myWindow.cbClsExtra = 0;      myWindow.cbSize = sizeof(myWindow);      myWindow.cbWndExtra = 0;      myWindow.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);      myWindow.hCursor = LoadCursor(NULL, IDC_ARROW);      myWindow.hIcon = LoadIcon(NULL, IDI_APPLICATION);      myWindow.hIconSm = LoadIcon(NULL, IDI_APPLICATION);      myWindow.hInstance = hInst;      myWindow.lpfnWndProc = MyWindowProc;          // We'll get to this...      myWindow.lpszClassName = "MyWin32Window";      myWindow.lpszMenuName = NULL;      myWindow.style = CS_HREDRAW | CS_VREDRAW;      // Register the class.      RegisterClassEx(&myWindow);      MSG msg;      while(GetMessage(&msg, NULL, 0, 0))      {           TranslateMessage(&msg);           DispatchMessage(&msg);      }      return msg.wParam; } 

Showing the Window

Notice that once we have filled a WNDCLASSEX structure with all the relevant information (and registered this window using RegisterClassEx()) all we need to do to finish up WinMain() is to literally create and show the window. This is done with three additional API calls.

First, CreateWindow() is used to load our window into memory and establish the rectangular dimensions of the window. Take note that CreateWindow() returns an almighty HWND variable, which we must store for use throughout our program. The HWND data type is a handle to the window we have just registered with the system, and is the first parameter to numerous other Win32 API functions.

Once the window is created in memory, we follow up with a call to ShowWindow() (which tells the window how to display itself) and UpdateWindow() (which forces a WM_PAINT message into the message queue). As you can see below, some of the parameters of CreateWindow() are the same parameters received by WinMain() itself. This final bit of logic can be sandwiched right between the class registration and message loop logic:

// Create and show the window. HWND hwnd; hwnd = CreateWindow("MyWin32Window",         // Class name (again).                   "My Raw Window",           // Caption of this window.                   WS_OVERLAPPEDWINDOW,       // Style flags.                   10, 10, 200, 200,          // Size of window.                   NULL,                      // Parent handle.                   NULL,                      // Menu handle.                   hInst,                     // First parameter of WinMain()                   NULL);                     // Any additional creation parameters. ShowWindow(hwnd, iShow);                     // Our HWND and the 3rd parameter of WinMain() UpdateWindow(hwnd);

Processing the Messages: Building a Windows Procedure

WinMain() is now complete, and as you can see we are again knee-deep with standard boilerplate code. Before we see how ATL provides a complete implementation of this logic on our behalf, we need to investigate the next major player in a Windows EXE, the WndProc function. When you establish your WNDCLASSEX structure for a given window, the lpfnWndProc ("long-pointer-to-a-function") field must be set to the name of the function that is ready to process the events being spit out from the message pump. For the current window under construction, we specified lpfnWndProc as:

// Call this function when you have a message for me... myWindow.lpfnWndProc = MyWindowProc; 

So, somewhere in our program we need to have a function indeed called MyWindowProc. The name of a window's WndProc function can be anything within the syntactical constraints of C++ (WndProc, MyRadWndProc, XXXX, or what have you). The parameters and return type, however, must always be the same. Here is the prototype of our window's procedure function:

// The callback function must have this signature, but may take any valid name. LRESULT CALLBACK MyWindowProc(HWND, UINT, WPARAM, LPARAM); 

Again, this function will be called whenever your window has been sent a message from the system (all those "WM_" messages) as well as any user-defined messages, notification messages, and so forth. As a Win32 developer, it is up to you to decide which of the hundreds of messages you care to process. For example, if you want to paint some text into the client area, your WndProc function needs to respond to WM_PAINT. If you want to do some special processing when the left mouse button has been clicked in your client area, you need to contend with WM_LBUTTONDOWN. The entire set of system-defined messages can be discovered within <windows.h> and friends. Learning about all the messages you can respond to is largely a matter of time spent as a Windows developer.

Note 

MFC developers intercept messages indirectly by using the ClassWizard CASE tool. Even though this tool is unavailable in ATL development, we do have a slimmed-down variation which we can make use of (as we will see in a bit).

So then, if your particular WndProc function responds to a tiny set of possible Windows messages, what about all the others which you may well receive that you do not care about? Luckily, the DefWindowProc() API function can be used as your back door. If your WndProc responds to WM_PAINT, WM_CREATE, and WM_DESTROY messages (and nothing else), the remaining messages will be sent into the default windows procedure for processing. We can now finish up our Win32 EXE project by providing the following implementation of MyWindowProc():

// Pick out the messages you want to respond to, and send all others // to the default windows procedure. LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg,                            WPARAM wParam, LPARAM lParam) {      // Variables for painting.      HDC hDC;      PAINTSTRUCT ps;      RECT clientRect;      // Which message have we been given?      switch(msg)      {      // We are coming to life.      case WM_CREATE:           MessageBox(NULL, "Your window is about to be created",           "Message from WM_CREATE", MB_OK | MB_SETFOREGROUND);      return 0;      // We need to be rendered.      case WM_PAINT:           hDC = BeginPaint(hwnd, &ps);           GetClientRect(hwnd, &clientRect);           DrawText(hDC, "The Raw Win32 Window", -1,                   &clientRect,                   DT_CENTER | DT_VCENTER | DT_SINGLELINE);           EndPaint(hwnd, &ps);      return 0;      // We are going down, must place a WM_QUIT into the queue      // to terminate the message pump.      case WM_DESTROY:           PostQuitMessage(0);      return 0;      }      // Let system handle everything else.      return DefWindowProc(hwnd, msg, wParam, lParam); } 

With this final piece of the puzzle in place, we can now compile our program and run the application. You will see a message box pop up in response to WM_CREATE, just before the main window comes to life, as seen in Figure 13-1:


Figure 13-1: A simple Win32 window.

Although the above code is by no means mind numbing, Win32 programming (not unlike COM) requires a good deal of boilerplate code. If you are an MFC veteran, you may have never been exposed to the details of WinMain() and related items (which may or may not be a good thing). As you already know the evils of linking into the MFC libraries from within an ATL project, we will spend the rest of this chapter learning about some of the support ATL provides for building stand-alone Windows applications.



 < Free Open Study > 



Developer's Workshop to COM and ATL 3.0
Developers Workshop to COM and ATL 3.0
ISBN: 1556227043
EAN: 2147483647
Year: 2000
Pages: 171

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net