The scratch program


Occasionally, there is need to illustrate a point with a full program. To avoid reproducing the boring parts of the program, let’s agree on using the following template for our sample programs.

For expository purposes, I won’t use a C++ class. I’ll just keep all my variables global. In a real program, of course, instance data would be attached to the window instead of floating globally.

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <ole2.h>
#include <commctrl.h>
#include <shlwapi.h>

HINSTANCE g_hinst;                          /* This application's HINSTANCE */
HWND g_hwndChild;                           /* Optional child window */

/*
 *  OnSize
 *      If we have an inner child, resize it to fit.
 */
void
OnSize(HWND hwnd, UINT state, int cx, int cy)
{
    if (g_hwndChild) {
        MoveWindow(g_hwndChild, 0, 0, cx, cy, TRUE);
    }
}

/*
 *  OnCreate
 *      Applications will typically override this and maybe even
 *      create a child window.
 */
BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
    return TRUE;
}

/*
 *  OnDestroy
 *      Post a quit message because our application is over when the
 *      user closes this window.
 */
void
OnDestroy(HWND hwnd)
{
    PostQuitMessage(0);
}

/*
 *  PaintContent
 *      Interesting things will be painted here eventually.
 */
void
PaintContent(HWND hwnd, PAINTSTRUCT *pps)
{
}

/*
 *  OnPaint
 *      Paint the content as part of the paint cycle.
 */
void
OnPaint(HWND hwnd)
{
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    PaintContent(hwnd, &ps);
    EndPaint(hwnd, &ps);
}

/*
 *  OnPrintClient
 *      Paint the content as requested by USER.
 */
void
OnPrintClient(HWND hwnd, HDC hdc)
{
    PAINTSTRUCT ps;
    ps.hdc = hdc;
    GetClientRect(hwnd, &ps.rcPaint);
    PaintContent(hwnd, &ps);

}

/*
 *  Window procedure
 */
LRESULT CALLBACK
WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uiMsg) {

    HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
    HANDLE_MSG(hwnd, WM_SIZE, OnSize);
    HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
    HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
    case WM_PRINTCLIENT: OnPrintClient(hwnd, (HDC)wParam); return 0;
    }

    return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}

BOOL
InitApp(void)
{
    WNDCLASS wc;

    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = g_hinst;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = TEXT("Scratch");

    if (!RegisterClass(&wc)) return FALSE;

    InitCommonControls();               /* In case we use a common control */

    return TRUE;
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                   LPSTR lpCmdLine, int nShowCmd)
{
    MSG msg;
    HWND hwnd;

    g_hinst = hinst;

    if (!InitApp()) return 0;

    if (SUCCEEDED(CoInitialize(NULL))) {/* In case we use COM */

        hwnd = CreateWindow(
            TEXT("Scratch"),                /* Class Name */
            TEXT("Scratch"),                /* Title */
            WS_OVERLAPPEDWINDOW,            /* Style */
            CW_USEDEFAULT, CW_USEDEFAULT,   /* Position */
            CW_USEDEFAULT, CW_USEDEFAULT,   /* Size */
            NULL,                           /* Parent */
            NULL,                           /* No menu */
            hinst,                          /* Instance */
            0);                             /* No special parameters */

        ShowWindow(hwnd, nShowCmd);

        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        CoUninitialize();
    }

    return 0;
}

Notice that all painting gets funneled through the PaintContent function. This allows us to route the WM_PRINTCLIENT message through the same paint function, which has as an immediate consequence the ability to animate the window with AnimateWindow. This will also prove useful for printing high-resolution screenshots.

Other than the trickiness with painting, there really isn’t anything here that you shouldn’t already know. The point of this program is to be a template for future programs.

My first mission will be an eight-part series on scrollbars.

That’s right. Scrollbars.

I can’t believe I have an eight-part series on scrollbars. And you probably can’t believe you’re reading about it.

Comments (23)
  1. Just a note: MSDN says that InitCommonControls() is obsolete and that new applications should use InitCommonControlsEx().

  2. Use the font-linking functions to change fonts as necessary.

  3. Often programming is just assembling the building blocks you already have.

  4. They allow information to cross security boundaries.

  5. The shell gives you the IDataObject; all you have to do is drag it around.

  6. Thinking through message pumping.

  7. The two usually agree but are not required to.

  8. The visual state becomes out of sync with the stack state.

  9. Writing your own dialog loop.

  10. It gives you a one-shot solid color brush.

  11. The taskbar detects that you created a fullscreen window and gets out of the way automatically.

  12. It doesn’t really help much.

  13. Transparent text output requires extra attention.

  14. The TTM_ADJUSTRECT message does the heavy lifting.

  15. If you have a very high number of tools in one tooltip.

  16. The text foreground and background colors play a role.

  17. Large scale color changes by changing four bytes for each color.

  18. It’s just WM_SETCURSOR, but that in itself is rather complicated.

  19. Both require special handling.

  20. For making dialog controls match a menu, as if anybody even does this any more.

Comments are closed.