Tricks of the Windows Game Programming Gurus:Advanced Windows Programming

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

In other words, referring back to Figure 3.12, the window is 400×400, but only the lower region of the window—300,300 to 400,400—needs repainting. Thus, the graphics device context returned by the call to `BeginPaint()` is only valid for this 100×100 region of your window! Obviously, this is a problem if you want to have access to the entire client area.

The solution to the problem has to do with gaining access to the graphics device context for the window directly without it being sent as part of a window update message via `BeginPaint()`. You can always get a graphics context for a window or `hdc` using the `GetDC()` function, as shown here:

    HDC GetDC(HWND hWnd); // handle of window

You simply pass the window handle of the graphics device context you want to access, and the function returns a handle to it. If the function is unsuccessful, it returns `NULL`. When you’re done with the graphics device context handle, you must give it back to Windows with a call to `ReleaseDC()`, as shown here:

    int ReleaseDC(HWND hWnd, // handle of window

                  HDC hDC);  // handle of device context

`ReleaseDC()` takes the window handle and the handle to the device context you previously acquired with `GetDC()`.

> Note: Windows-speak gets confusing when it comes to graphics device contexts. Technically, a handle to a device context can refer to more than one output device. For example, a device context could be a printer. Therefore, I usually refer to a graphics-only device context as a graphics device context. But the data type is `HDC` or handle to device context. So typically, I will define a graphics device context variable as `HDC hdc`, but sometimes I will also use `HDC gdc` because it makes more sense to me. In any case, just be aware that for this book, a graphics device context and a device context are interchangeable, and variables with the names `hdc` and `gdc` are of the same type.

Here’s how you would use `GetDC()`—`ReleaseDC()` to do graphics:

    HDC gdc = NULL; // this will hold the graphics device context

    // get the graphics context for the window

    if (!(gdc = GetDC(hwnd)))

        error();

    // use the gdc here and do graphics - you don’t know how yet!

    // release the dc back to windows

    ReleaseDC(hwnd, gdc);

Of course, you don’t know how to do any graphics yet, but I’m getting there… The important thing is that you now have another way to process a `WM_PAINT` message. However, there is one problem: When you make a call to `GetDC()`—`ReleaseDC()`, Windows has no idea that you have restored or validated the client area of your window. In other words, if you use `GetDC()`—`ReleaseDC()` in place of `BeginPaint()`—`EndPaint()`, you’ll create another problem!

The problem is that `BeginPaint()`—`EndPaint()` sends a message to Windows indicating that the window contents have been restored (even if you don’t make any graphics calls). Hence, Windows won’t send any more `WM_PAINT` messages. On the other hand, if you replace `BeginPaint()`—`EndPaint()` with `GetDC()`—`ReleaseDC()` in the `WM_PAINT` handler, `WM_PAINT` messages will continue to be sent forever! Why? Because you must validate the window.

To validate the area of a window that needs repainting and tell Windows that you have restored the window, you could call `BeginPaint()`—`EndPaint()` after the call to `GetDC()`—`ReleaseDC()`, but this would be inefficient. Instead, use the call specifically designed for this, called `ValidateRect()`:

    BOOL ValidateRect(HWND hWnd, // handle of window

     CONST RECT *lpRect);  // address of validation rectangle coordinates

To validate a window, send the handle of the window along with the region you want to be validated in `lpRect`. In most cases, the region to validate would be the entire window. Thus, to use `GetDC()`—`ReleaseDC()` in the `WM_PAINT` handler, you would have to do something like this:

    PAINTSTRUCT   ps;   // used in WM_PAINT

    HDC        hdc; // handle to a device context

    RECT          rect; // rectangle of window

    case WM_PAINT:

        {

        // simply validate the window

        hdc = GetDC(hwnd);

        // you would do all your painting here

           ReleaseDC(hwnd,hdc);

           // get client rectangle of window - use Win32 call

           GetClientRect(hwnd,&rect);

           // validate window

           ValidateRect(hwnd,&rect);

           // return success

           return(0);

           } break;

> Note: Notice the call to `GetClientRect()`. All this does is get the client rectangle coordinates for you. Remember, because a window can move around, it has two sets of coordinates: window coordinates and client coordinates. Window coordinates are relative to the screen, and client coordinates are relative to the upper left-hand corner of the window (0,0). Figure 3.13 shows this more clearly.

Figure 3.13 Window coordinates versus client coordinates.

You must be saying, “Does it really need to be this hard?” Of course it does—it’s Windows <BG>. Remember, the whole reason for all this drama in the `WM_PAINT` message handler is that you need to make sure that you can draw graphics anywhere you want in the client area of the window. This is only possible if you use `GetDC()`—`ReleaseDC()` or `BeginPaint()`—`EndPaint()` with a completely invalid window. However, we are trying to get the best of both worlds, and we’re almost done. The final trick I want to show you is how to invalidate a window manually.

Consider this: If you could somehow invalidate the entire window within your `WM_PAINT` handler, you would be sure that the `rcPaint` field of the `ps PAINTSTRUCT` returned by `BeginPaint()` and the associated `gdc` would give you access to the entire client area of the window. To make this happen, you can manually enlarge the invalidated area of any window with a call to `InvalidateRect()`, as shown here:

    BOOL InvalidateRect(HWND hWnd, // handle of window with

                                   // changed update region

    CONST RECT *lpRect,  // address of rectangle coordinates

    BOOL bErase);        // erase-background flag

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.