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. Anonymous says:

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

  2. Anonymous says:

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

  3. Anonymous says:

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

  4. Anonymous says:

    They allow information to cross security boundaries.

  5. Anonymous says:

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

  6. Anonymous says:

    Thinking through message pumping.

  7. Anonymous says:

    The two usually agree but are not required to.

  8. Anonymous says:

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

  9. Anonymous says:

    Writing your own dialog loop.

  10. Anonymous says:

    It gives you a one-shot solid color brush.

  11. Anonymous says:

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

  12. Anonymous says:

    It doesn’t really help much.

  13. Anonymous says:

    Transparent text output requires extra attention.

  14. Anonymous says:

    The TTM_ADJUSTRECT message does the heavy lifting.

  15. Anonymous says:

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

  16. Anonymous says:

    The text foreground and background colors play a role.

  17. Anonymous says:

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

  18. Anonymous says:

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

  19. Anonymous says:

    Both require special handling.

  20. Anonymous says:

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

Comments are closed.