PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

MS Windows Sample Code

The following document is from the Microsoft Programmer’s Library 1.3 CD-ROM.

Microsoft Windows S.D.K. v3.0 Sample `C' Source Code


BITMAP.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\BITMAP\BITMAP.C

/****************************************************************************

    PROGRAM: Bitmap.c

    PURPOSE: Demonstrates how to use bitmap

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box
        MakeColorBitmap(HWND) - creates a color bitmap

    COMMENTS:

        This application is linked with select.exe which is a library
        module.  For the source code, look in the \select\ directory, and
        in Appendix C of the Windows Programmer's Learning Guide.

****************************************************************************/

#include "windows.h"
#include "bitmap.h"
 "select.h"                /* used to link with select.exe */

HANDLE hInst;

/* Patterns used for the background */

short White[] =  { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
short Black[] =  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
short Zigzag[] = { 0xFF, 0xF7, 0xEB, 0xDD, 0xBE, 0x7F, 0xFF, 0xFF };
short CrossHatch[] = { 0xEF, 0xEF, 0xEF, 0xEF, 0x00, 0xEF, 0xEF, 0xEF };

/* handles used for the various bitmaps */

HBITMAP hPattern1;
HBITMAP hPattern2;
HBITMAP hPattern3;
HBITMAP hPattern4;
HBITMAP hBitmap1;
HBITMAP hBitmap2;
HBITMAP hBitmap3;
HBITMAP hMenuBitmap1;
HBITMAP hMenuBitmap2;
HBITMAP hMenuBitmap3;
HBITMAP hBitmap;
HBITMAP hOldBitmap;

HBRUSH hBrush;                         /* brush handle
int fStretchMode;                      /* type of stretch mode to use

HDC hDC;                               /* handle to device context
HDC hMemoryDC;                         /* handle to memory device context
BITMAP Bitmap;                         /* bitmap structure

BOOL bTrack = FALSE;                   /* TRUE if user is selecting a region
RECT Rect;

/* The following variables keep track of which menu item is checked */

WORD wPrevBitmap = IDM_BITMAP1;
WORD wPrevPattern = IDM_PATTERN1;
WORD wPrevMode = IDM_WHITEONBLACK;
WORD wPrevItem;

int Shape = SL_BLOCK;            /* shape to use for the selection rectangle

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = CS_VREDRAW | CS_HREDRAW;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "BitmapMenu";
    wc.lpszClassName = "BitmapWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    HWND            hwnd;

    hInst = hInstance;

    hwnd = CreateWindow(
        "BitmapWClass",
        "Bitmap Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND    - application menu (About dialog box)
        WM_CREATE      - create window and objects
        WM_LBUTTONDOWN - begin selection
        WM_MOUSEMOVE   - keep track of mouse movement during selection
        WM_LBUTTONUP   - end selection, draw bitmap
        WM_RBUTTONUP   - draw bitmap without resizing
        WM_ERASEBKGND  - erase background and redraw
        WM_DESTROY     - destroy window

    COMMENTS:

        User may select a "normal" size bitmap by pressing the right mouse
        button, or set the size of the bitmap to display by using the left
        button to define a region.  The routines to display the selection
        region are defined in the library module select.exe.
  WM_DESTROY    - destroy window

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;

    HMENU hMenu;
    HBRUSH hOldBrush;
    HBITMAP hOurBitmap;

    switch (message) {
        case WM_CREATE:                            /* message: create window

            hPattern1 = CreateBitmap(8, 8, 1, 1, (LPSTR) White);
            hPattern2 = CreateBitmap(8, 8, 1, 1, (LPSTR) Black);
            hPattern3 = CreateBitmap(8, 8, 1, 1, (LPSTR) Zigzag);
            hPattern4 = CreateBitmap(8, 8, 1, 1, (LPSTR) CrossHatch);

            hBitmap1 = LoadBitmap(hInst, "dog");
            hBitmap2 = LoadBitmap(hInst, "cat");
            hBitmap3 = MakeColorBitmap(hWnd);

            hMenuBitmap1 = LoadBitmap(hInst, "dog");
            hMenuBitmap2 = LoadBitmap(hInst, "cat");
            hMenuBitmap3 = MakeColorBitmap(hWnd);

            hMenu = CreateMenu();
      AppendMenu(hMenu, MF_STRING | MF_CHECKED, IDM_PATTERN1, "&White");
      AppendMenu(hMenu, MF_STRING, IDM_PATTERN2, "&Black");
            AppendMenu(hMenu, MF_BITMAP, IDM_PATTERN3,
           (LPSTR)(LONG)hPattern3);
            AppendMenu(hMenu, MF_BITMAP, IDM_PATTERN4,
           (LPSTR)(LONG)hPattern4);

      ModifyMenu(GetMenu(hWnd), 1, MF_POPUP | MF_BYPOSITION,
           (WORD)hMenu, "&Pattern");

            hMenu = CreateMenu();

            /* Use bitmaps for menu items */
            AppendMenu(hMenu, MF_BITMAP | MF_CHECKED, IDM_BITMAP1,
           (LPSTR)(LONG) hMenuBitmap1);
            AppendMenu(hMenu, MF_BITMAP, IDM_BITMAP2,
           (LPSTR)(LONG) hMenuBitmap2);
            AppendMenu(hMenu, MF_BITMAP, IDM_BITMAP3,
           (LPSTR)(LONG) hMenuBitmap3);
            ModifyMenu(GetMenu(hWnd), 0, MF_POPUP | MF_BYPOSITION,
                       (WORD) hMenu, "&Bitmap");

            hBrush = CreatePatternBrush(hPattern1);
            fStretchMode = IDM_BLACKONWHITE;

            /* Select the first bitmap */

            hDC = GetDC(hWnd);
            hMemoryDC = CreateCompatibleDC(hDC);
            ReleaseDC(hWnd, hDC);
            hOldBitmap = SelectObject(hMemoryDC, hBitmap1);
            GetObject(hBitmap1, 16, (LPSTR) &Bitmap);

            break;

        case WM_LBUTTONDOWN:           /* message: left mouse button pressed

            /* Start selection of region */

            bTrack = TRUE;
            SetRectEmpty(&Rect);
            StartSelection(hWnd, MAKEPOINT(lParam), &Rect,
                (wParam & MK_SHIFT) ? (SL_EXTEND | Shape) : Shape);
            break;

        case WM_MOUSEMOVE:                        /* message: mouse movement

            /* Update the selection region */

            if (bTrack)
                UpdateSelection(hWnd, MAKEPOINT(lParam), &Rect, Shape);
            break;

        case WM_LBUTTONUP:            /* message: left mouse button released

            if (bTrack) {
               /* End the selection */

               EndSelection(MAKEPOINT(lParam), &Rect);
               ClearSelection(hWnd, &Rect, Shape);

               hDC = GetDC(hWnd);
               SetStretchBltMode(hDC, fStretchMode);
               StretchBlt(hDC, Rect.left, Rect.top,
                   Rect.right - Rect.left, Rect.bottom - Rect.top,
                  hMemoryDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, SRCCOPY);
               ReleaseDC(hWnd, hDC);
            }

            bTrack = FALSE;
            break;

        case WM_RBUTTONUP:           /* message: right mouse button released

            /* Display a normal sized bitmap */

            hDC = GetDC(hWnd);
            BitBlt(hDC, LOWORD(lParam), HIWORD(lParam),
                Bitmap.bmWidth, Bitmap.bmHeight, hMemoryDC, 0, 0, SRCCOPY);
            ReleaseDC(hWnd, hDC);
            break;

        case WM_ERASEBKGND:                    /* messsage: erase background

            /* Repaint the background */

            UnrealizeObject(hBrush);
            hOldBrush = SelectObject(wParam, hBrush);
            GetClientRect(hWnd, &Rect);
            PatBlt(wParam, Rect.left, Rect.top,
                Rect.right-Rect.left, Rect.bottom-Rect.top, PATCOPY);
            SelectObject(wParam, hOldBrush);
            return TRUE;

  case WM_COMMAND:
            switch (wParam) {
                case IDM_ABOUT:
        lpProcAbout = MakeProcInstance(About, hInst);
        DialogBox(hInst,
            "AboutBox",
            hWnd,
            lpProcAbout);
        FreeProcInstance(lpProcAbout);
        break;

                case IDM_BITMAP1:
                    wPrevItem = wPrevBitmap;
                    wPrevBitmap = wParam;
                    GetObject(hBitmap1, 16, (LPSTR) &Bitmap);
                    SelectObject(hMemoryDC, hBitmap1);
                    break;

                case IDM_BITMAP2:
                    wPrevItem = wPrevBitmap;
                    wPrevBitmap = wParam;
                    GetObject(hBitmap2, 16, (LPSTR) &Bitmap);
                    SelectObject(hMemoryDC, hBitmap2);
                    break;

                case IDM_BITMAP3:
                    wPrevItem = wPrevBitmap;
                    wPrevBitmap = wParam;
                    GetObject(hBitmap3, 16, (LPSTR) &Bitmap);
                    hOurBitmap = SelectObject(hMemoryDC, hBitmap3);
                    break;

                /* Pattern menu: select the background brush to use */

                case IDM_PATTERN1:
                    wPrevItem = wPrevPattern;
                    wPrevPattern = wParam;
                    DeleteObject(hBrush);
                    hBrush = CreatePatternBrush(hPattern1);
                    InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
                    UpdateWindow(hWnd);
                    break;

                case IDM_PATTERN2:
                    wPrevItem = wPrevPattern;
                    wPrevPattern = wParam;
                    DeleteObject(hBrush);
                    hBrush = CreatePatternBrush(hPattern2);
                    InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
                    UpdateWindow(hWnd);
                    break;

                case IDM_PATTERN3:
                    wPrevItem = wPrevPattern;
                    wPrevPattern = wParam;
                    DeleteObject(hBrush);
                    hBrush = CreatePatternBrush(hPattern3);
                    InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
                    UpdateWindow(hWnd);
                    break;

                case IDM_PATTERN4:
                    wPrevItem = wPrevPattern;
                    wPrevPattern = wParam;
                    DeleteObject(hBrush);
                    hBrush = CreatePatternBrush(hPattern4);
                    InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
                    UpdateWindow(hWnd);
                    break;

                /* Mode menu: select the stretch mode to use */

                case IDM_BLACKONWHITE:
                    wPrevItem = wPrevMode;
                    wPrevMode = wParam;
                    fStretchMode = BLACKONWHITE;
                    break;

                case IDM_WHITEONBLACK:
                    wPrevItem = wPrevMode;
                    wPrevMode = wParam;
                    fStretchMode = WHITEONBLACK;
                    break;

                case IDM_COLORONCOLOR:
                    wPrevItem = wPrevMode;
                    wPrevMode = wParam;
                    fStretchMode = COLORONCOLOR;
                    break;
            }

            /* Uncheck the old item, check the new item */

            CheckMenuItem(GetMenu(hWnd), wPrevItem, MF_UNCHECKED);
            CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
            break;


  case WM_DESTROY:
            SelectObject(hMemoryDC, hOldBitmap);
            DeleteDC(hMemoryDC);
            DeleteObject(hBrush);
            DeleteObject(hPattern1);
            DeleteObject(hPattern2);
            DeleteObject(hPattern3);
            DeleteObject(hPattern4);
            DeleteObject(hBitmap1);
            DeleteObject(hBitmap2);
            DeleteObject(hBitmap3);
            DeleteObject(hMenuBitmap1);
            DeleteObject(hMenuBitmap2);
            DeleteObject(hMenuBitmap3);

      PostQuitMessage(0);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}



/****************************************************************************

    FUNCTION: MakeColorBitmap(HWND)

    PURPOSE: Creates a color bitmap

    COMMENTS:

        This creates a plaid color bitmap by using overlappying colors.

****************************************************************************/

HBITMAP MakeColorBitmap(hWnd)
HWND hWnd;
{
    HDC hDC;
    HDC hMemoryDC;
    HBITMAP hBitmap;
    HBITMAP hOldBitmap;
    HBRUSH hRedBrush;
    HBRUSH hGreenBrush;
    HBRUSH hBlueBrush;
    HBRUSH hOldBrush;

    hDC = GetDC(hWnd);
    hMemoryDC = CreateCompatibleDC(hDC);
    hBitmap = CreateCompatibleBitmap(hDC, 64, 32);
    hOldBitmap = SelectObject(hMemoryDC, hBitmap);
    hRedBrush = CreateSolidBrush(RGB(255,0,0));
    hGreenBrush = CreateSolidBrush(RGB(0,255,0));
    hBlueBrush = CreateSolidBrush(RGB(0,0,255));

    PatBlt(hMemoryDC, 0, 0, 64, 32, BLACKNESS);
    hOldBrush = SelectObject(hMemoryDC, hRedBrush);
    PatBlt(hMemoryDC, 0, 0, 24, 11, PATORDEST);
    PatBlt(hMemoryDC, 40, 10, 24, 12, PATORDEST);
    PatBlt(hMemoryDC, 20, 21, 24, 11, PATORDEST);
    SelectObject(hMemoryDC, hGreenBrush);
    PatBlt(hMemoryDC, 20, 0, 24, 11, PATORDEST);
    PatBlt(hMemoryDC, 0, 10, 24, 12, PATORDEST);
    PatBlt(hMemoryDC, 40, 21, 24, 11, PATORDEST);
    SelectObject(hMemoryDC, hBlueBrush);
    PatBlt(hMemoryDC, 40, 0, 24, 11, PATORDEST);
    PatBlt(hMemoryDC, 20, 10, 24, 12, PATORDEST);
    PatBlt(hMemoryDC, 0, 21, 24, 11, PATORDEST);

    SelectObject(hMemoryDC, hOldBrush);
    DeleteObject(hRedBrush);
    DeleteObject(hGreenBrush);
    DeleteObject(hBlueBrush);
    SelectObject(hMemoryDC, hOldBitmap);
    DeleteDC(hMemoryDC);
    ReleaseDC(hWnd, hDC);
    return (hBitmap);
}


CFONT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SHOWFONT\CFONT.C

/****************************************************************************
    MODULE: cfont.c

    FUNCTION: CFontDlg(HWND, unsigned, WORD, LONG);

    PURPOSE: Processes dialog box messages for creating a font

****************************************************************************/

#include "windows.h"
#include "showfont.h"

BOOL FAR PASCAL CFontDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned  message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      SetDlgItemInt(hDlg, ID_HEIGHT, CLogFont.lfHeight, TRUE);
      SetDlgItemInt(hDlg, ID_WIDTH, CLogFont.lfWidth, TRUE);
      SetDlgItemInt(hDlg, ID_ESCAPEMENT,
    CLogFont.lfEscapement, TRUE);
      SetDlgItemInt(hDlg, ID_ORIENTATION,
    CLogFont.lfOrientation, TRUE);
      SetDlgItemText(hDlg, ID_FACE, CLogFont.lfFaceName);
      CheckDlgButton(hDlg, ID_ITALIC, CLogFont.lfItalic);
      CheckDlgButton(hDlg, ID_UNDERLINE, CLogFont.lfUnderline);
      CheckDlgButton(hDlg, ID_STRIKEOUT, CLogFont.lfStrikeOut);

      SetDlgItemInt(hDlg, ID_WEIGHT, CLogFont.lfWeight, TRUE);
      switch (CLogFont.lfWeight) {
    case FW_LIGHT:
        CheckRadioButton(hDlg, ID_LIGHT, ID_BOLD, ID_LIGHT);
        break;

    case FW_NORMAL:
        CheckRadioButton(hDlg, ID_LIGHT, ID_BOLD, ID_NORMAL);
        break;

    case FW_BOLD:
        CheckRadioButton(hDlg, ID_LIGHT, ID_BOLD, ID_BOLD);
        break;
      }

      SetDlgItemInt(hDlg, ID_CHARSET, CLogFont.lfCharSet, TRUE);
      switch (CLogFont.lfCharSet) {
    case ANSI_CHARSET:
        CheckRadioButton(hDlg, ID_ANSI, ID_OEM, ID_ANSI);
        break;

    case OEM_CHARSET:
        CheckRadioButton(hDlg, ID_ANSI, ID_OEM, ID_OEM);
        break;
      }

      switch (CLogFont.lfOutPrecision) {
    case OUT_STRING_PRECIS:
        CheckRadioButton(hDlg, ID_OUT_STRING, ID_OUT_DEFAULT,
      ID_OUT_STRING);
        break;

    case OUT_CHARACTER_PRECIS:
        CheckRadioButton(hDlg, ID_OUT_STRING, ID_OUT_DEFAULT,
      ID_OUT_CHAR);
        break;

    case OUT_STROKE_PRECIS:
        CheckRadioButton(hDlg, ID_OUT_STRING, ID_OUT_DEFAULT,
      ID_OUT_STROKE);
        break;

    case OUT_DEFAULT_PRECIS:
        CheckRadioButton(hDlg, ID_OUT_STRING, ID_OUT_DEFAULT,
      ID_OUT_DEFAULT);
        break;
      }

      switch (CLogFont.lfClipPrecision) {
    case CLIP_CHARACTER_PRECIS:
        CheckRadioButton(hDlg, ID_CLIP_CHAR, ID_CLIP_DEFAULT,
      ID_CLIP_CHAR);
        break;

    case CLIP_STROKE_PRECIS:
        CheckRadioButton(hDlg, ID_CLIP_CHAR, ID_CLIP_DEFAULT,
      ID_CLIP_STROKE);
        break;

    case CLIP_DEFAULT_PRECIS:
        CheckRadioButton(hDlg, ID_CLIP_CHAR, ID_CLIP_DEFAULT,
      ID_CLIP_DEFAULT);
        break;
      }

      switch (CLogFont.lfQuality) {
    case PROOF_QUALITY:
        CheckRadioButton(hDlg, ID_PROOF, ID_DEF_QUALITY, ID_PROOF);
        break;

    case DRAFT_QUALITY:
        CheckRadioButton(hDlg, ID_PROOF, ID_DEF_QUALITY, ID_DRAFT);
        break;

    case DEFAULT_QUALITY:
        CheckRadioButton(hDlg, ID_PROOF, ID_DEF_QUALITY,
      ID_DEF_QUALITY);
        break;
      }

      switch ((CLogFont.lfPitchAndFamily) & 3) {
    case FIXED_PITCH:
        CheckRadioButton(hDlg, ID_FIXED, ID_DEF_PITCH, ID_FIXED);
        break;

    case VARIABLE_PITCH:
        CheckRadioButton(hDlg, ID_FIXED, ID_DEF_PITCH, ID_VARIABLE);
        break;

    case DEFAULT_PITCH:
        CheckRadioButton(hDlg, ID_FIXED, ID_DEF_PITCH,
      ID_DEF_PITCH);
        break;
      }

      switch ((CLogFont.lfPitchAndFamily) & 240) {
    case FF_ROMAN:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_ROMAN);
        break;

    case FF_SWISS:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_SWISS);
        break;

    case FF_MODERN:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_MODERN);
        break;

    case FF_SCRIPT:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_SCRIPT);
        break;

    case FF_DECORATIVE:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_DECO);
        break;

    case FF_DONTCARE:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY,
      ID_DEF_FAMILY);
        break;
      }
      break;

  case WM_COMMAND:
      switch (wParam) {
    case IDOK:
        CLogFont.lfHeight = GetDlgItemInt(hDlg,
      ID_HEIGHT, NULL, TRUE);
        CLogFont.lfWidth = GetDlgItemInt(hDlg,
      ID_WIDTH, NULL, TRUE);
        CLogFont.lfEscapement = GetDlgItemInt(hDlg,
      ID_ESCAPEMENT, NULL, FALSE);
        CLogFont.lfOrientation = GetDlgItemInt(hDlg,
      ID_ORIENTATION, NULL, FALSE);
        GetDlgItemText(hDlg, ID_FACE, CLogFont.lfFaceName, 32);
        CLogFont.lfWeight = GetDlgItemInt(hDlg,
      ID_WEIGHT, NULL, FALSE);
        CLogFont.lfCharSet = GetDlgItemInt(hDlg,
      ID_CHARSET, NULL, FALSE);
        EndDialog(hDlg, 1);
        break;

    case IDCANCEL:
        EndDialog(hDlg, 0);
        break;

    case ID_ITALIC:
        CLogFont.lfItalic = IsDlgButtonChecked(hDlg, ID_ITALIC);
        break;

    case ID_UNDERLINE:
        CLogFont.lfUnderline = IsDlgButtonChecked(hDlg,
      ID_UNDERLINE);
        break;

    case ID_STRIKEOUT:
        CLogFont.lfStrikeOut = IsDlgButtonChecked(hDlg,
      ID_STRIKEOUT);
        break;

    case ID_LIGHT:
        SetDlgItemInt(hDlg, ID_WEIGHT, CLogFont.lfWeight, TRUE);
        CheckRadioButton(hDlg, ID_LIGHT, ID_BOLD, ID_LIGHT);
        CLogFont.lfWeight = FW_LIGHT;
        break;

    case ID_NORMAL:
        SetDlgItemInt(hDlg, ID_WEIGHT, CLogFont.lfWeight, TRUE);
        CheckRadioButton(hDlg, ID_LIGHT, ID_BOLD, ID_NORMAL);
        CLogFont.lfWeight = FW_NORMAL;
        break;

    case ID_BOLD:
        SetDlgItemInt(hDlg, ID_WEIGHT, CLogFont.lfWeight, TRUE);
        CheckRadioButton(hDlg, ID_LIGHT, ID_BOLD, ID_BOLD);
        CLogFont.lfWeight = FW_BOLD;
        break;

    case ID_WEIGHT:
        CheckDlgButton(hDlg, ID_LIGHT, FALSE);
        CheckDlgButton(hDlg, ID_NORMAL, FALSE);
        CheckDlgButton(hDlg, ID_BOLD, FALSE);
        break;

    case ID_ANSI:
        SetDlgItemInt(hDlg, ID_CHARSET, CLogFont.lfCharSet, TRUE);
        CheckRadioButton(hDlg, ID_ANSI, ID_OEM, ID_ANSI);
        break;

    case ID_OEM:
        SetDlgItemInt(hDlg, ID_CHARSET, CLogFont.lfCharSet, TRUE);
        CheckRadioButton(hDlg, ID_ANSI, ID_OEM, ID_OEM);
        break;

    case ID_CHARSET:
        CheckDlgButton(hDlg, ID_ANSI, FALSE);
        CheckDlgButton(hDlg, ID_OEM, FALSE);
        break;

    case ID_OUT_STRING:
        CheckRadioButton(hDlg, ID_OUT_STRING, ID_OUT_DEFAULT,
      ID_OUT_STRING);
        CLogFont.lfOutPrecision = OUT_STRING_PRECIS;
        break;

    case ID_OUT_CHAR:
        CheckRadioButton(hDlg, ID_OUT_STRING, ID_OUT_DEFAULT,
      ID_OUT_CHAR);
        CLogFont.lfOutPrecision = OUT_CHARACTER_PRECIS;
        break;

    case ID_OUT_STROKE:
        CheckRadioButton(hDlg, ID_OUT_STRING, ID_OUT_DEFAULT,
      ID_OUT_STROKE);
        CLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
        break;

    case ID_OUT_DEFAULT:
        CheckRadioButton(hDlg, ID_OUT_STRING, ID_OUT_DEFAULT,
      ID_OUT_DEFAULT);
        CLogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
        break;

    case ID_CLIP_CHAR:
        CheckRadioButton(hDlg, ID_CLIP_CHAR, ID_CLIP_DEFAULT,
      ID_CLIP_CHAR);
        CLogFont.lfClipPrecision = CLIP_CHARACTER_PRECIS;
        break;

    case ID_CLIP_STROKE:
        CheckRadioButton(hDlg, ID_CLIP_CHAR, ID_CLIP_DEFAULT,
      ID_CLIP_STROKE);
        CLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
        break;

    case ID_CLIP_DEFAULT:
        CheckRadioButton(hDlg, ID_CLIP_CHAR, ID_CLIP_DEFAULT,
      ID_CLIP_DEFAULT);
        CLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
        break;

    case ID_PROOF:
        CheckRadioButton(hDlg, ID_PROOF, ID_DEF_QUALITY, ID_PROOF);
        CLogFont.lfQuality = PROOF_QUALITY;
        break;

    case ID_DRAFT:
        CheckRadioButton(hDlg, ID_PROOF, ID_DEF_QUALITY, ID_DRAFT);
        CLogFont.lfQuality = DRAFT_QUALITY;
        break;

    case ID_DEF_QUALITY:
        CheckRadioButton(hDlg, ID_PROOF, ID_DEF_QUALITY,
      ID_DEF_QUALITY);
        CLogFont.lfQuality = DEFAULT_QUALITY;
        break;

    case ID_FIXED:
        CheckRadioButton(hDlg, ID_FIXED, ID_DEF_PITCH, ID_FIXED);
        CLogFont.lfPitchAndFamily =
      (~3 & CLogFont.lfPitchAndFamily) | FIXED_PITCH;
        break;

    case ID_VARIABLE:
        CheckRadioButton(hDlg, ID_FIXED, ID_DEF_PITCH, ID_VARIABLE);
        CLogFont.lfPitchAndFamily =
      (~3 & CLogFont.lfPitchAndFamily) | VARIABLE_PITCH;
        break;

    case ID_DEF_PITCH:
        CheckRadioButton(hDlg, ID_FIXED, ID_DEF_PITCH,
      ID_DEF_PITCH);
        CLogFont.lfPitchAndFamily =
      (~3 & CLogFont.lfPitchAndFamily) | DEFAULT_PITCH;
        break;

    case ID_ROMAN:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_ROMAN);
        CLogFont.lfPitchAndFamily =
      (~240 & CLogFont.lfPitchAndFamily) | FF_ROMAN;
        break;

    case ID_SWISS:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_SWISS);
        CLogFont.lfPitchAndFamily =
      (~240 & CLogFont.lfPitchAndFamily) | FF_SWISS;
        break;

    case ID_MODERN:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_MODERN);
        CLogFont.lfPitchAndFamily =
      (~240 & CLogFont.lfPitchAndFamily) | FF_MODERN;
        break;

    case ID_SCRIPT:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_SCRIPT);
        CLogFont.lfPitchAndFamily =
      (~240 & CLogFont.lfPitchAndFamily) | FF_SCRIPT;
        break;

    case ID_DECO:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY, ID_DECO);
        CLogFont.lfPitchAndFamily =
      (~240 & CLogFont.lfPitchAndFamily) | FF_DECORATIVE;
        break;

    case ID_DEF_FAMILY:
        CheckRadioButton(hDlg, ID_ROMAN, ID_DEF_FAMILY,
      ID_DEF_FAMILY);
        CLogFont.lfPitchAndFamily =
      (~240 & CLogFont.lfPitchAndFamily) | FF_DONTCARE;
        break;

      }
      break;
       }
       return (FALSE);
}


CLIDATA.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\DDE\CLIDATA.C

/****************************************************************************

    MODULE: CLIDATA.C

    PURPOSE: Maintains conversation data and paints data in the client
       application window.

****************************************************************************/

#include "windows.h"
#include "clires.h"
#include "client.h"
#include <string.h>
#include <stdlib.h>

typedef struct ITEM
{
    char szItem[ITEM_MAX_SIZE+1];
    char szValue[VALUE_MAX_SIZE+1];
};

typedef struct CONV
{
    BOOL  bInTerminate;
    enum PENDINGACK ePendingAck;
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
    char  szApplication[APP_MAX_SIZE+1];
    char  szTopic[TOPIC_MAX_SIZE+1];
    int   nItemCount;
    struct ITEM Item[ITEMS_PER_CONV_MAX_COUNT];
};

static struct CONV Conv[CONV_MAX_COUNT];
static int   nConvCount = 0;
static HWND  hwndNewClientDDE; /* used by SelectNewConvDlgProc */
static HWND  hwndNewServerDDE; /* new conversation selected by user */

static char  szSelectedApplication[APP_MAX_SIZE+1];
static char  szSelectedTopic[TOPIC_MAX_SIZE+1];
static char  szSelectedItem[ITEM_MAX_SIZE+1];
static char  szSelectedValue[VALUE_MAX_SIZE+1];

static struct CONV * NEAR FindConv(HWND);
static struct ITEM * NEAR FindItem(HWND, char *);

BOOL FAR PASCAL SelectNewConvDlgProc(HWND, unsigned, WORD, LONG);

/*****************************************************************

    FUNCTION: AddItemToConv

    PURPOSE:  Add a data item to an existing conversation.

*****************************************************************/
BOOL AddItemToConv(hwndClientDDE, szItem)
    HWND   hwndClientDDE;
    char * szItem;
{
    struct CONV * pConv;
    struct ITEM * pItem;

    if (!(pConv = FindConv(hwndClientDDE)))
        return (FALSE);
    if (pConv->nItemCount >= ITEMS_PER_CONV_MAX_COUNT)
        return (FALSE);
   pItem =  pConv->Item + pConv->nItemCount++;
   strcpy(pItem->szItem, szItem);
   pItem->szValue[0] = 0;
   return (TRUE);
}




/**********************************************************

    FUNCTION:  AtLeastOneConvActive

    PURPOSE:   Used during termination of application, to
         determine whether pending DDE terminations
         have completed.

***********************************************************/
BOOL AtLeastOneConvActive()
{
    return (nConvCount? TRUE: FALSE);
}



/*****************************************************************

    FUNCTION: AddConv

    PURPOSE:  Initialize items in CONV structure.

*****************************************************************/
BOOL AddConv(hwndClientDDE, hwndServerDDE, szApplication, szTopic)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
    char * szApplication;
    char * szTopic;
{
    struct CONV * pConv;

    if (nConvCount >= CONV_MAX_COUNT)
        return (FALSE);
    pConv = Conv + nConvCount++;
    pConv->bInTerminate = FALSE;
    pConv->ePendingAck = NONE;
    pConv->hwndClientDDE = hwndClientDDE;
    pConv->hwndServerDDE = hwndServerDDE;
    strcpy(pConv->szApplication, szApplication);
    strcpy(pConv->szTopic, szTopic);
    pConv->nItemCount = 0;
    return (TRUE);
}



/*****************************************************************

    FUNCTION: DoesAdviseAlreadyExist

    PURPOSE:  Determines whether hot/warm link has already been
        established for specified conversation item.

*****************************************************************/
BOOL DoesAdviseAlreadyExist(hwndClientDDE, szItem)
    HWND hwndClientDDE;
    char * szItem;
{
    return (FindItem(hwndClientDDE,szItem)==NULL?FALSE:TRUE);
}



/*****************************************************************

    FUNCTION: FindItem

    PURPOSE:  Return pointer to item structure given conversation and item.

*****************************************************************/
struct ITEM * NEAR FindItem(hwndClientDDE, szItem)
    HWND hwndClientDDE;
    char * szItem;
{
    struct CONV * pConv;
    struct ITEM * pItem;
    int           nItemIndex;

    if (!(pConv = FindConv(hwndClientDDE)))
        return (NULL);
    for (pItem = pConv->Item, nItemIndex = 0;
   nItemIndex < pConv->nItemCount;
         pItem++, nItemIndex++)
    {
        if (!strcmpi(szItem, pItem->szItem))
        {
            return (pItem);
        }
    }
    return (NULL);
}


/*****************************************************************

    FUNCTION: FindConv

    PURPOSE:  Return pointer to conversation structure given handle
        to client DDE window.

*****************************************************************/
struct CONV * NEAR FindConv(hwndClientDDE)
    HWND  hwndClientDDE;
{
    struct CONV * pConv;
    int     nConvIndex;

    for (pConv = Conv, nConvIndex = 0;
   nConvIndex < nConvCount;
   pConv++, nConvIndex++)
    {
  if (pConv->hwndClientDDE == hwndClientDDE)
      return (pConv);
    }
    return (NULL);
}


/***************************************************************

    FUNCTION:  FindConvGivenAppTopic

    PURPOSE:   Find handle of client DDE window given
         application and topic for an active conversation.

***************************************************************/
HWND FindConvGivenAppTopic(szApp, szTopic)
    char * szApp;
    char * szTopic;
{
    struct CONV * pConv;
    int     nConvIndex;

    for (pConv = Conv, nConvIndex = 0;
   nConvIndex < nConvCount;
   pConv++, nConvIndex++)
    {
   if (!(stricmp(pConv->szApplication, szApp))
       && !(stricmp(pConv->szTopic, szTopic)))
   {
       return pConv->hwndClientDDE;
   }
    }
    return NULL;
}


/*****************************************************************

    FUNCTION: GetAppAndTopic

    PURPOSE:  Get conversation's application and topic strings.

*****************************************************************/
void GetAppAndTopic(hwndClientDDE, szApp, szTopic)
    HWND  hwndClientDDE;
    char * szApp;
    char * szTopic;
{
    struct CONV * pConv;

    if (!(pConv = FindConv(hwndClientDDE)))
        return;
    strcpy(szApp, pConv->szApplication);
    strcpy(szTopic, pConv->szTopic);
    return;
}



/****************************************************************

    FUNCTION: GetConvPendingAck

    PURPOSE:  Return state of acknowledgement for specified
        conversation.

****************************************************************/
enum PENDINGACK GetConvPendingAck(hwndClientDDE)
    HWND hwndClientDDE;
{
    struct CONV * pConv;

    if (pConv = FindConv(hwndClientDDE))
  return pConv->ePendingAck;
    else
        return NONE;
}



/*****************************************************************

    FUNCTION: GetHwndServerDDE

    PURPOSE:  Gets the hwnd of the server, given the handle of
        the client DDE window.

*****************************************************************/
HWND GetHwndServerDDE(hwndClientDDE)
    HWND  hwndClientDDE;
{
    struct CONV * pConv;
    int  nConvIndex;

    for (pConv = Conv, nConvIndex = 0;
   nConvIndex < nConvCount;
   pConv++, nConvIndex++)
    {
  if (pConv->hwndClientDDE == hwndClientDDE)
      return (pConv->hwndServerDDE);
    }
    return (NULL);
}



/*****************************************************************

    FUNCTION: GetNextConv

    PURPOSE:  Get next conversation in the list of current conversations.
        To get the first conversation, pass a NULL hwnd.

*****************************************************************/
HWND GetNextConv(hwndClientDDE)
    HWND hwndClientDDE;
{
    struct CONV * pConv;
    int     nConvIndex;

    if (hwndClientDDE)
    {
  for (nConvIndex = 0, pConv = Conv;
       nConvIndex < nConvCount;
       nConvIndex++, pConv++)
        {
      if (pConv->hwndClientDDE == hwndClientDDE)
            {
    if (++nConvIndex < nConvCount)
        return (++pConv)->hwndClientDDE;
                else
                    return (NULL);
            }
        }
        return (NULL);
    }
    if (nConvCount > 0)
  return (Conv[0].hwndClientDDE);
    else
        return (NULL);
}



/****************************************************************

    FUNCTION: HexToInt

    PURPOSE:  Convert from ascii to integer up to 4 chars
        representing a hexidecimal number.  The hex number
        is considered terminated by a null or any non-hex
        digit.

****************************************************************/
int HexToInt(szHex)
    char * szHex;
{
    unsigned int  nReturn;
    int     nDigit;
    int     nIndex;

    nReturn = 0;
    for (nIndex = 0; nIndex < 4; nIndex++)
    {
  if (*szHex >= '0' && *szHex <= '9')
      nDigit = *szHex - '0';
  else if (*szHex >= 'A' && *szHex <= 'F')
      nDigit = *szHex - 'A' + 10;
  else if (*szHex >= 'a' && *szHex <= 'f')
      nDigit = *szHex - 'a' + 10;
  else
      return ((int)nReturn);
  nReturn = (nReturn<<4) + nDigit;
  szHex++;
    }
    return ((int)nReturn);
}




/****************************************************************

    FUNCTION: IsConvInTerminateState

    PURPOSE:  Determine whether conversation is in process of
        being terminated.   In identifying the conversation,
        it is necessary to compare both the hwndClientDDE
        and hwndServerDDE, because during an initiate,
        there may be multiple unwanted conversations sharing
        the same hwndClientDDE.

****************************************************************/
BOOL IsConvInTerminateState(hwndClientDDE, hwndServerDDE)
    HWND hwndClientDDE;
    HWND hwndServerDDE;
{
    struct CONV * pConv;
    int  nConvIndex;

    for (nConvIndex = 0, pConv = Conv;
   nConvIndex < nConvCount;
   nConvIndex++, pConv++)
    {
  if (pConv->hwndClientDDE == hwndClientDDE
      && pConv->hwndServerDDE == hwndServerDDE)
      return pConv->bInTerminate;
    }
    return (TRUE);  /* If conversation not found, assume terminate state */
        /* to avoid possible endless terminate feedback loop */
}



/****************************************************************

    FUNCTION: IsHwndClientDDEUsed

    PURPOSE:  Determine whether hwndClientDDE is still being
        used in some second conversation after the
        first conversation has been terminated.  This
        determination is necessary during an initiate
        in which the same hwndClientDDE is used in
        multiple WM_DDE_INITIATE messages.
        being terminated.

****************************************************************/
BOOL IsHwndClientDDEUsed(hwndClientDDE)
    HWND  hwndClientDDE;
{
    struct CONV * pConv;
    int  nConvIndex;

    for (pConv = Conv, nConvIndex = 0;
   nConvIndex < nConvCount;
   pConv++, nConvIndex++)
    {
  if (pConv->hwndClientDDE == hwndClientDDE)
      return (TRUE);
    }
    return (FALSE);
}



/****************************************************************

    FUNCTION: LetUserPickConversation

    PURPOSE:  The client has initiated possibly multiple
        conversations (if a wild card application or topic
        was specified in the WM_DDE_INITIATE).  This function
        determines whether multiple servers established
        conversations with the specified hwndClientDDE.
        If so, this function asks the user to pick one of
        the conversations, and terminates the other
        conversations.

****************************************************************/
BOOL LetUserPickConversation(hwndClientDDE)
    HWND  hwndClientDDE;
{
    struct CONV * pConv;
    FARPROC  lpfnDlgProc;
    int  nCountSameWnd;
    int  nConvIndex;

    nCountSameWnd = 0;
    for (pConv = Conv, nConvIndex = 0;
   nConvIndex < nConvCount;
   pConv++, nConvIndex++)
    {
  if (pConv->hwndClientDDE == hwndClientDDE)
      nCountSameWnd++;
    }
    if (nCountSameWnd == 1)
  return (TRUE);
    if (nCountSameWnd == 0)
  return (FALSE);
    hwndNewServerDDE = NULL;
    hwndNewClientDDE = hwndClientDDE; /* make known to SelectNewConvDlgProc *
    /* Dialog proc SelectNewConvDlgProc is defined in this module */
    lpfnDlgProc = MakeProcInstance(SelectNewConvDlgProc, hInst);
    DialogBox(hInst,
        "SelectNewConversation",
        hwndMain,
        lpfnDlgProc);
    FreeProcInstance(lpfnDlgProc);

    /* Terminate unwanted conversations.  The only conversation  */
    /* wanted is the one (hwndNewServerDDE) selected by the user */
    /* in the SelectNewConv dialog box.        */

    for (pConv = Conv, nConvIndex = 0;
   nConvIndex < nConvCount;
   pConv++, nConvIndex++)
    {
  if (pConv->hwndClientDDE == hwndClientDDE
      && pConv->hwndServerDDE != hwndNewServerDDE)
  {
      SendTerminate(hwndClientDDE, pConv->hwndServerDDE);
  }
    }
    return (TRUE);

}


/****************************************************************

    FUNCTION: PaintConvData

    PURPOSE:  Paint the client application window, using all
        conversation/item information.

****************************************************************/
void PaintConvData(hwnd)
    HWND  hwnd;
{
    HDC  hDC;
    PAINTSTRUCT ps;
    int  x,y;
    int  nConvIndex, nItemIndex;
    struct CONV * pConv;
    struct ITEM * pItem;
    char          szNumber[10];

    BeginPaint(hwnd, (LPPAINTSTRUCT)&ps);
    hDC = ps.hdc;

    y = yDelta/2;  /* start 1/2 line down window */

    for (pConv = Conv, nConvIndex = 0;
   nConvIndex < nConvCount;
   pConv++, nConvIndex++)
    {
  x = xDelta/2;  /* start 1/2 char across window */
        TextOut(hDC, x, y,
      (LPSTR)pConv->szApplication,
      strlen(pConv->szApplication));
        x += ((3+APP_MAX_SIZE)*xDelta);
        TextOut(hDC, x, y,
      (LPSTR)pConv->szTopic,
      strlen(pConv->szTopic));
        x += ((3+TOPIC_MAX_SIZE) *xDelta);
  itoa(pConv->hwndClientDDE, szNumber, 16);
  strupr(szNumber);
        TextOut(hDC, x, y, (LPSTR)szNumber, strlen(szNumber));
        y += yDelta;

  for (pItem = pConv->Item, nItemIndex = 0;
       nItemIndex < pConv->nItemCount;
             pItem++, nItemIndex++)
        {
            x = 4 * xDelta;   /* Indent items 4 spaces */
            TextOut(hDC, x, y,
                (LPSTR)pItem->szItem,
                strlen(pItem->szItem));
            x += ((3+ITEM_MAX_SIZE)*xDelta);
            TextOut(hDC, x, y,
                (LPSTR)pItem->szValue,
                strlen(pItem->szValue));
            y += yDelta;
        }

    }

    ValidateRect(hwnd, (LPRECT)NULL);
    EndPaint(hwnd, (LPPAINTSTRUCT)&ps);

    return;
}



/*****************************************************************

    FUNCTION: RemoveItemFromConv

    PURPOSE:  Remove an item (advise) from an existing conversation.

*****************************************************************/
BOOL RemoveItemFromConv(hwndClientDDE, szItem)
    HWND   hwndClientDDE;
    char * szItem;
{
    struct CONV * pConv;
    int           nItemIndex;
    struct ITEM * pItem;

    if (!(pConv = FindConv(hwndClientDDE)))
        return (FALSE);
    pItem = pConv->Item;
    nItemIndex = 0;
    while (nItemIndex < pConv->nItemCount)
    {
        if (!(strcmpi(pItem->szItem, szItem)))
            break;
        pItem++;
        nItemIndex++;
    }
    pConv->nItemCount--;
    while (nItemIndex < pConv->nItemCount)
    {
        *pItem = *(pItem+1);
        pItem++;
        nItemIndex++;
    }
    return (TRUE);
}



/*****************************************************************

    FUNCTION: RemoveConv

    PURPOSE:  Remove active conversation.   It is necessary to
        specify both the client and server DDE windows,
        since during an initiate, only the server DDE
        window handles are distinguishable.

*****************************************************************/
BOOL RemoveConv(hwndClientDDE, hwndServerDDE)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
{
    struct CONV * pRemoveConv;
    struct CONV * pConv;
    int     nConvIndex;

    for (pRemoveConv = Conv, nConvIndex = 0;
   nConvIndex < nConvCount;
   pRemoveConv++, nConvIndex++)
    {
  if (pRemoveConv->hwndClientDDE == hwndClientDDE
      && pRemoveConv->hwndServerDDE == hwndServerDDE)
      break;
    }
    if (nConvIndex >= nConvCount)
        return (FALSE);
    nConvIndex = 0;
    pConv = Conv;
    while (pConv != pRemoveConv)
    {
  if (++nConvIndex >= nConvCount)
            return (FALSE);
  pConv++;
    }
    while (++nConvIndex < nConvCount)
    {
  *pConv = *(pConv+1);
  pConv++;
    }
    nConvCount--;
    return (TRUE);
}




/****************************************************************

    FUNCTION: SelectNewConvDlgProc

    PURPOSE:  Ask the user to select one of multiple conversations
        that were established during a wild card initiate.

****************************************************************/
BOOL FAR PASCAL SelectNewConvDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND hctlConvBox;
    struct CONV * pConv;
    int  nConvIndex;
    char szConvInfo[CONVINFO_MAX_SIZE];
    char * pcBlank;
    int    nIndex;

    switch (message)
    {
  case WM_INITDIALOG:
      hctlConvBox = GetDlgItem(hdlg, IDC_CONVBOX);
      SendMessage(hctlConvBox, LB_RESETCONTENT, 0, 0L);
      for (pConv = Conv, nConvIndex = 0;
     nConvIndex < nConvCount;
     pConv++, nConvIndex++)
      {
    /* If this is one of the newly initiated conversations */
    /* then add it to the list box.            */
    if (pConv->hwndClientDDE == hwndNewClientDDE)
    {
        /* Display server's DDE window in the list box, */
        /* as that is the only way to distinguish      */
        /* conversations at this point.        */
        itoa((int)pConv->hwndServerDDE, szConvInfo, 16);
        strupr(szConvInfo);
        strcat(szConvInfo, " ");
        strcat(szConvInfo, pConv->szApplication);
        strcat(szConvInfo, " | ");
        strcat(szConvInfo, pConv->szTopic);

        SendMessage(hctlConvBox,
      LB_ADDSTRING,
      0,
      (LONG)(LPSTR)szConvInfo);
    }
      }
      return (TRUE);

  case WM_COMMAND:
      switch (wParam)
      {
    case IDC_CANCEL:
        /* Function LetUserPickConveresation will terminate */
        /* all newly initiated conversations.    */
        hwndNewServerDDE = NULL;
        EndDialog(hdlg, FALSE);
        return (TRUE);

    case IDC_OK:
        /* Function LetUserPickConversation will terminate */
        /* all newly initiated conversations, except the   */
        /* one selected by the user: hwndNewServerDDE.     */
        hwndNewServerDDE = NULL;
        hctlConvBox = GetDlgItem(hdlg, IDC_CONVBOX);
        if ((nIndex = SendMessage(hctlConvBox,
                LB_GETCURSEL, 0, 0L))
      != LB_ERR)
        {
      szConvInfo[0] = 0;
      SendMessage(hctlConvBox,
          LB_GETTEXT,
          nIndex,
          (LONG)(LPSTR)szConvInfo);
      if (pcBlank = strchr(szConvInfo, ' '))
      {
          *pcBlank = 0; /* terminate hwnd numeric value */
          hwndNewServerDDE = HexToInt(szConvInfo);
      }
        }
        EndDialog(hdlg, TRUE);
        return (TRUE);

    default:
        return (FALSE);
      }
    }
    return (FALSE);
}



/****************************************************************

    FUNCTION: SetConvInTerminateState

    PURPOSE:  Set conversation terminate state to TRUE.

****************************************************************/
void SetConvInTerminateState(hwndClientDDE, hwndServerDDE)
    HWND hwndClientDDE;
    HWND hwndServerDDE;
{
    struct CONV * pConv;
    int  nConvIndex;

    for (nConvIndex = 0, pConv = Conv;
   nConvIndex < nConvCount;
   nConvIndex++, pConv++)
    {
  if (pConv->hwndClientDDE == hwndClientDDE
      && pConv->hwndServerDDE == hwndServerDDE)
  {
      pConv->bInTerminate = TRUE;
      return;
  }
    }
    return;
}



/****************************************************************

    FUNCTION: SetConvItemValue

    PURPOSE:  Find server data item and set value.

****************************************************************/
BOOL SetConvItemValue(hwndClientDDE, szItem, lpszValue)
    HWND hwndClientDDE;
    char * szItem;
    LPSTR  lpszValue;
{
   struct ITEM * pItem;
   char        * pcValue;

    if (pItem = FindItem(hwndClientDDE, szItem))
    {
        pcValue = pItem->szValue;
        /* copy until <CR> in CF_TEXT data */
        while (*lpszValue != '\r' && *lpszValue)
        {
            *pcValue++ = *lpszValue++;
        }
        *pcValue++ = 0;
  /* Repaint client application window */
  InvalidateRect(hwndMain, NULL, TRUE);
        return (TRUE);
    }
    return (FALSE);
}



/****************************************************************

    FUNCTION: SetConvPendingAck

    PURPOSE:  Set the state of acknowledgement for a specified
        conversation.

****************************************************************/
void SetConvPendingAck(hwndClientDDE, ePendingAck)
    HWND hwndClientDDE;
    enum PENDINGACK ePendingAck;
{
    struct CONV * pConv;

    if (pConv = FindConv(hwndClientDDE))
    {
  pConv->ePendingAck = ePendingAck;
    }
    return;
}


CLIDDE.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\DDE\CLIDDE.C

/****************************************************************************

    MODULE: CLIDDE.C

    PURPOSE: Processes incoming and outgoing DDE messages


****************************************************************************/

#include "windows.h"

#include "dde.h"
#include "clires.h"
#include "client.h"
#include <string.h>

#define DEFAULT_ACK_TIME_OUT_MILLISEC 10000
static int nAckTimeOut;

static BOOL   bInInitiate = FALSE;

BOOL NEAR AwaitingAck(HWND);


/****************************************************************************

    FUNCTION: AwaitingAck

    PURPOSE:  Inform user if acknowledgement is required before further
              action.

****************************************************************************/
BOOL NEAR AwaitingAck(hwndClientDDE)
    HWND hwndClientDDE;
{
    if (GetConvPendingAck(hwndClientDDE) == NONE)
    {
        return (FALSE);
    }
    MessageBox(hwndMain,
        "Previous DDE operation must be acknowledged first",
        "Client",
        MB_ICONEXCLAMATION | MB_OK);
    return (TRUE);
}



/****************************************************************************

    FUNCTION: ClientAcknowledge

    PURPOSE:  Called when client application receives WM_DDE_ACK message
        or WM_TIMER message (time out on wait for ACK).

****************************************************************************/
void ClientAcknowledge(hwndClientDDE, hwndServerDDE, lParam, bTimeOut)
    HWND hwndClientDDE;
    HWND hwndServerDDE;
    LONG lParam;    /* lParam of WM_DDE_ACK message */
    BOOL bTimeOut;  /* TRUE if NACK is due to time-out */
{
    enum PENDINGACK ePendingAck;
    char szApplication[APP_MAX_SIZE+1];
    char szTopic[TOPIC_MAX_SIZE+1];
    char szItem[ITEM_MAX_SIZE+1];
    char message[80];

    ePendingAck = GetConvPendingAck(hwndClientDDE);
    SetConvPendingAck(hwndClientDDE, NONE);
    KillTimer(hwndClientDDE, hwndServerDDE);

    if (bInInitiate)
    {
        GlobalGetAtomName(LOWORD(lParam),
            szApplication,
            APP_MAX_SIZE);
        GlobalGetAtomName(HIWORD(lParam),
            szTopic,
            TOPIC_MAX_SIZE);
  if (!AddConv(hwndClientDDE, hwndServerDDE, szApplication, szTopic))
        {
      MessageBox(hwndMain,
    "Maximum conversation count exceeded",
                "Client",
                MB_ICONEXCLAMATION | MB_OK);
        }
  /*
  GlobalDeleteAtom(LOWORD(lParam));
        GlobalDeleteAtom(HIWORD(lParam));
  */
        return;
    }
    if ((ePendingAck == ADVISE) && (LOWORD(lParam) & 0x8000))
    {  /* received positive ACK in response to ADVISE */
        GlobalGetAtomName(HIWORD(lParam), szItem, ITEM_MAX_SIZE);
  AddItemToConv(hwndClientDDE, szItem);

  /* Conversation item is established: now get current value */
  /* and update screen            */
  SendRequest(hwndClientDDE, hwndServerDDE, szItem);
  InvalidateRect(hwndMain, NULL, TRUE);
    }
    if ((ePendingAck == UNADVISE) && (LOWORD(lParam) & 0x8000))
    {  /* received positive ACK in response to UNADVISE */
        GlobalGetAtomName(HIWORD(lParam), szItem, ITEM_MAX_SIZE);
  RemoveItemFromConv(hwndClientDDE, szItem);
  InvalidateRect(hwndMain, NULL, TRUE);
    }
    if (!(LOWORD(lParam) & 0x8000))    /* NACK */
    {
        strcpy(message, "DDE ");
        strcat(message, ePendingAck == ADVISE?    "ADVISE "
                      : ePendingAck == UNADVISE?  "UNADVISE "
                      : ePendingAck == POKE?      "POKE "
                      : ePendingAck == REQUEST?   "REQUEST "
          : ePendingAck == EXECUTE?   "EXECUTE "
                      : " ");
        strcat(message, bTimeOut? "acknowledge time out"
                                : "operation failed");
  MessageBox(hwndMain,
            message,
            "Client",
            MB_ICONEXCLAMATION | MB_OK);
    }
    switch (ePendingAck)
    {
  case ADVISE:
  case UNADVISE:
  case POKE:
  case REQUEST:
      if (HIWORD(lParam))  /* will not be available for time-out */
    GlobalDeleteAtom(HIWORD(lParam));
      break;
  case EXECUTE:
      GlobalFree(HIWORD(lParam)); /* hCommand (execute string) */
      break;
    }
    return;
}



/****************************************************************************

    FUNCTION: ClientReceiveData

    PURPOSE:  Called when client application receives WM_DDE_DATA message.

****************************************************************************/
void ClientReceiveData(hwndClientDDE, hwndServerDDE, lParam)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
    LONG  lParam;
{
    DDEDATA FAR * lpDDEData;
    char          szItem[ITEM_MAX_SIZE+1];
    BOOL          bRelease;
    BOOL          bAck;

    if (IsConvInTerminateState(hwndClientDDE, hwndServerDDE))
    { /* Terminate in progress: do not receive data */
        GlobalFree(LOWORD(lParam));
        GlobalDeleteAtom(HIWORD(lParam));
        return;
    }

    if (GetConvPendingAck(hwndClientDDE) == REQUEST)
    {
  SetConvPendingAck(hwndClientDDE, NONE);
  KillTimer(hwndClientDDE, hwndServerDDE);
    }

    if (!(lpDDEData = (DDEDATA FAR *)GlobalLock(LOWORD(lParam)))
        || (lpDDEData->cfFormat != CF_TEXT))
    {
  PostMessage(hwndServerDDE,
            WM_DDE_ACK,
      hwndClientDDE,
            MAKELONG(0, HIWORD(lParam)));  /* Negative ACK */
    }
    bAck = FALSE;
    if (IsInRequestDlg())
    {
  /* Update REQUEST dialog box value */
  RequestSatisfied(lpDDEData->Value);
        bAck = TRUE;
    }
    else
    {
        GlobalGetAtomName(HIWORD(lParam), szItem, ITEM_MAX_SIZE);
  bAck = SetConvItemValue(hwndClientDDE, szItem, lpDDEData->Value);
    }
    if (lpDDEData->fAckReq)
    {
  /* return ACK or NACK */
  PostMessage(hwndServerDDE,
            WM_DDE_ACK,
      hwndClientDDE,
      MAKELONG( (bAck? 0x8000:0), HIWORD(lParam)));
    }
    bRelease = lpDDEData->fRelease;
    GlobalUnlock(LOWORD(lParam));
    if (bRelease)
        GlobalFree(LOWORD(lParam));
    return;
}


/****************************************************************************

    FUNCTION: ClientTerminate

    PURPOSE:  Called when client application receives WM_DDE_TERMINATE
        message.

****************************************************************************/
void ClientTerminate(hwndClientDDE, hwndServerDDE)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
{
    if (!IsConvInTerminateState(hwndClientDDE, hwndServerDDE))
    { /* Server has requested terminate: respond with terminate */
  PostMessage(hwndServerDDE, WM_DDE_TERMINATE, hwndClientDDE, 0L);
    }
    RemoveConv(hwndClientDDE, hwndServerDDE);
    if (!IsHwndClientDDEUsed(hwndClientDDE))
  DestroyWindow(hwndClientDDE);
    InvalidateRect(hwndMain, NULL, TRUE);
    return;
}



/****************************************************************************

    FUNCTION: DDEWndProc

    PURPOSE:  Handles all DDE messages received by the client application.

****************************************************************************/
long FAR PASCAL DDEWndProc(hwnd, message, wParam, lParam)
    HWND      hwnd;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    switch (message)
    {
  case WM_DDE_ACK:
      ClientAcknowledge(hwnd,(HWND)wParam, lParam, FALSE);
      return (0L);

  case WM_TIMER:
      /* Negative ACK because of time out */
      ClientAcknowledge(hwnd,(HWND)wParam, 0L, TRUE);
      return (0L);

   case WM_DDE_DATA:
       ClientReceiveData(hwnd,(HWND)wParam, lParam);
       return (0L);

   case WM_DDE_TERMINATE:
        ClientTerminate(hwnd,(HWND)wParam);
        return (0L);

   default:
        return (DefWindowProc(hwnd, message, wParam, lParam));
    }
}



/****************************************************************************

    FUNCTION: DoPasteLink

    PURPOSE:  Get conversation information (app/topic/item) from clipboard.
        Initiate conversation, if not already initiated.
        Then, request server to advise the specified item.

        Note, the standard clipboard format registered with the
        name "Link" is as follows:

        <app> <null> <topic> <null> <item> <null> <null>

****************************************************************************/
void DoPasteLink(void)
{

    HANDLE hData;
    LPSTR  lpData;
    HWND   hwndClientDDE;
    HWND   hwndServerDDE;
    char   szApplication[APP_MAX_SIZE+1];
    char   szTopic[TOPIC_MAX_SIZE+1];
    char   szItem[ITEM_MAX_SIZE+1];
    int    nBufLen;

    if (OpenClipboard(hwndMain))
    {
       if (!(hData = GetClipboardData(cfLink)) ||
    !(lpData = GlobalLock(hData)))
       {
          CloseClipboard();
    return;
       }


       /* Parse clipboard data */
       if ((nBufLen = lstrlen(lpData)) >= APP_MAX_SIZE)
       {
      CloseClipboard();
      GlobalUnlock(hData);
      return;
       }
       lstrcpy(szApplication, lpData);
       lpData += (nBufLen+1); /* skip over null */
       if ((nBufLen = lstrlen(lpData)) >= TOPIC_MAX_SIZE)
       {
      CloseClipboard();
      GlobalUnlock(hData);
      return;
       }
       lstrcpy(szTopic, lpData);
       lpData += (nBufLen+1); /* skip over null */
       if ((nBufLen = lstrlen(lpData)) >= ITEM_MAX_SIZE)
       {
      CloseClipboard();
      GlobalUnlock(hData);
      return;
       }
       lstrcpy(szItem, lpData);

       GlobalUnlock(hData);
       CloseClipboard();

       if (hwndClientDDE = FindConvGivenAppTopic(szApplication, szTopic))
       {   /* app/topic conversation already started */
     if (DoesAdviseAlreadyExist(hwndClientDDE, szItem))
         MessageBox(hwndMain,"Advisory already established",
                   "Client", MB_ICONEXCLAMATION | MB_OK);
           else
         hwndServerDDE = GetHwndServerDDE(hwndClientDDE);
         SendAdvise(hwndClientDDE, hwndServerDDE, szItem);
       }
       else
       {   /* must initiate new conversation first */
     SendInitiate(szApplication, szTopic);
     if (hwndClientDDE = FindConvGivenAppTopic(szApplication, szTopic))
     {
    hwndServerDDE = GetHwndServerDDE(hwndClientDDE);
    SendAdvise(hwndClientDDE, hwndServerDDE, szItem);
     }
       }
    }

    return;
}



/****************************************************************************

    FUNCTION: InitAckTimeOut

    PURPOSE:  Get DDE timeout value from win.ini.  Value is in milliseconds.

****************************************************************************/
void InitAckTimeOut(void)
{

   /* Finds value in win.ini section corresponding to application name */

   nAckTimeOut = GetPrivateProfileInt("Client",
             "DdeTimeOut",
             DEFAULT_ACK_TIME_OUT_MILLISEC,
                               "client.ini");
   return;
}

/****************************************************************************

    FUNCTION: SendAdvise

    PURPOSE:  Send advise message to server.


****************************************************************************/
void SendAdvise(hwndClientDDE, hwndServerDDE, szItem)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
    char * szItem;
{
    ATOM            atomItem;
    HANDLE          hOptions;
    DDEADVISE FAR * lpOptions;

    /* don't send another message requiring an ACK until first message */
    /* is acknowledged                   */
    if (AwaitingAck(hwndClientDDE))
        return;

    if (!(hOptions
          = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (LONG)sizeof(DDEADVISE
        return;
    if (!(lpOptions
          = (DDEADVISE FAR *)GlobalLock(hOptions)))
    {
  GlobalFree(hOptions);
        return;
    }
    lpOptions->cfFormat = CF_TEXT;
    lpOptions->fAckReq = TRUE;
    lpOptions->fDeferUpd = FALSE;
    GlobalUnlock(hOptions);
    atomItem = GlobalAddAtom((LPSTR)szItem);
    SetConvPendingAck(hwndClientDDE, ADVISE);
    SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
    if (!PostMessage(hwndServerDDE,
            WM_DDE_ADVISE,
      hwndClientDDE,
            MAKELONG(hOptions, atomItem)))
    {
        GlobalDeleteAtom(atomItem);
        GlobalFree(hOptions);
    }
    return;
}



/****************************************************************************

    FUNCTION: SendExecute

    PURPOSE:  Send execute string to server.


****************************************************************************/
void SendExecute(hwndClientDDE, hwndServerDDE, szExecuteString)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
    char * szExecuteString;
{
    HANDLE  hExecuteString;
    LPSTR  lpExecuteString;

    /* don't send another message requiring an ACK until first message */
    /* is acknowledged                   */
    if (AwaitingAck(hwndClientDDE))
        return;

    if (!(hExecuteString
          = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
      (DWORD)lstrlen(szExecuteString) + 1)))
        return;
    if (!(lpExecuteString
    = GlobalLock(hExecuteString)))
    {
  GlobalFree(hExecuteString);
        return;
    }
    lstrcpy(lpExecuteString, szExecuteString);
    GlobalUnlock(hExecuteString);
    SetConvPendingAck(hwndClientDDE, EXECUTE);
    SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
    if (!PostMessage(hwndServerDDE,
      WM_DDE_EXECUTE,
      hwndClientDDE,
      MAKELONG(0, hExecuteString)))
    {
  GlobalFree(hExecuteString);
    }
    return;
}



/****************************************************************************

    FUNCTION: SendInitiate

    PURPOSE:  Sends initiate message to all windows.  By the time this
        function returns, all servers matching the app/topic will
        have acknowledged, and this client applicaiton will have
        temporarily registered the new conversations.   If more
        than one server responded, then this client application
        asks the user which conversation to keep; all other
        conversations will then be terminated.   This function
        returns the handle of the hidden DDE window used to
        initiate the conversation with server(s).

****************************************************************************/
HWND SendInitiate(szApplication, szTopic)
    char * szApplication;
    char * szTopic;
{
    HWND  hwndClientDDE;
    ATOM  atomApplication;
    ATOM  atomTopic;

    if (!(hwndClientDDE = CreateWindow(
      "ClientDDEWndClass",
      "ClientDDE",
      WS_CHILD,  /* not visible */
      0, 0, 0, 0, /* no position or dimensions */
      hwndMain,  /* parent */
      NULL,  /* no menu */
      hInst,
      NULL)))
    {
  return (NULL);
    }

    atomApplication
        = *szApplication == 0 ? NULL : GlobalAddAtom((LPSTR)szApplication);
    atomTopic
        = *szTopic == 0 ? NULL : GlobalAddAtom((LPSTR)szTopic);

    /* flag bIniInitiate is queried when client processes the server's ACK */
    bInInitiate = TRUE;
    SendMessage(-1,
        WM_DDE_INITIATE,
  hwndClientDDE,
        MAKELONG(atomApplication, atomTopic));
    bInInitiate = FALSE;
    if (atomApplication != NULL)
        GlobalDeleteAtom(atomApplication);
    if (atomTopic != NULL)
        GlobalDeleteAtom(atomTopic);
    return (hwndClientDDE);
}

/****************************************************************************

    FUNCTION: SendPoke

    PURPOSE:  Send poke message to server.


****************************************************************************/
void SendPoke(hwndClientDDE, hwndServerDDE, szItem, szValue)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
    char * szItem;
    char * szValue;
{
    ATOM        atomItem;
    HANDLE      hPokeData;
    DDEPOKE FAR * lpPokeData;

    /* don't send another message requiring an ACK until first message */
    /* is acknowledged                   */
    if (AwaitingAck(hwndClientDDE))
        return;

    /* Allocate size of DDE data header, plus the data:  a string   */
    /* terminated by <CR> <LF> <NULL>.  The <NULL> is counted by    */
    /* by DDEPOKE.Value[1].                                         */

    if (!(hPokeData
          = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
                        (LONG)sizeof(DDEPOKE) + lstrlen(szValue) + 2)))
        return;
    if (!(lpPokeData
          = (DDEPOKE FAR*)GlobalLock(hPokeData)))
    {
  GlobalFree(hPokeData);
        return;
    }
    lpPokeData->fRelease = TRUE;
    lpPokeData->cfFormat = CF_TEXT;
    lstrcpy((LPSTR)lpPokeData->Value, (LPSTR)szValue);
    /* each line of CF_TEXT data is terminated by CR/LF */
    lstrcat((LPSTR)lpPokeData->Value, (LPSTR)"\r\n");
    GlobalUnlock(hPokeData);
    atomItem = GlobalAddAtom((LPSTR)szItem);
    SetConvPendingAck(hwndClientDDE, POKE);
    SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
    if (!PostMessage(hwndServerDDE,
            WM_DDE_POKE,
      hwndClientDDE,
            MAKELONG(hPokeData, atomItem)))
    {
        GlobalDeleteAtom(atomItem);
        GlobalFree(hPokeData);
    }
    return;
}



/****************************************************************************

    FUNCTION: SendRequest

    PURPOSE:  Send request message to server.


****************************************************************************/
void SendRequest(hwndClientDDE, hwndServerDDE, szItem)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
    char * szItem;
{
    ATOM  atomItem;

    /* don't send another message requiring an ACK until first message */
    /* is acknowledged                   */
    if (AwaitingAck(hwndClientDDE))
        return;

    atomItem = GlobalAddAtom((LPSTR)szItem);
    SetConvPendingAck(hwndClientDDE, REQUEST);
    SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
    if (!PostMessage(hwndServerDDE,
            WM_DDE_REQUEST,
      hwndClientDDE,
            MAKELONG(CF_TEXT,atomItem)))
    {
        GlobalDeleteAtom(atomItem);
    }
    return;
}


/****************************************************************************

    FUNCTION: SendTerminate

    PURPOSE:  Send terminate message to server.

****************************************************************************/
void SendTerminate(hwndClientDDE, hwndServerDDE)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
{
    MSG msg;
    LONG lTimeOut;

    SetConvInTerminateState(hwndClientDDE, hwndServerDDE);
    PostMessage(hwndServerDDE, WM_DDE_TERMINATE, hwndClientDDE, 0L);
    return;
}




/****************************************************************************

    FUNCTION: SendUnadvise

    PURPOSE:  Send unadvise message to server.


****************************************************************************/
void SendUnadvise(hwndClientDDE, hwndServerDDE, szItem)
    HWND  hwndClientDDE;
    HWND  hwndServerDDE;
    char * szItem;
{
    ATOM  atomItem;

    /* don't send another message requiring an ACK until first message */
    /* is acknowledged                   */
    if (AwaitingAck(hwndClientDDE))
        return;

    atomItem = GlobalAddAtom((LPSTR)szItem);
    SetConvPendingAck(hwndClientDDE, UNADVISE);
    SetTimer(hwndClientDDE, hwndServerDDE, nAckTimeOut, NULL);
    if (!PostMessage(hwndServerDDE,
            WM_DDE_UNADVISE,
      hwndClientDDE,
            MAKELONG(0,atomItem)))
    {
        GlobalDeleteAtom(atomItem);
    }
    return;
}


/****************************************************************************

    FUNCTION: TerminateConversations

    PURPOSE:  Processes WM_DESTROY message, terminates all conversations.

****************************************************************************/
void TerminateConversations(void)
{
   HWND  hwndClientDDE;
   HWND  hwndServerDDE;
   LONG  lTimeOut;
   MSG   msg;


   /* Terminate each active conversation */
   hwndClientDDE = NULL;
   while (hwndClientDDE = GetNextConv(hwndClientDDE))
   {
  hwndServerDDE = GetHwndServerDDE(hwndClientDDE);
  if (IsWindow(hwndServerDDE)) /* if server window still alive */
      SendTerminate(hwndClientDDE, hwndServerDDE);
   }

   /* Wait for all conversations to terminate or for time out */
   lTimeOut = GetTickCount() + (LONG)nAckTimeOut;
   while (PeekMessage(&msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
   {
         DispatchMessage (&msg);
   if (msg.message == WM_DDE_TERMINATE)
   {
       if (!AtLeastOneConvActive())
     break;
   }
         if (GetTickCount() > lTimeOut)
             break;
   }

   return;
}


CLIENT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\DDE\CLIENT.C

/****************************************************************************

    PROGRAM: CLIENT

    PURPOSE: Illustrates client side of DDE conversation

    MODULES:

        CLIENT.C   Window and dialog procedures.
  CLIDATA.C  Maintains conversation data.
        CLIDDE.C   Processes incoming and outgoing DDE messages.

****************************************************************************/

#include "windows.h"

#include "dde.h"
#include "clires.h"
#include "client.h"
#include <string.h>
#include <stdlib.h>

static int    nInstCount;
static HWND   hwndRequestDlg;

static BOOL   bInInitiate = FALSE;
static BOOL   bInRequestDlg = FALSE;
static BOOL   bTerminating = FALSE;

static char  szSelectedApplication[APP_MAX_SIZE+1];
static char  szSelectedTopic[TOPIC_MAX_SIZE+1];
static char  szSelectedItem[ITEM_MAX_SIZE+1];
static char  szSelectedValue[VALUE_MAX_SIZE+1];
static HWND  hwndSelectedClientDDE;
static int   cfSelectedFormat;

long FAR PASCAL MainWndProc(HWND, unsigned, WORD, LONG);
BOOL            InitApplication(HANDLE);
void            InitAddedInstance(HANDLE, HANDLE);
BOOL            InitInstance(HANDLE, int);
int  NEAR  DoDialog(char *, FARPROC);
BOOL FAR PASCAL AboutDlgProc(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL AdviseDlgProc(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL ClearDlgProc(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL ExecuteDlgProc(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL InitiateDlgProc(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL PokeDlgProc(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL RequestDlgProc(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL TerminateDlgProc(HWND, unsigned, WORD, LONG);
BOOL FAR PASCAL UnadviseDlgProc(HWND, unsigned, WORD, LONG);
void    AddConversationsToBox(HWND, unsigned);
void NEAR  GetSelectedConversation(HWND, unsigned, unsigned);


/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: Calls initialization functions and processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
    {
        if (!InitApplication(hInstance))
         return (FALSE);
    }
    else
    {
        InitAddedInstance(hInstance, hPrevInstance);
    }

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL))
    {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window classes

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    nInstCount = 1;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "ClientMenu";
    wc.lpszClassName = "ClientWClass";

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

    wc.style = NULL;
    wc.lpfnWndProc = DDEWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = NULL;
    wc.hbrBackground = NULL;
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = "ClientDDEWndClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION: InitAddedInstance

    PURPOSE:  Increment instance counter.

****************************************************************************/
void InitAddedInstance(hInstance, hPrevInstance)
    HANDLE  hInstance;
    HANDLE  hPrevInstance;
{
    GetInstanceData(hPrevInstance, (NPSTR)&nInstCount, sizeof(int));
    nInstCount++;
    return;
}



/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    HDC         hDC;
    TEXTMETRIC  tm;

    hInst = hInstance;

    hwndMain = CreateWindow(
        "ClientWClass",
        "Client",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwndMain)
        return (FALSE);

    InitDataTextMetrics();
    InitAckTimeOut();      /* in module CLIDDE */

    MoveWindow(hwndMain,
        xDelta*(5+nInstCount),
        ((nInstCount-1)&1)*nVertRes/2 + yDelta*nInstCount,
        xDelta*30,
        yDelta*12,
        FALSE);

    if (!(cfLink = RegisterClipboardFormat("Link")))
  return (FALSE);

    ShowWindow(hwndMain, nCmdShow);
    UpdateWindow(hwndMain);

    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for client application.
        DDE messages are handled by DDEWndProc in CLIDDE.C.

****************************************************************************/

long FAR PASCAL MainWndProc(hwnd, message, wParam, lParam)
HWND hwnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    char  szApplication[APP_MAX_SIZE+1];
    char  szTopic[TOPIC_MAX_SIZE+1];

    switch (message)
    {
        case WM_PAINT:
      PaintConvData(hwnd);
            return (0L);

        case WM_INITMENU:
            if (wParam == GetMenu(hwnd))
            {
    if (IsClipboardFormatAvailable(cfLink))
        EnableMenuItem(wParam, IDM_PASTELINK, MF_ENABLED);
    else
        EnableMenuItem(wParam, IDM_PASTELINK, MF_GRAYED);
            }
            return (0L);

        case WM_COMMAND:
            switch (wParam)
            {
    case IDM_INITIATE:
                    /* If we are in the process of terminating, no new
                       conversations are allowed */
                    if (!bTerminating)
                    {
      DoDialog("Initiate", InitiateDlgProc);
                    }
                    return 0L;

                case IDM_TERMINATE:
        DoDialog("Terminate", TerminateDlgProc);
                    return 0L;

                case IDM_ADVISE:
        DoDialog("Advise", AdviseDlgProc);
                    return 0L;

                case IDM_UNADVISE:
        DoDialog("Unadvise", UnadviseDlgProc);
                    return 0L;

                case IDM_REQUEST:
        DoDialog("Request", RequestDlgProc);
                    return 0L;

                case IDM_POKE:
        DoDialog("Poke", PokeDlgProc);
                    return 0L;

                case IDM_PASTELINK:
        DoPasteLink();
                    return 0L;

                case IDM_CLEAR:
        DoDialog("Clear", ClearDlgProc);
                    return 0L;

    case IDM_EXECUTE:
        DoDialog("Execute", ExecuteDlgProc);
        return 0L;

    case IDM_ABOUT:
        DoDialog("About", AboutDlgProc);
        break;

                default:
                    return (DefWindowProc(hwnd, message, wParam, lParam));
            }
            break;

        case WM_DESTROY:
            /* Terminate all DDE conversations before destroying
               client window */
      bTerminating = TRUE;
      TerminateConversations();
            PostQuitMessage(0);
            break;

  default:
      return (DefWindowProc(hwnd, message, wParam, lParam));
    }
    return (0L);
}


/****************************************************************************

    FUNCTION: DoDialog

    PURPOSE:  Creates dialog given dialog i.d.

****************************************************************************/
int NEAR DoDialog(szDialog, lpfnDlgProc)
    char   * szDialog;
    FARPROC  lpfnDlgProc;
{
    int      nReturn;

    lpfnDlgProc = MakeProcInstance(lpfnDlgProc, hInst);
    nReturn = DialogBox(hInst,
  szDialog,
  hwndMain,
        lpfnDlgProc);
    FreeProcInstance(lpfnDlgProc);
    return  (nReturn);
}



/****************************************************************************

    FUNCTION: AboutDlgProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

****************************************************************************/

BOOL FAR PASCAL AboutDlgProc(hDlg, message, wParam, lParam)
    HWND hDlg;
    unsigned message;
    WORD wParam;
    LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}



/****************************************************************************

    FUNCTION: AdviseDlgProc

    PURPOSE:  Processes messages for the Advise dialog.

****************************************************************************/
BOOL FAR PASCAL AdviseDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND  hwndServerDDE;

    switch (message)
    {
        case WM_INITDIALOG:
      AddConversationsToBox(hdlg, CB_ADDSTRING);
            return (TRUE);

        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_OK:
        GetSelectedConversation(hdlg, CB_GETCURSEL, CB_GETLBTEXT);
                    GetDlgItemText(hdlg,
                        IDC_ITEM,
                        (LPSTR)szSelectedItem,
                        ITEM_MAX_SIZE);
        if (DoesAdviseAlreadyExist(hwndSelectedClientDDE,
                                               szSelectedItem))
                        MessageBox(hdlg, "Advisory already established",
                            "Client", MB_ICONEXCLAMATION | MB_OK);
                    else
      hwndServerDDE
          = GetHwndServerDDE(hwndSelectedClientDDE);
      SendAdvise(hwndSelectedClientDDE,
          hwndServerDDE,
          szSelectedItem);
        return (TRUE);

                case IDC_CANCEL:
                    EndDialog(hdlg, FALSE);
        return (FALSE);
            }

        default:
            return (FALSE);
    }
    return (FALSE);
}



/****************************************************************************

    FUNCTION: ClearDlgProc

    PURPOSE:  Processes messages for the Clear dialog.

****************************************************************************/
BOOL FAR PASCAL ClearDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND  hwndServerDDE;
    HWND  hctlItemBox;
    int   nIndex;

    switch (message)
    {
        case WM_INITDIALOG:
      AddConversationsToBox(hdlg, CB_ADDSTRING);
            return (TRUE);

        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_OK:
        GetSelectedConversation(hdlg, CB_GETCURSEL, CB_GETLBTEXT);
                    GetDlgItemText(hdlg,
                        IDC_ITEM,
                        (LPSTR)szSelectedItem,
                        ITEM_MAX_SIZE);
        hwndServerDDE = GetHwndServerDDE(hwndSelectedClientDDE);
        SendUnadvise(hwndSelectedClientDDE,
      hwndServerDDE,
      szSelectedItem);
                    EndDialog(hdlg, TRUE);
        return (TRUE);

                case IDC_CANCEL:
                    EndDialog(hdlg, FALSE);
                    return (TRUE);
            }
        default:
            return (FALSE);
    }
    return (FALSE);
}



/****************************************************************************

    FUNCTION: ExecuteDlgProc

    PURPOSE:  Processes messages for the Execute dialog.

****************************************************************************/
BOOL FAR PASCAL ExecuteDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND  hwndServerDDE;
    char  szExecuteString[EXECUTE_STRING_MAX_SIZE+1];

    switch (message)
    {
        case WM_INITDIALOG:
      AddConversationsToBox(hdlg, CB_ADDSTRING);
            return (TRUE);

        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_OK:
        GetSelectedConversation(hdlg, CB_GETCURSEL, CB_GETLBTEXT);
                    GetDlgItemText(hdlg,
      IDC_EXECUTE_STRING,
      (LPSTR)szExecuteString,
      EXECUTE_STRING_MAX_SIZE);
        hwndServerDDE = GetHwndServerDDE(hwndSelectedClientDDE);
        SendExecute(hwndSelectedClientDDE,
      hwndServerDDE,
      szExecuteString);
        return (TRUE);

                case IDC_CANCEL:
                    EndDialog(hdlg, FALSE);
        return (TRUE);
            }

        default:
            return (FALSE);
    }
    return (FALSE);
}




/****************************************************************************

    FUNCTION: InitiateDlgProc

    PURPOSE:  Processes messages for the Initiate dialog.

****************************************************************************/
BOOL FAR PASCAL InitiateDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND      hwndClientDDE;

    switch (message)
    {
        case WM_INITDIALOG:
            return (TRUE);

        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_OK:
                    GetDlgItemText(hdlg,
                        IDC_APPLICATION,
                        (LPSTR)szSelectedApplication,
                        APP_MAX_SIZE);
                    GetDlgItemText(hdlg,
                        IDC_TOPIC,
                        (LPSTR)szSelectedTopic,
                        TOPIC_MAX_SIZE);
                    EndDialog(hdlg, TRUE);
        if (hwndClientDDE
      = SendInitiate(szSelectedApplication, szSelectedTopic))
        {
      LetUserPickConversation(hwndClientDDE);
      InvalidateRect(hwndMain, NULL, TRUE);
        }
        return (TRUE);

                case IDC_CANCEL:
                    EndDialog(hdlg, FALSE);
                    return (TRUE);
            }
        default:
            return (FALSE);
    }
    return (FALSE);
}




/****************************************************************************

    FUNCTION: PokeDlgProc

    PURPOSE:  Processes messages for the Poke dialog.

****************************************************************************/
BOOL FAR PASCAL PokeDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND  hwndServerDDE;

    switch (message)
    {
        case WM_INITDIALOG:
      AddConversationsToBox(hdlg, CB_ADDSTRING);
            return (TRUE);

        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_OK:
        GetSelectedConversation(hdlg, CB_GETCURSEL, CB_GETLBTEXT);
                    GetDlgItemText(hdlg,
                        IDC_ITEM,
                        (LPSTR)szSelectedItem,
                        ITEM_MAX_SIZE);
                    GetDlgItemText(hdlg,
                        IDC_VALUE,
                        (LPSTR)szSelectedValue,
                        VALUE_MAX_SIZE);
        hwndServerDDE = GetHwndServerDDE(hwndSelectedClientDDE);
        SendPoke(hwndSelectedClientDDE,
      hwndServerDDE,
                        szSelectedItem,
                        szSelectedValue);
        return (TRUE);

                case IDC_CANCEL:
                    EndDialog(hdlg, FALSE);
        return (TRUE);
            }

        default:
            return (FALSE);
    }
    return (FALSE);
}


/****************************************************************************

    FUNCTION: RequestDlgProc

    PURPOSE:  Processes messages for the Request dialog.

****************************************************************************/
BOOL FAR PASCAL RequestDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND  hwndServerDDE;

    switch (message)
    {
        case WM_INITDIALOG:
            hwndRequestDlg = hdlg;
            bInRequestDlg = TRUE;
      AddConversationsToBox(hdlg, CB_ADDSTRING);
            return (TRUE);

        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_OK:
        GetSelectedConversation(hdlg, CB_GETCURSEL, CB_GETLBTEXT);
                    GetDlgItemText(hdlg,
                        IDC_ITEM,
                        (LPSTR)szSelectedItem,
                        ITEM_MAX_SIZE);
        hwndServerDDE = GetHwndServerDDE(hwndSelectedClientDDE);
        SendRequest(hwndSelectedClientDDE,
      hwndServerDDE,
      szSelectedItem);
        return (TRUE);

                case IDC_CANCEL:
                    bInRequestDlg = FALSE;
                    EndDialog(hdlg, FALSE);
                    return (TRUE);
            }
            break;

        case WM_DESTROY:
            bInRequestDlg = FALSE;
            return (FALSE);

        default:
            return (FALSE);
    }
    return (FALSE);
}



/****************************************************************************

    FUNCTION: TerminateDlgProc

    PURPOSE:  Processes messages for the Terminate dialog.

****************************************************************************/
BOOL FAR PASCAL TerminateDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND hwndServerDDE;

    switch (message)
    {
        case WM_INITDIALOG:
      AddConversationsToBox(hdlg, LB_ADDSTRING);
            return (TRUE);

        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_OK:
        GetSelectedConversation(hdlg, LB_GETCURSEL, LB_GETTEXT);
                    EndDialog(hdlg, TRUE);
        hwndServerDDE = GetHwndServerDDE(hwndSelectedClientDDE);
        SendTerminate(hwndSelectedClientDDE, hwndServerDDE);
        return (TRUE);

                case IDC_CANCEL:
                    EndDialog(hdlg, FALSE);
        return (TRUE);
            }
        default:
            return (FALSE);
    }
    return (FALSE);
}




/****************************************************************************

    FUNCTION: UnadviseDlgProc

    PURPOSE:  Processes messages for the Unadvise dialog.

****************************************************************************/
BOOL FAR PASCAL UnadviseDlgProc(hdlg, message, wParam, lParam)
    HWND      hdlg;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    HWND  hctlItemBox;
    int   nIndex;
    HWND  hwndServerDDE;

    switch (message)
    {
        case WM_INITDIALOG:
      AddConversationsToBox(hdlg, CB_ADDSTRING);
            return (TRUE);

        case WM_COMMAND:
            switch (wParam)
            {
                case IDC_OK:
        GetSelectedConversation(hdlg, CB_GETCURSEL, CB_GETLBTEXT);
                    GetDlgItemText(hdlg,
                        IDC_ITEM,
                        (LPSTR)szSelectedItem,
                        ITEM_MAX_SIZE);
        hwndServerDDE = GetHwndServerDDE(hwndSelectedClientDDE);
        SendUnadvise(hwndSelectedClientDDE,
      hwndServerDDE,
                        szSelectedItem);
        return (TRUE);

                case IDC_CANCEL:
                    EndDialog(hdlg, FALSE);
                    return (TRUE);
            }
        default:
            return (FALSE);
    }
    return (FALSE);
}



/****************************************************************

    FUNCTION: AddConversationsToBox

    PURPOSE:  Add server, app, topic info to client list box


****************************************************************/
void AddConversationsToBox(hdlg, nAddMessage)
    HWND      hdlg;
    unsigned  nAddMessage;  /* LB_ADDSTRING or CB_ADDSTRING */
{
    HWND  hwndClientDDE;
    HWND  hctlServerList;
    char  szConvInfo[CONVINFO_MAX_SIZE+1];
    char  szApp[APP_MAX_SIZE+1];
    char  szTopic[TOPIC_MAX_SIZE+1];

    hctlServerList = GetDlgItem(hdlg, IDC_CONVBOX);
    SendMessage(hctlServerList,
        nAddMessage == LB_ADDSTRING ? LB_RESETCONTENT : CB_RESETCONTENT,
        0,
        0L);
    hwndClientDDE = NULL;
    while (1)
    {
  if (!(hwndClientDDE = GetNextConv(hwndClientDDE)))
      break;
  itoa((int)hwndClientDDE, szConvInfo, 16);
  strupr(szConvInfo);
  strcat(szConvInfo,"->");
  itoa((int)GetHwndServerDDE(hwndClientDDE),
      szConvInfo+strlen(szConvInfo),
      16);
  strcat(szConvInfo," ");
  GetAppAndTopic(hwndClientDDE, szApp, szTopic);
  strcat(szConvInfo, szApp);
  strcat(szConvInfo, " | ");
  strcat(szConvInfo, szTopic);

        SendMessage(hctlServerList,
            nAddMessage,
            0,
      (LONG)(LPSTR)szConvInfo);
    }
    if (nAddMessage == CB_ADDSTRING)
    {
  SendMessage(hctlServerList, CB_SETCURSEL, 0, 0L);
    }
    return;
}


/*********************************************************************

    FUNCTION: IsInRequestDlg

    PURPOSE:  Returns whether the user is in the Request dialog or not.

*********************************************************************/
BOOL IsInRequestDlg()
{
    return (bInRequestDlg);
}



/*********************************************************************

    FUNCTION: GetSelectedConversation

    PURPOSE:  Gets the user's selection from the conversation list
        box, and returns result in hwndSelectedClientDDE.

*********************************************************************/
void NEAR GetSelectedConversation(hdlg, nCurSelMessage, nGetTextMessage)
    HWND      hdlg;
    unsigned  nCurSelMessage;
    unsigned  nGetTextMessage;
{
    HWND hctlConvBox;
    int  nIndex;
    char szConvInfo[CONVINFO_MAX_SIZE+1];
    char * pcBlank;


    hctlConvBox = GetDlgItem(hdlg, IDC_CONVBOX);
    if ((nIndex = SendMessage(hctlConvBox, nCurSelMessage, 0, 0L))
        != LB_ERR)
    {
  szConvInfo[0] = 0;
  SendMessage(hctlConvBox,
            nGetTextMessage,
            nIndex,
      (LONG)(LPSTR)szConvInfo);
  /* find '-' in 'hwnd->hwnd' list box entry */
  if (!(pcBlank = strchr(szConvInfo, '-')))
            return;
        *pcBlank = 0;  /* terminate hwnd numeric value */
  hwndSelectedClientDDE = HexToInt(szConvInfo);
     }
     return;
}



/****************************************************************

    FUNCTION: InitDataTextMetrics

    PURPOSE:  Get font information

****************************************************************/
void InitDataTextMetrics()
{
    HDC hDC;
    TEXTMETRIC tm;

    hDC = GetDC(hwndMain);
    GetTextMetrics(hDC, (LPTEXTMETRIC)&tm);
    nHorzRes = GetDeviceCaps(hDC, HORZRES);
    nVertRes = GetDeviceCaps(hDC, VERTRES);
    ReleaseDC(hwndMain, hDC);
    yDelta = tm.tmHeight + tm.tmExternalLeading;
    xDelta = tm.tmAveCharWidth;
    return;
}


/*********************************************************************

    FUNCTION: RequestSatisfied

    PURPOSE:  Updates Request dialog box with value requested from
        server.

*********************************************************************/
void RequestSatisfied(lpszValue)
    LPSTR lpszValue;
{
    HWND hctlValue;

    hctlValue = GetDlgItem(hwndRequestDlg, IDC_VALUE);
    SetWindowText(hctlValue, lpszValue);
    return;
}


/*********************************************************************

    FUNCTION: SetSelectedValue

    PURPOSE:  Set specified value.

*********************************************************************/
void SetSelectedValue(lpszSelectedValue)
    LPSTR lpszSelectedValue;
{
    lstrcpy((LPSTR)szSelectedValue, lpszSelectedValue);
    return;
}


CLIPTEXT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\CLIPTEXT\CLIPTEXT.C

/****************************************************************************

    PROGRAM: Cliptext.c

    PURPOSE: Demonstrates copying text to and from the clipboard

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box
        OutOfMemory() - displays warning message

****************************************************************************/

#include "windows.h"
#include "cliptext.h"

HANDLE hInst;
HANDLE hAccTable;
HWND   hwnd;

HANDLE hText = NULL;

char szInitialClientAreaText[] =
    "This program demonstrates the use of the Edit menu to copy and "
    "paste text to and from the clipboard.  Try using the Copy command "
    "to move this text to the clipboard, and the Paste command to replace "
    "this text with data from another application.  \r\n\r\n"
    "You might want to try running Notepad and Clipbrd alongside this "
    "application so that you can watch the data exchanges take place.  ";

HANDLE hData, hClipData;                            /* handles to clip data
LPSTR lpData, lpClipData;                           /* pointers to clip data

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;
    LPSTR lpszText;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {

    /* Only translate message if it is not an accelerator message */

        if (!TranslateAccelerator(hwnd, hAccTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "CliptextMenu";
    wc.lpszClassName = "CliptextWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    LPSTR           lpszText;

    hInst = hInstance;

    hAccTable = LoadAccelerators(hInst, "ClipTextAcc");

    if (!(hText
          = GlobalAlloc(GMEM_MOVEABLE,(DWORD)sizeof(szInitialClientAreaText))
        OutOfMemory();
        return (FALSE);
    }

    if (!(lpszText = GlobalLock(hText))) {
        OutOfMemory();
        return (FALSE);
    }

    lstrcpy(lpszText, szInitialClientAreaText);
    GlobalUnlock(hText);

    hwnd = CreateWindow(
        "CliptextWClass",
        "Cliptext Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

        WM_COMMAND    - message from menu
        WM_INITMENU   - initialize menu
  WM_PAINT      - update window
        WM_DESTROY    - destroy window

    COMMENTS:

        WM_INITMENU - when this message is received, the application checks
        to see if there is any text data in the clipboard, and enables or
        disables the Paste menu item accordingly.

        Seclecting the Copy menu item will send the text "Hello Windows" to
        the clipboard.

        Seclecting the Paste menu item will copy whatever text is in the
        clipboard to the application window.

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;
    HDC hDC;
    HMENU hMenu;
    PAINTSTRUCT ps;
    RECT rectClient;
    LPSTR lpszText;

    switch (message) {

        case WM_INITMENU:
            if (wParam == GetMenu(hWnd)) {
                if (OpenClipboard(hWnd)) {
                    if (IsClipboardFormatAvailable(CF_TEXT)
                        || IsClipboardFormatAvailable(CF_OEMTEXT))
                        EnableMenuItem(wParam, IDM_PASTE, MF_ENABLED);
                    else
                        EnableMenuItem(wParam, IDM_PASTE, MF_GRAYED);
                    CloseClipboard();
                    return (TRUE);
                }
                else                           /* Clipboard is not available
                    return (FALSE);

            }
            return (TRUE);

        case WM_COMMAND:
            switch(wParam) {
                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance(About, hInst);
                    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
                    FreeProcInstance(lpProcAbout);
                    break;

                /* file menu commands */

                case IDM_NEW:
                case IDM_OPEN:
                case IDM_SAVE:
                case IDM_SAVEAS:
                case IDM_PRINT:
                    MessageBox (
                          GetFocus ()
                        , "Command not implemented."
                        , "ClipText Sample Application"
                        , MB_ICONASTERISK | MB_OK
                    );
                    break;

                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;

                /* edit menu commands */

                case IDM_UNDO:
                case IDM_CLEAR:
                    MessageBox (
                          GetFocus ()
                        , "Command not implemented."
                        , "ClipText Sample Application"
                        , MB_ICONASTERISK | MB_OK
                    );
                    break;

                case IDM_CUT:
                case IDM_COPY:

                    if (hText != NULL) {

                        /* Allocate memory and copy the string to it */

                        if (!(hData
                             = GlobalAlloc(GMEM_MOVEABLE, GlobalSize (hText))
                            OutOfMemory();
                            return (TRUE);
                        }
                        if (!(lpData = GlobalLock(hData))) {
                            OutOfMemory();
                            return (TRUE);
                        }
                        if (!(lpszText = GlobalLock (hText))) {
                            OutOfMemory();
                            return (TRUE);
                        }
                        lstrcpy(lpData, lpszText);
                        GlobalUnlock(hData);
                        GlobalUnlock (hText);

                        /* Clear the current contents of the clipboard, and s
                         * the data handle to the new string.
                         */

                        if (OpenClipboard(hWnd)) {
                            EmptyClipboard();
                            SetClipboardData(CF_TEXT, hData);
                            CloseClipboard();
                        }
                        hData = NULL;

                        if (wParam == IDM_CUT) {
                            GlobalFree (hText);
                            hText = NULL;
                            EnableMenuItem(GetMenu (hWnd), IDM_CUT, MF_GRAYED
                            EnableMenuItem(GetMenu(hWnd), IDM_COPY, MF_GRAYED
                            InvalidateRect (hWnd, NULL, TRUE);
                            UpdateWindow (hWnd);
                        }
                    }

                    return (TRUE);

                case IDM_PASTE:
                    if (OpenClipboard(hWnd)) {

                        /* get text from the clipboard */

                        if (!(hClipData = GetClipboardData(CF_TEXT))) {
                            CloseClipboard();
                            break;
                        }
                        if (hText != NULL) {
                            GlobalFree(hText);
                        }
                        if (!(hText = GlobalAlloc(GMEM_MOVEABLE
                                                    , GlobalSize(hClipData)))
                            OutOfMemory();
                            CloseClipboard();
                            break;
                        }
                        if (!(lpClipData = GlobalLock(hClipData))) {
                            OutOfMemory();
                            CloseClipboard();
                            break;
                        }
                        if (!(lpszText = GlobalLock(hText))) {
                            OutOfMemory();
                            CloseClipboard();
                            break;
                        }
                        lstrcpy(lpszText, lpClipData);
                        GlobalUnlock(hClipData);
                        CloseClipboard();
                        GlobalUnlock(hText);
                        EnableMenuItem(GetMenu(hWnd), IDM_CUT, MF_ENABLED);
                        EnableMenuItem(GetMenu(hWnd), IDM_COPY, MF_ENABLED);

                        /* copy text to the application window */

                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                        return (TRUE);
                    }
                    else
                        return (FALSE);
            }
            break;

  case WM_SIZE:
      InvalidateRect(hWnd, NULL, TRUE);
      break;

  case WM_PAINT:
            hDC = BeginPaint (hWnd, &ps);
            if (hText != NULL) {
                if (!(lpszText = GlobalLock (hText))) {
                    OutOfMemory();
                } else {
        GetClientRect (hWnd, &rectClient);
                    DrawText (hDC, lpszText, -1, &rectClient
                                , DT_EXTERNALLEADING | DT_NOPREFIX | DT_WORDB
                    GlobalUnlock (hText);
                }
            }
      EndPaint (hWnd, &ps);
            break;

  case WM_DESTROY:
      PostQuitMessage(0);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


/****************************************************************************

    FUNCTION: OutOfMemory(void)

    PURPOSE:  Displays warning message

****************************************************************************/
void OutOfMemory(void)
{
    MessageBox(
        GetFocus(),
        "Out of Memory",
        NULL,
        MB_ICONHAND | MB_SYSTEMMODAL);
    return;
}


CLOCK.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\CLOCK\CLOCK.C

/***************************************************************************
 *                                                                         *
 *  PROGRAM   : Clock.c                                                    *
 *                                                                         *
 *  PURPOSE   : To give a demonstration on the use of a timer in a windows *
 *              application.                                               *
 *                                                                         *
 *  MACROS    : HourHandPos  - Computes the hour hand position based on    *
 *                             both the hour and minute values.            *
 *                                                                         *
 *              VertEquiv    - Computes the raster line equivalent to the  *
 *                             given pixel value.                          *
 *                                                                         *
 *              HorzEquiv    - Computes the pixel equivalent to the given  *
 *                             raster line value.                          *
 *                                                                         *
 *  FUNCTIONS : About        - Dialog function for the About Dialog.       *
 *                                                                         *
 *              ClockWndProc - Window function for the application.        *
 *                                                                         *
 *              CreateTools  - Creates brushes and pens to coincide with   *
 *                             the current system colors.                  *
 *                                                                         *
 *              DeleteTools  - Destroys the brushes and pens created by    *
 *                             CreateTools.                                *
 *                                                                         *
 *              ClockCreate  - Performs the necessary initialization for   *
 *                             drawing the clock correctly and gets the    *
 *                             initial time to be displayed by the clock.  *
 *                                                                         *
 *              ClockSize    - Resize the clock to the largest possible    *
 *                             circle that will fit in the client area.    *
 *                                                                         *
 *              ClockTimer   - Update the clock to reflect the most recent *
 *                             time.                                       *
 *                                                                         *
 *              ClockPaint   - Paint the clock to display the most recent  *
 *                             time.                                       *
 *                                                                         *
 *              DrawFace     - Draws the clock face.                       *
 *                                                                         *
 *              DrawHand     - Draws a thin hand with the specified brush  *
 *                             in the specified hand position.             *
 *                                                                         *
 *              DrawFatHand  - Draws a fat hand with the specified brush   *
 *                             in the specified hand position.             *
 *                                                                         *
 *              CircleClock  - Resizes clock rectangle to keep clock       *
 *                             circular.                                   *
 *                                                                         *
 *              WinMain      - Calls the initialization function, creates  *
 *                             the main application window, and enters the *
 *                             message loop.                               *
 *                                                                         *
 *              ClockInit    - Registers the application window class and  *
 *                             initializes the circle values for the clock *
 *                             face.                                       *
 *                                                                         *
 ***************************************************************************/

#include "windows.h"
#include "clock.h"

/* Structure for holding time (in hours, minutes, and seconds) */
typedef struct
{
  int hour;
  int minute;
  int second;
} TIME;

extern void GetTime(TIME *); /* asm function to get current time */

TIME oTime;             /* the time currently displayed on the clock

HBRUSH hbrForegnd;      /* foreground brush -- system window text color
HBRUSH hbrBackgnd;      /* background brush -- system window backbround color
HPEN   hpenForegnd;     /* foreground pen   -- system window text color
HPEN   hpenBackgnd;     /* background pen   -- system window background color

HANDLE hInst;           /* instance of the CLOCK program being executed
BOOL   bFirst = TRUE;   /* TRUE if this is the 1st instance; FALSE otherwise

HANDLE    hCirTab;      /* Circle table for the circular clock face positions
POINT FAR *lpCirTab;    /* Pointer to the circle table

int   TimerID = 1;      /* number used for timer-id
char  szBuffer[BUFLEN]; /* buffer for stringtable data
RECT  ClockRect;        /* rectangle that EXACTLY bounds the clock face
long  ClockRadius;      /* clock face radius
POINT ClockCenter;      /* clock face center
BOOL  bIconic = FALSE;  /* TRUE if clock is currently iconic; FALSE otherwise

int   HorzRes;          /* width of the display (in pixels)
int   VertRes;          /* height of the display (in raster lines)
long  AspectH;          /* number of pixels per decimeter on the display
long  AspectV;          /* number of raster lines per decimeter on the displa


/***************************************************************************
 *                                                                         *
 *  MACRO    : HourHandPos (TIME)                                          *
 *                                                                         *
 *  PURPOSE  : Computes the hour hand position based on both the hour and  *
 *             minute values in the given time record.                     *
 *                                                                         *
 ***************************************************************************/

#define HourHandPos(time)  (time.hour * 5) + (time.minute /12)


/***************************************************************************
 *                                                                         *
 *  MACRO    : VertEquiv (int)                                             *
 *                                                                         *
 *  PURPOSE  : Computes the raster line (vertical) equivalent to the given *
 *             pixel (horizontal) value.                                   *
 *                                                                         *
 ***************************************************************************/

#define VertEquiv(lengthH) ((lengthH * AspectV) / AspectH)


/***************************************************************************
 *                                                                         *
 *  MACRO    : HorzEquiv (int)                                             *
 *                                                                         *
 *  PURPOSE  : Computes the pixel (horizontal) equivalent to the given     *
 *             raster line (vertical) value.                               *
 *                                                                         *
 ***************************************************************************/

#define HorzEquiv(lengthV) ((lengthV * AspectH) / AspectV)


/***************************************************************************
 *                                                                         *
 *  FUNCTION : About (HWND, unsigned, WORD, LONG)                          *
 *                                                                         *
 *  PURPOSE  : Dialog function for the "About..." menu item dialog.        *
 *                                                                         *
 ***************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
  HWND     hDlg;
  unsigned message;
  WORD     wParam;
  LONG     lParam;
{
  switch (message)
  {
    case WM_COMMAND:
      EndDialog(hDlg, TRUE);
      /* fall through */

    case WM_INITDIALOG:
      return(TRUE);

    default:
      return(FALSE);
  }
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : ClockWndProc (HWND, unsigned, WORD, LONG)                   *
 *                                                                         *
 *  PURPOSE  : Window function for the application.                        *
 *                                                                         *
 ***************************************************************************/

long FAR PASCAL ClockWndProc(hWnd, message, wParam, lParam)
  HWND     hWnd;
  unsigned message;
  WORD     wParam;
  LONG     lParam;
{
  switch (message)
  {
    case WM_SYSCOMMAND:
      if (wParam == IDM_ABOUT)
      {
        /* Draw and handle messages for the "About..." Dialog */
        DialogBox(hInst,
            MAKEINTRESOURCE(1),
            hWnd,
            MakeProcInstance((FARPROC) About, hInst));
      }
      else
      {
        /* Perform the default window processing */
        return(DefWindowProc(hWnd, message, wParam, lParam));
      }
      break;

    case WM_SIZE:
      /* Resize clock based on window size and redraw */
      ClockSize(hWnd, LOWORD(lParam), HIWORD(lParam), wParam);
      UpdateWindow(hWnd);
      break;

    case WM_DESTROY:
      /* Destroy clock's timer and tools before exiting */
      KillTimer(hWnd, TimerID);
      DeleteTools();
      PostQuitMessage(0);
      break;

    case WM_PAINT:
      {
        PAINTSTRUCT ps;

        /* Paint clock displaying current time */
        InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
        BeginPaint(hWnd, (LPPAINTSTRUCT) &ps);
        ClockPaint(hWnd, ps.hdc, PAINTALL);
        EndPaint(hWnd, (LPPAINTSTRUCT) &ps);
      }
      break;

    case WM_TIMECHANGE:
    case WM_TIMER:
      /* Update clock to display new time */
      ClockTimer(hWnd);
      break;

    case WM_SYSCOLORCHANGE:
      /* Change tools to coincide with system window colors */
      DeleteTools();
      CreateTools();
      break;

    case WM_ERASEBKGND:
      {
        RECT rc;

        /* Paint over the entire client area */
        GetClientRect(hWnd, (LPRECT) &rc);
        FillRect((HDC) wParam, (LPRECT) &rc, hbrBackgnd);
      }
      break;

    default:
      /* Perform the default window processing */
      return(DefWindowProc(hWnd, message, wParam, lParam));
  }
  return(0L);
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : CreateTools ()                                              *
 *                                                                         *
 *  PURPOSE  : Creates brushes and pens to coincide with the current       *
 *             system colors.                                              *
 *                                                                         *
 ***************************************************************************/

int CreateTools()
{
    hbrForegnd  = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT));
    hbrBackgnd  = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
    hpenForegnd = CreatePen(0, 1, GetSysColor(COLOR_WINDOWTEXT));
    hpenBackgnd = CreatePen(0, 1, GetSysColor(COLOR_WINDOW));
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : DeleteTools ()                                              *
 *                                                                         *
 *  PURPOSE  : Destroys the brushes and pens created by CreateTools.       *
 *                                                                         *
 ***************************************************************************/

int DeleteTools()
{
    DeleteObject(hbrForegnd);
    DeleteObject(hbrBackgnd);
    DeleteObject(hpenForegnd);
    DeleteObject(hpenBackgnd);
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : ClockCreate ()                                              *
 *                                                                         *
 *  PURPOSE  : First, for drawing the clock, ClockCreate computes the      *
 *             aspect ratio and creates the necessary pens and brushes.    *
 *             Then, if this is the first instance of the app running,     *
 *             ClockCreate scales the circle table values according to the *
 *             aspect ratio. Finally, ClockCreate gets the initial time.   *
 *                                                                         *
 ***************************************************************************/

int ClockCreate()
{
  int  pos;      /* hand position index into the circle table */
  int  vertSize; /* height of the display in millimeters      */
  int  horzSize; /* width of the display in millimeters       */
  HDC  hDC;
  RECT rc;

  /* Get display size in (pixels X raster lines) */
  /* and in (millimeters X millimeters)          */
  hDC = GetDC(NULL);
  VertRes = GetDeviceCaps(hDC, VERTRES);
  HorzRes = GetDeviceCaps(hDC, HORZRES);
  vertSize= GetDeviceCaps(hDC, VERTSIZE);
  horzSize= GetDeviceCaps(hDC, HORZSIZE);
  ReleaseDC(NULL, hDC);

  /* Compute (raster lines / decimeter) and (pixels / decimeter) */
  AspectV = ((long) VertRes * MMPERDM) / (long) vertSize;
  AspectH = ((long) HorzRes * MMPERDM) / (long) horzSize;

  CreateTools();

  /* Scale cosines for aspect ratio if this is the first instance */
  if (bFirst)
  {
    lpCirTab = (POINT far *) GlobalLock(hCirTab);
    for (pos = 0; pos < HANDPOSITIONS; pos++)
    {
      lpCirTab[pos].y = VertEquiv(lpCirTab[pos].y);
    }
    GlobalUnlock(hCirTab);
        }

  GetTime(&oTime);
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : ClockSize (HWND, int, int, WORD)                            *
 *                                                                         *
 *  PURPOSE  : Resize the clock to the largest possible circle that will   *
 *             fit in the client area. If switching from not iconic to     *
 *             iconic, alter the timer to update every minute.  And if     *
 *             switching back to non iconic, restore the timer to update   *
 *             every second.                                               *
 *                                                                         *
 ***************************************************************************/

int ClockSize(hWnd, newWidth, newHeight, sizeType)
  HWND hWnd;
  int  newWidth;
  int  newHeight;
  WORD sizeType;
{
  /* Set ClockRect to bound the largest possible circle in the window */
  SetRect((LPRECT) &(ClockRect), 0, 0, newWidth, newHeight);
  CircleClock(newWidth, newHeight);

  if(sizeType == SIZEICONIC)
  {
    /* Update once every minute in the iconic state */
    KillTimer(hWnd, TimerID);
    SetTimer(hWnd, TimerID, (unsigned) ICON_TLEN, 0L);
    bIconic = TRUE;
  }
  else if (bIconic)
  {
    /* Update every second in the opened state (ignore tiling) */
    KillTimer(hWnd, TimerID);
    SetTimer(hWnd, TimerID, OPEN_TLEN, 0L);
    bIconic = FALSE;
  }
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : ClockTimer (HWND)                                           *
 *                                                                         *
 *  PURPOSE  : Update the clock to reflect the most recent time.           *
 *                                                                         *
 ***************************************************************************/

int ClockTimer(hWnd)
  HWND hWnd;
{
  TIME nTime;
  HDC  hDC;

  GetTime(&nTime);

  /* It's possible to change any part of the system at any time through */
  /* the Control Panel. Check for any change in second, minute, or hour */
  if ((nTime.second != oTime.second) ||
      (nTime.minute != oTime.minute) ||
      (nTime.hour   != oTime.hour))
  {
    /* The time has changed -- update the clock */
    hDC = GetDC(hWnd);
    ClockPaint(hWnd, hDC, HANDPAINT);
    ReleaseDC(hWnd, hDC);
  }
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : ClockPaint (HWND, HDC, int)                                 *
 *                                                                         *
 *  PURPOSE  : Paint the clock to display the most recent time.            *
 *                                                                         *
 ***************************************************************************/

int ClockPaint(hWnd, hDC, paintType)
  HWND hWnd;
  HDC  hDC;
  int  paintType;
{
  TIME nTime;

  SetBkMode(hDC, TRANSPARENT);

  lpCirTab = (POINT far *) GlobalLock(hCirTab);

  if (paintType == PAINTALL)
  {
    /* Paint entire clock -- face and hands */
    FillRect(hDC, (LPRECT) &ClockRect, hbrBackgnd);
    DrawFace(hDC);
    DrawFatHand(hDC, HourHandPos(oTime), hpenForegnd, HHAND);
    DrawFatHand(hDC, oTime.minute, hpenForegnd, MHAND);
    if (!bIconic)
    {
      /* Erase old second hand */
      DrawHand(hDC, oTime.second, hpenBackgnd, SECONDTIP, R2_NOT);
    }
        }
  else if (paintType == HANDPAINT)
  {
    GetTime(&nTime);

    if ((!bIconic) && (nTime.second != oTime.second))
    {
      /* Second has changed -- erase old second hand */
      DrawHand(hDC, oTime.second, hpenBackgnd, SECONDTIP, R2_NOT);
    }

    if ((nTime.minute != oTime.minute) || (nTime.hour != oTime.hour))
    {
      /* Hour and/or minute have changed -- update hands */
      if (bIconic)
      {
        /* Erase old minute and hour hands */
        DrawHand(hDC, oTime.minute,
                 hpenBackgnd, MINUTETIP, R2_COPYPEN);
        DrawHand(hDC, HourHandPos(oTime),
                 hpenBackgnd, HOURTIP, R2_COPYPEN);

        /* Draw new minute and hour hands */
        DrawHand(hDC, nTime.minute,
                 hpenForegnd, MINUTETIP, R2_COPYPEN);
        DrawHand(hDC, HourHandPos(nTime),
                 hpenForegnd, HOURTIP, R2_COPYPEN);

      }
      else
      {
        /* Erase old minute and hour fat hands */
        DrawFatHand(hDC, oTime.minute,
              hpenBackgnd, MHAND);
        DrawFatHand(hDC, HourHandPos(oTime),
              hpenBackgnd, HHAND);

        /* Draw new minute and hour fat hands */
        DrawFatHand(hDC, nTime.minute,
              hpenForegnd, MHAND);
        DrawFatHand(hDC, HourHandPos(nTime),
                    hpenForegnd, HHAND);
      }
    }

    if ((!bIconic) && (nTime.second != oTime.second))
    {
      /* second has changed -- draw new second hand */
      DrawHand(hDC, nTime.second,
               hpenBackgnd, SECONDTIP, R2_NOT);
    }

    /* Store most recent time */
    oTime.minute = nTime.minute;
    oTime.hour   = nTime.hour;
    oTime.second = nTime.second;
  }
  GlobalUnlock(hCirTab);
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : DrawFace (HDC)                                              *
 *                                                                         *
 *  PURPOSE  : Draws the clock face.                                       *
 *                                                                         *
 ***************************************************************************/

DrawFace(hDC)
  HDC hDC; /* device context to be used when drawing face */
{
  int    pos;       /* hand position index into the circle table */
  int    dotHeight; /* height of the hour-marking dot            */
  int    dotWidth;  /* width of the hour-marking dot             */
  POINT  dotCenter; /* center point of the hour-marking dot      */
  RECT   rc;

  /* Compute hour-marking dot width, height, and center point */
  dotWidth = (MAXDOTWIDTH * (long) (ClockRect.right - ClockRect.left)) / Horz
  dotHeight = VertEquiv(dotWidth);

  if (dotHeight < MINDOTHEIGHT)
  {
    dotHeight = MINDOTHEIGHT;
  }

  if (dotWidth < MINDOTWIDTH)
  {
    dotWidth = MINDOTWIDTH;
  }

  dotCenter.x = dotWidth >> 1;
  dotCenter.y = dotHeight >> 1;

  /* Compute the clock center and radius */
  InflateRect((LPRECT) &ClockRect, -dotCenter.y, -dotCenter.x);
  ClockRadius = (long) ((ClockRect.right - ClockRect.left) >> 1);
  ClockCenter.x = ClockRect.left + ClockRadius;
  ClockCenter.y = ClockRect.top + ((ClockRect.bottom - ClockRect.top) >> 1);
  InflateRect((LPRECT) &ClockRect, dotCenter.y, dotCenter.x);

  /* Draw the large hour-marking dots and small minute-marking dots */
  for(pos = 0; pos < HANDPOSITIONS; pos++)
  {
    rc.top = (lpCirTab[pos].y * ClockRadius) / CIRTABSCALE + ClockCenter.y;
    rc.left = (lpCirTab[pos].x * ClockRadius) / CIRTABSCALE + ClockCenter.x;
    if (pos % 5)
    {
      if ((dotWidth > MINDOTWIDTH) && (dotHeight > MINDOTHEIGHT))
      {
        /* Draw small minute-marking dot */
        rc.right = rc.left + 1;
        rc.bottom = rc.top + 1;
        FillRect(hDC, (LPRECT) &rc, hbrForegnd);
      }
    }
    else
    {
      /* Draw large hour-marking dot */
      rc.right = rc.left + dotWidth;
      rc.bottom = rc.top + dotHeight;
      OffsetRect((LPRECT) &rc, -dotCenter.x, -dotCenter.y);
      FillRect(hDC, (LPRECT) &rc, hbrForegnd);
      }
  }
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : DrawHand (HDC, int, HPEN, int, int)                         *
 *                                                                         *
 *  PURPOSE  : Draws a thin hand with the specified pen in the specified   *
 *             hand position.                                              *
 *                                                                         *
 ***************************************************************************/

DrawHand(hDC, pos, hPen, scale, patMode)
  HDC  hDC;     /* device context to be used when drawing hand */
  int  pos;     /* hand position index into the circle table   */
  HPEN hPen;    /* pen to be used when drawing hand            */
  int  scale;   /* ClockRadius percentage to scale drawing to  */
  int  patMode; /* pattern mode to be used when drawing hand   */
{
  long radius;

  /* scale length of hand */
  radius = (ClockRadius * scale) / 100;

  /* set pattern mode for hand */
  SetROP2(hDC, patMode);

  /* select pen for hand */
  SelectObject(hDC, hPen);

  /* Draw thin hand */
  MoveTo(hDC, ClockCenter.x, ClockCenter.y);
  LineTo(hDC, ClockCenter.x + (int)((lpCirTab[pos].x * radius) / CIRTABSCALE)
        ClockCenter.y + (int)((lpCirTab[pos].y * radius) / CIRTABSCALE));
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : DrawFatHand (HDC, int, HPEN, BOOL)                          *
 *                                                                         *
 *  PURPOSE  : Draws a fat hand with the specified pen in the specified    *
 *             hand position.                                              *
 *                                                                         *
 ***************************************************************************/

DrawFatHand(hDC, pos, hPen, hHand)
  HDC  hDC;     /* device context to be used when drawing hand */
  int  pos;     /* hand position index into the circle table   */
  HPEN hPen;    /* pen to be used when drawing hand            */
  BOOL hHand;   /* TRUE if drawing hour hand; FALSE otherwise  */
{
  POINT ptTip;  /* coordinates for the tip of the hand        */
  POINT ptTail; /* coordinates for the tail of the hand       */
  POINT ptSide; /* coordinates for the side of the hand       */
  int   index;  /* position index into the circle table       */
  long  scale;  /* ClockRadius percentage to scale drawing to */

  /* set pattern mode for hand */
  SetROP2(hDC, 13);

  /* select pen for hand */
  SelectObject(hDC, hPen);

  /* compute coordinates for the side of the hand */
  scale = (ClockRadius * (hHand ? HOURSIDE : MINUTESIDE)) / 100;
  index = (pos + SIDESHIFT) % HANDPOSITIONS;
  ptSide.y = (lpCirTab[index].y * scale) / CIRTABSCALE;
  ptSide.x = (lpCirTab[index].x * scale) / CIRTABSCALE;

  /* compute coordinates for the tip of the hand */
  scale = (ClockRadius * (hHand ? HOURTIP : MINUTETIP)) / 100;
  ptTip.y = (lpCirTab[pos].y * scale) / CIRTABSCALE;
  ptTip.x = (lpCirTab[pos].x * scale) / CIRTABSCALE;

  /* compute coordinates for the tail of the hand */
  scale = (ClockRadius * (hHand ? HOURTAIL : MINUTETAIL)) / 100;
  index = (pos + TAILSHIFT) % HANDPOSITIONS;
  ptTail.y = (lpCirTab[index].y * scale) / CIRTABSCALE;
  ptTail.x = (lpCirTab[index].x * scale) / CIRTABSCALE;

  /* Draw tip of hand */
  MoveTo(hDC, ClockCenter.x + ptSide.x, ClockCenter.y + ptSide.y);
  LineTo(hDC, ClockCenter.x +  ptTip.x, ClockCenter.y +  ptTip.y);
  MoveTo(hDC, ClockCenter.x - ptSide.x, ClockCenter.y - ptSide.y);
  LineTo(hDC, ClockCenter.x +  ptTip.x, ClockCenter.y +  ptTip.y);

  /* Draw tail of hand */
  MoveTo(hDC, ClockCenter.x + ptSide.x, ClockCenter.y + ptSide.y);
  LineTo(hDC, ClockCenter.x + ptTail.x, ClockCenter.y + ptTail.y);
  MoveTo(hDC, ClockCenter.x - ptSide.x, ClockCenter.y - ptSide.y);
  LineTo(hDC, ClockCenter.x + ptTail.x, ClockCenter.y + ptTail.y);
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : CircleClock (int, int)                                      *
 *                                                                         *
 *  PURPOSE  : Resizes the clock rectangle to keep the face circular.      *
 *                                                                         *
 ***************************************************************************/

CircleClock(maxWidth, maxHeight)
  int maxWidth;    /* the maximum width of the clock face         */
  int maxHeight;   /* the maximum height of the clock face        */
{
  int clockHeight; /* tallest height that will keep face circular */
  int clockWidth;  /* widest width that will keep face circular   */

  if (maxWidth > HorzEquiv(maxHeight))
  {
    /* too wide -- decrease width to keep face circular */
    clockWidth = HorzEquiv(maxHeight);
    ClockRect.left += (maxWidth - clockWidth) >> 1;
    ClockRect.right = ClockRect.left + clockWidth;
  }
  else
  {
    /* too tall -- decrease height to keep face circular */
    clockHeight = VertEquiv(maxWidth);
    ClockRect.top += (maxHeight - clockHeight) >> 1;
    ClockRect.bottom = ClockRect.top + clockHeight;
  }
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : WinMain (HANDLE, HANDLE, LPSTR, int)                        *
 *                                                                         *
 *  PURPOSE  : Calls the initialization function, creates the main appli-  *
 *             cation window, and enters the message loop.                 *
 *                                                                         *
 ***************************************************************************/

int PASCAL WinMain(hInstance, hPrev, lpszCmdLine, cmdShow)
  HANDLE hInstance;
  HANDLE hPrev;
  LPSTR  lpszCmdLine;
  int    cmdShow;
{
  HWND  hWnd;
  MSG   msg;
  HMENU hMenu;
  TIME  nTime;
  int   sysWidth;  /* width of left and right frames                  */
  int   sysHeight; /* height of caption bar and top and bottom frames */
  int   width;     /* width of entire clock window                    */
  int   height;    /* height of entire clock window                   */

  hInst = hInstance;

  LoadString(hInst, IDS_APPNAME, (LPSTR) szBuffer, BUFLEN);

  if (!hPrev)
  {
    /* First instance -- register window class */
    if (!ClockInit())
      return(FALSE);
        }
  else
  {
    /* Not first instance -- get circle table and reset bFirst flag */
    GetInstanceData(hPrev, (PSTR) &hCirTab, sizeof(HANDLE));
    bFirst = FALSE;
        }

  ClockCreate();

  /* compute window height and width */
  sysWidth  = GetSystemMetrics(SM_CXFRAME) * 2;
  sysHeight = GetSystemMetrics(SM_CYCAPTION) + (GetSystemMetrics(SM_CYFRAME)
  width = (HorzRes / 3) + sysWidth;
  height = VertEquiv(width) + sysHeight;

  hWnd = CreateWindow( (LPSTR) szBuffer, /* class name              */
                             (LPSTR) szBuffer, /* The window name         */
                             WS_TILEDWINDOW,   /* window style            */
                             CW_USEDEFAULT,    /* use default positioning */
                             0,                /* y not used              */
                             width,            /* window width            */
           height,           /* window height           */
                             NULL,             /* NULL parent handle      */
                             NULL,             /* NULL menu/child handle  */
                             hInst,            /* program instance        */
                             NULL              /* NULL data structure ref.*/
         );

  GetTime(&nTime);
  GetTime(&oTime);
  while ((nTime.second == oTime.second) &&
               (nTime.minute == oTime.minute) &&
               (nTime.hour   == oTime.hour)     )
  {
    GetTime(&oTime);
  }

  if (!SetTimer(hWnd, TimerID, OPEN_TLEN, 0L))
  {
    LPSTR szTooMany;

    /* 16 public timers already in use -- post error and exit */
    szTooMany = (LPSTR)(unsigned long) LocalAlloc(LPTR, 40);
    LoadString(hInst, IDS_TOOMANY, szTooMany, 40);
    MessageBox((HWND) NULL, szTooMany, (LPSTR) szBuffer,
         MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
    DeleteTools();
    return(FALSE);
  }

  /* Add the "About..." menu item to the bottom of the system menu */
  LoadString(hInst, IDS_ABOUTMENU, (LPSTR) szBuffer, BUFLEN);
  hMenu = GetSystemMenu(hWnd, FALSE);
  ChangeMenu(hMenu, 0, (LPSTR) szBuffer, IDM_ABOUT, MF_APPEND | MF_STRING);

  ShowWindow(hWnd, cmdShow);

  /* Process messages until program termination */
  while (GetMessage((LPMSG) &msg, NULL, 0, 0))
  {
    TranslateMessage((LPMSG) &msg);
    DispatchMessage((LPMSG) &msg);
  }
  return(msg.wParam);
}


/***************************************************************************
 *                                                                         *
 *  FUNCTION : ClockInit ()                                                *
 *                                                                         *
 *  PURPOSE  : Registers the applicatoin windwo class and initializes the  *
 *             circle values for the clock face.                           *
 *                                                                         *
 ***************************************************************************/

ClockInit()
{
  PWNDCLASS pClockClass;
  HANDLE    hRes;
  char      szData[5];

  pClockClass = (PWNDCLASS) LocalAlloc(LPTR, sizeof(WNDCLASS));

  pClockClass->lpszClassName = (LPSTR) szBuffer;
  pClockClass->hbrBackground = (HBRUSH) NULL;
  pClockClass->style         = CS_VREDRAW | CS_HREDRAW | CS_BYTEALIGNCLIENT;
  pClockClass->hInstance     = hInst;
  pClockClass->lpfnWndProc   = ClockWndProc;
  pClockClass->hCursor       = LoadCursor(NULL, IDC_ARROW);
  pClockClass->hIcon         = NULL;

  if (!RegisterClass((LPWNDCLASS) pClockClass))
  {
    /* Error registering class -- return */
    return(FALSE);
  }

  LocalFree((HANDLE) pClockClass);

  /* Load in pre-computed circle table cosine values from resource file */
  LoadString(hInst, IDS_DATA, (LPSTR) szData, 5);
  hRes = FindResource(hInst, (LPSTR) szBuffer, (LPSTR) szData);
  if (!hRes)
  {
    /* Could not find circle table resource data -- return */
    return(FALSE);
  }

  hCirTab = LoadResource(hInst, hRes);
  LockResource(hCirTab);

  return(TRUE);
}


CURSOR.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\CURSOR\CURSOR.C

/****************************************************************************

    PROGRAM: Cursor.c

    PURPOSE: Demonstrates how to manipulate a cursor and select a region

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box
        sieve() - time consuming function, generates primes

****************************************************************************/

#include "windows.h"
#include "cursor.h"

HANDLE hInst;

char str[255];                              /* general-purpose string buffer

HCURSOR hSaveCursor;                        /* handle to current cursor
HCURSOR hHourGlass;                         /* handle to hourglass cursor

BOOL bTrack = FALSE;                        /* TRUE if left button clicked
int OrgX = 0, OrgY = 0;                     /* original cursor position
int PrevX = 0, PrevY = 0;                   /* current cursor position
int X = 0, Y = 0;                           /* last cursor position
RECT Rect;                                  /* selection rectangle

POINT ptCursor;                             /* x and y coordinates of cursor
int repeat = 1;                             /* repeat count of keystroke

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    MessageBox (
          GetFocus ()
        , "Use the mouse button in this program for an example of graphics "
                        "selection, or the <Enter> key for an example of "
                        "using a special cursor to reflect a program state."
        , "Cursor Sample Application"
        , MB_ICONASTERISK | MB_OK
    );

    while (GetMessage(&msg, NULL, NULL, NULL)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(hInstance, "bullseye");
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "CursorMenu";
    wc.lpszClassName = "CursorWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    HWND            hWnd;

    hInst = hInstance;

    strcpy(str,"");

    hHourGlass = LoadCursor(NULL, IDC_WAIT);

    hWnd = CreateWindow(
        "CursorWClass",
        "Cursor Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hWnd)
        return (FALSE);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND     - application menu (About dialog box)
        WM_CHAR        - ASCII key value received
        WM_LBUTTONDOWN - left mouse button
        WM_MOUSEMOVE   - mouse movement
        WM_LBUTTONUP   - left button released
        WM_KEYDOWN     - key pressed
        WM_KEYUPS      - key released
        WM_PAINT       - update window
  WM_DESTROY     - destroy window

    COMMENTS:

        When the left mouse button is pressed, btrack is set to TRUE so that
        the code for WM_MOUSEMOVE will keep track of the mouse and update
        the box accordingly.  Once the button is released, btrack is set to
        FALSE, and the current position is saved.  Holding the SHIFT key
        while pressing the left button will extend the current box rather
        then erasing it and starting a new one.

        When an arrow key is pressed, the cursor is repositioned in the
        direction of the arrow key.  A repeat count is kept so that the
        longer the user holds down the arrow key, the faster it will move.
        As soon as the key is released, the repeat count is set to 1 for
        normal cursor movement.

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;
    HDC hDC;

    switch (message) {
  case WM_COMMAND:
      if (wParam == IDM_ABOUT) {
    lpProcAbout = MakeProcInstance(About, hInst);

    DialogBox(hInst,
        "AboutBox",
        hWnd,
        lpProcAbout);

    FreeProcInstance(lpProcAbout);
    break;
      }
      else
    return (DefWindowProc(hWnd, message, wParam, lParam));

        case WM_CHAR:
            if (wParam == '\r') {
                SetCapture(hWnd);

                /* Set the cursor to an hourglass */

                hSaveCursor = SetCursor(hHourGlass);

                strcpy (str, "Calculating prime numbers...");
                InvalidateRect (hWnd, NULL, TRUE);
                UpdateWindow (hWnd);
                wsprintf(str, "Calculated %d primes.       ", sieve());
                InvalidateRect (hWnd, NULL, TRUE);
                UpdateWindow (hWnd);

                SetCursor(hSaveCursor);          /* Restores previous cursor
                ReleaseCapture();
            }
            break;

        case WM_LBUTTONDOWN:
            bTrack = TRUE;
            strcpy (str, "");
            PrevX = LOWORD(lParam);
            PrevY = HIWORD(lParam);
            if (!(wParam & MK_SHIFT)) {       /* If shift key is not pressed
                OrgX = LOWORD(lParam);
                OrgY = HIWORD(lParam);
            }
            InvalidateRect (hWnd, NULL, TRUE);
            UpdateWindow (hWnd);

            /* Capture all input even if the mouse goes outside of window */

            SetCapture(hWnd);
            break;

        case WM_MOUSEMOVE:
            {
                RECT        rectClient;
                int         NextX;
                int         NextY;

                if (bTrack) {
                    NextX = LOWORD(lParam);
                    NextY = HIWORD(lParam);

                    /* Do not draw outside the window's client area */

                    GetClientRect (hWnd, &rectClient);
                    if (NextX < rectClient.left) {
                        NextX = rectClient.left;
                    } else if (NextX >= rectClient.right) {
                        NextX = rectClient.right - 1;
                    }
                    if (NextY < rectClient.top) {
                        NextY = rectClient.top;
                    } else if (NextY >= rectClient.bottom) {
                        NextY = rectClient.bottom - 1;
                    }

                    /* If the mouse position has changed, then clear the */
                    /* previous rectangle and draw the new one.          */

                    if ((NextX != PrevX) || (NextY != PrevY)) {
                        hDC = GetDC(hWnd);
                        SetROP2(hDC, R2_NOT);          /* Erases the previous
                        MoveTo(hDC, OrgX, OrgY);
                        LineTo(hDC, OrgX, PrevY);
                        LineTo(hDC, PrevX, PrevY);
                        LineTo(hDC, PrevX, OrgY);
                        LineTo(hDC, OrgX, OrgY);

                        /* Get the current mouse position */

                        PrevX = NextX;
                        PrevY = NextY;
                        MoveTo(hDC, OrgX, OrgY);        /* Draws the new box
                        LineTo(hDC, OrgX, PrevY);
                        LineTo(hDC, PrevX, PrevY);
                        LineTo(hDC, PrevX, OrgY);
                        LineTo(hDC, OrgX, OrgY);
                        ReleaseDC(hWnd, hDC);
                    }
                }
            }
            break;

        case WM_LBUTTONUP:
      bTrack = FALSE;        /* No longer creating a selection */
      ReleaseCapture();        /* Releases hold on mouse input */

      X = LOWORD(lParam);       /* Saves the current value      */
            Y = HIWORD(lParam);
            break;

        case WM_KEYDOWN:
            if (wParam != VK_LEFT && wParam != VK_RIGHT
                    && wParam != VK_UP && wParam != VK_DOWN)
                break;

            GetCursorPos(&ptCursor);

            /* Convert screen coordinates to client coordinates */

            ScreenToClient(hWnd, &ptCursor);
            repeat++;                           /* Increases the repeat rate

            switch (wParam) {

            /* Adjust cursor position according to which key was pressed. */

                case VK_LEFT:
                    ptCursor.x -= repeat;
                    break;

                case VK_RIGHT:
                    ptCursor.x += repeat;
                    break;

                case VK_UP:
                    ptCursor.y -= repeat;
                    break;

                case VK_DOWN:
                    ptCursor.y += repeat;
                    break;

            }

            /* Get the client boundaries */

            GetClientRect(hWnd, &Rect);

            /* Do not draw outside the window's client area */

            if (ptCursor.x >= Rect.right)
                ptCursor.x = Rect.right - 1;
            else if (ptCursor.x < Rect.left)
                ptCursor.x = Rect.left;
            if (ptCursor.y >= Rect.bottom)
                ptCursor.y = Rect.bottom - 1;
            else if (ptCursor.y < Rect.top)
                ptCursor.y = Rect.top;

            /* Convert the coordinates to screen coordinates */

            ClientToScreen(hWnd, &ptCursor);
            SetCursorPos(ptCursor.x, ptCursor.y);
            break;

        case WM_KEYUP:
            repeat = 1;                          /* Clears the repeat count.
            break;

        case WM_ACTIVATE:
            if (!GetSystemMetrics(SM_MOUSEPRESENT)) {
                if (!HIWORD(lParam)) {
                    if (wParam) {
                        SetCursor(LoadCursor(hInst, "bullseye"));
                        ptCursor.x = X;
                        ptCursor.y = Y;
                        ClientToScreen(hWnd, &ptCursor);
                        SetCursorPos(ptCursor.x, ptCursor.y);
                    }
                    ShowCursor(wParam);
                }
            }
            break;

        case WM_PAINT:
            {
                PAINTSTRUCT     ps;

                hDC = BeginPaint (hWnd, &ps);
                if (OrgX != PrevX || OrgY != PrevY) {
                    MoveTo(hDC, OrgX, OrgY);
                    LineTo(hDC, OrgX, PrevY);
                    LineTo(hDC, PrevX, PrevY);
                    LineTo(hDC, PrevX, OrgY);
                    LineTo(hDC, OrgX, OrgY);
                }
                TextOut (hDC, 1, 1, str, strlen (str));
                EndPaint (hWnd, &ps);
            }
            break;

  case WM_DESTROY:
      PostQuitMessage(0);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


/****************************************************************************

    FUNCTION: Sieve()

    PURPOSE:  Example of time consuming process

    COMMENTS:

  Sieve of Eratosthenes, BYTE, Volume 8, Number 1, by Jim Gilbreath
  and Gary Gilbreath.  Code changed to give correct results.

  One could return the count, and after restoring the cursor, use
  sprintf() to copy the information to a string which could then be
  put up in a MessageBox().

****************************************************************************/

 NITER  20           /* number of iterations */
#define SIZE  8190

char flags[SIZE+1]={ 0};

sieve() {
    int i,k;
    int iter, count;

    for (iter = 1; iter <= NITER; iter++) {   /* Does sieve NITER times */
  count = 0;
  for (i = 0; i <= SIZE; i++)     /* Sets all flags TRUE     */
      flags[i] = TRUE;

  for (i = 2; i <= SIZE; i++) {
      if (flags[i] ) {          /* Found a prime?      */
    for (k = i + i; k <= SIZE; k += i)
        flags[k] = FALSE;       /* Cancelsits multiples */
    count++;
      }
  }
    }
    return (count);
}


DEFDLG.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\DEFPROCS\DEFDLG.C


/*--------------------------------------------------------------------------*
/*                      */
/*  DefDlgProc() -                  */
/*                      */
/*--------------------------------------------------------------------------*

LONG FAR PASCAL DefDlgProc(hwnd, message, wParam, lParam)

register HWND hwnd;
register WORD message;
WORD        wParam;
LONG        lParam;

{
  HWND        hwndT1;
  HWND        hwndT2;
  int        result;

  if (!CheckHwnd(hwnd))
      return(NULL);

  ((PDLG)hwnd)->resultWP = 0L;

  if (((PDLG)hwnd)->lpfnDlg == NULL ||
      !(result = (*((PDLG)hwnd)->lpfnDlg)(hwnd, message, wParam, lParam)))
    {
      switch (message)
  {
    case WM_ERASEBKGND:
        FillWindow(hwnd, hwnd, (HDC)wParam, (HBRUSH)CTLCOLOR_DLG);
        return((LONG)TRUE);

    case WM_SHOWWINDOW:
        /* If hiding the window, save the focus. If showing the window
         * by means of a SW_* command and the fEnd bit is set, do not
         * pass to DWP so it won't get shown.
         */
        if (!wParam && ((PDLG)hwnd)->hwndFocusSave == NULL)
      ((PDLG)hwnd)->hwndFocusSave = hwndFocus;
        else if (LOWORD(lParam) != 0 && ((PDLG)hwnd)->fEnd)
      break;
        goto CallDWP;

    case WM_ACTIVATE:
        fDialog = FALSE;
        if (wParam)
    {
      fDialog = TRUE;
      if (((PDLG)hwnd)->hwndFocusSave)
        {
          SetFocus(((PDLG)hwnd)->hwndFocusSave);

          /* Set to NULL so we don't reset if we get more than
       one activate message. */
          ((PDLG)hwnd)->hwndFocusSave = NULL;
        }
    }
        else if (hwndFocus && IsChild(hwnd, hwndFocus) &&
           ((PDLG)hwnd)->hwndFocusSave == NULL)
    {
      /* Must remember focus if deactivated */
      ((PDLG)hwnd)->hwndFocusSave = hwndFocus;
    }
        break;

    case WM_SETFOCUS:
              if (!((PDLG)hwnd)->fEnd)
                  /* Don't set the focus if we are ending this dialog box
       */
              DlgSetFocus(GetFirstTab(hwnd));
        break;

    case WM_CLOSE:
        /* Make sure cancel button is not disabled before sending the
         * IDCANCEL.  Note that we need to do this as a message instead
         * of directly calling the dlg proc so that any dialog box
         * filters get this.
         */
        hwndT1 = GetDlgItem(hwnd, IDCANCEL);
        if (hwndT1 && TestWF(hwndT1, WFDISABLED))
      MessageBeep(0);
        else
      PostMessage(hwnd, WM_COMMAND, IDCANCEL, 0L);
        break;

    case WM_NCDESTROY:
              fDialog = FALSE;      /* clear this flag */
        if (!(hwnd->style & DS_LOCALEDIT))
    {
      if (((PDLG)hwnd)->hData)
        {
          GlobalUnWire(((PDLG)hwnd)->hData);
          ReleaseEditDS(((PDLG)hwnd)->hData);
        }
    }
        /* Delete the user defined font if any */
        if (((PDLG)hwnd)->hUserFont)
      DeleteObject(((PDLG)hwnd)->hUserFont);

        /* Gotta let DefWindowProc do its thing here or we won't
         * get all of the little chunks of memory freed for this
         * window (szName and rgwScroll).
         */
        DefWindowProc(hwnd, message, wParam, lParam);
        break;

    case DM_SETDEFID:
              if (!(((PDLG)hwnd)->fEnd) && (((PDLG)hwnd)->result != wParam))
                {
                  /* Make sure that the new default button has the highlight.
       * We need to blow this off if we are ending the dialog box
       * because hwnd->result is no longer a default window id but
       * rather the return value of the dialog box.
       */
                  /* Catch the case of setting the defid to null or setting
       * the defid to something else when it was initially null.
       */
                  CheckDefPushButton(hwnd,
                       (((PDLG)hwnd)->result ?
                                     GetDlgItem(hwnd, ((PDLG)hwnd)->result) :
                                     NULL),
                       (wParam ? GetDlgItem(hwnd, wParam) : NULL));
               ((PDLG)hwnd)->result = wParam;
                }
        return(TRUE);

    case DM_GETDEFID:
        return(MAKELONG(((PDLG)hwnd)->result, DC_HASDEFID));

    /* This message was added so that user defined controls that want
     * tab keys can pass the tab off to the next/previous control in the
     * dialog box.  Without this, all they could do was set the focus
     * which didn't do the default button stuff.
     */
    case WM_NEXTDLGCTL:
        hwndT2 = hwndFocus;
        if (LOWORD(lParam))
    {
      if (hwndT2 == NULL)
          hwndT2 = hwnd;

      /* wParam contains the hwnd of the ctl to set focus to. */
      hwndT1 = (HWND)wParam;
    }
        else
    {
      if (hwndT2 == NULL)
        {
          /* Set focus to the first tab item. */
          hwndT1 = GetFirstTab(hwnd);
          hwndT2 = hwnd;
        }
      else
        {
          /* If window with focus not a dlg ctl, ignore message. */
          if (!IsChild(hwnd, hwndT2))
        return(TRUE);

          /* wParam = TRUE for previous, FALSE for next */
          hwndT1 = GetNextDlgTabItem(hwnd, hwndT2, wParam);
        }
    }

        DlgSetFocus(hwndT1);
        CheckDefPushButton(hwnd, hwndT2, hwndT1);
        return(TRUE);

    case WM_LBUTTONDOWN:
    case WM_NCLBUTTONDOWN:
        hwndT1 = hwndFocus;
        if (hwndT1->pcls->lpfnWndProc == ComboBoxCtlWndProc)
      /* If user clicks anywhere in dialog box and a combo box (or
       * the editcontrol of a combo box) has the focus, then hide
       * it's listbox.
       */
      SendMessage(hwndT1,CB_SHOWDROPDOWN, FALSE, 0L);
        else
        if (hwndT1->pcls->lpfnWndProc == EditWndProc &&
      hwndT1->hwndParent->pcls->lpfnWndProc==ComboBoxCtlWndProc)
    {
      SendMessage(hwndT1->hwndParent,CB_SHOWDROPDOWN, FALSE, 0L);
    }
        /* Always send the message off to DefWndProc */
        goto CallDWP;

    case WM_GETFONT:
        return(((PDLG)hwnd)->hUserFont);

    case WM_VKEYTOITEM:
    case WM_COMPAREITEM:
    case WM_CHARTOITEM:
        /* We need to return the 0 the app may have returned for these
         * items instead of calling defwindow proc.
         */
        return(result);

    default:
CallDWP:
        return(DefWindowProc(hwnd, message, wParam, lParam));
  }
    }

  /* Must return brush which apps dlgfn returns. */
  if (message == WM_CTLCOLOR ||
      message == WM_COMPAREITEM ||
      message == WM_VKEYTOITEM ||
      message == WM_CHARTOITEM)
      return(result);

  return(((PDLG)hwnd)->resultWP);
}


DEFWND.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\DEFPROCS\DEFWND.C

/*--------------------------------------------------------------------------*
/*                      */
/*  DefWindowProc() -                  */
/*                      */
/*--------------------------------------------------------------------------*

LONG FAR PASCAL DefWindowProc(hwnd, message, wParam, lParam)

register HWND hwnd;
WORD        message;
register WORD wParam;
LONG        lParam;

{
  int        i;
  HDC        hdc;
  PAINTSTRUCT ps;
  HICON        hIcon;
  RECT        rc;
  HANDLE      hCurs;
  HBRUSH      hbr;
  HWND        hwndT;

  if (!CheckHwnd(hwnd))
      return((DWORD)FALSE);

  switch (message)
    {
      case WM_NCACTIVATE:
    if (wParam != 0)
        SetWF(hwnd, WFFRAMEON);
    else
        ClrWF(hwnd, WFFRAMEON);

    if (TestWF(hwnd, WFVISIBLE) && !TestWF(hwnd, WFNONCPAINT))
      {
        hdc = GetWindowDC(hwnd);
        DrawCaption(hwnd, hdc, TRUE, TestWF(hwnd, WFFRAMEON));
        InternalReleaseDC(hdc);
        if (TestWF(hwnd,WFMINIMIZED))
      RedrawIconTitle(hwnd);
      }
    return(TRUE);

      case WM_NCHITTEST:
    return(FindNCHit(hwnd, lParam));

      case WM_NCCALCSIZE:
    CalcClientRect(hwnd, (LPRECT)lParam);
    break;

      case WM_NCLBUTTONDOWN:
  {
    WORD       cmd;
    RECT       rcWindow;
    RECT       rcCapt;
    RECT       rcInvert;
    RECT       rcWindowSave;

    cmd = 0;

    switch(wParam)
      {
        case HTZOOM:
        case HTREDUCE:
      GetWindowRect(hwnd, (LPRECT)&rcWindow);
      CopyRect((LPRECT)&rcWindowSave, (LPRECT)&rcWindow);

      if (TestWF(hwnd, WFSIZEBOX))
          InflateRect((LPRECT)&rcWindow,
                -cxSzBorderPlus1, -cySzBorderPlus1);
      else
          InflateRect((LPRECT)&rcWindow, -cxBorder, -cyBorder);

      rcCapt.right = rcWindow.right + cxBorder;
                  rcCapt.left = rcWindow.right - oemInfo.bmReduce.cx-cxBorder

      if (wParam == HTREDUCE)
          cmd = SC_MINIMIZE;
      else if (TestWF(hwnd, WFMAXIMIZED))
          cmd = SC_RESTORE;
      else
          cmd = SC_MAXIMIZE;

      if (wParam == HTREDUCE && TestWF(hwnd, WFMAXBOX))
                      OffsetRect((LPRECT)&rcCapt, -oemInfo.bmReduce.cx, 0);

      rcCapt.top = rcWindow.top;
      rcCapt.bottom = rcCapt.top + cyCaption;

      CopyRect((LPRECT)&rcInvert, (LPRECT)&rcCapt);
                  InflateRect((LPRECT)&rcInvert, -cxBorder, -cyBorder);

                  rcInvert.right += cxBorder;
      rcInvert.left += cxBorder;

      /* Converting to window coordinates. */
      OffsetRect((LPRECT)&rcInvert,
           -(rcWindowSave.left + cxBorder),
           -(rcWindowSave.top + cyBorder));
      /* Wait for the BUTTONUP message and see if cursor is still
       * in the Minimize or Maximize box.
       *
       * NOTE: rcInvert is in window coords, rcCapt is in screen
       * coords
       */
                  if (!DepressTitleButton(hwnd, rcCapt, rcInvert, wParam))
          cmd = 0;

      break;

        default:
      if (wParam >= HTSIZEFIRST && wParam <= HTSIZELAST)
          /* Change HT into a MV command. */
          cmd = SC_SIZE + (wParam - HTSIZEFIRST + MVSIZEFIRST);
      }

    if (cmd != 0)
      {
        /* For SysCommands on system menu, don't do if
         * menu item is disabled.
         */
        if (TestWF(hwnd, WFSYSMENU))
    {
                  /* don't check old app child windows
       */
      if (LOWORD(GetExpWinVer(hwnd->hInstance)) >= VER ||
          !TestwndChild(hwnd))
        {
          SetSysMenu(hwnd);
          if (GetMenuState(GetSysMenuHandle(hwnd), cmd & 0xFFF0,
        MF_BYCOMMAND) & (MF_DISABLED | MF_GRAYED))
        break;
        }
    }
        SendMessage(hwnd, WM_SYSCOMMAND, cmd, lParam);
        break;
      }
    /*** FALL THRU ***/
  }

      case WM_NCMOUSEMOVE:
      case WM_NCLBUTTONUP:
      case WM_NCLBUTTONDBLCLK:
    HandleNCMouseGuys(hwnd, message, wParam, lParam);
    break;

      case WM_CANCELMODE:
    if (hwndCapture == hwnd && pfnSB != NULL)
        EndScroll(hwnd, TRUE);

          if (fMenu && hwndMenu == hwnd)
              EndMenu();

    /* If the capture is still set, just release at this point.
     * Can put other End* functions in later.
     */
    if (hwnd == hwndCapture)
        ReleaseCapture();
    break;

      case WM_NCCREATE:
    if (TestWF(hwnd, (WFHSCROLL | WFVSCROLL)))
        if (InitPwSB(hwnd) == NULL)
      return((LONG)FALSE);

    return((LONG)DefSetText(hwnd, ((LPCREATESTRUCT)lParam)->lpszName));

      case WM_NCDESTROY:
    if (hwnd->hName)
        hwnd->hName = TextFree(hwnd->hName);
    break;

      case WM_NCPAINT:
    /* Force the drawing of the menu. */
    SetWF(hwnd, WFMENUDRAW);
    DrawWindowFrame(hwnd, (HRGN)wParam);
    ClrWF(hwnd, WFMENUDRAW);
    break;

      case WM_SETTEXT:
    DefSetText(hwnd, (LPSTR)lParam);
    if (TestWF(hwnd, WFVISIBLE))
            {
        if (TestWF(hwnd,WFMINIMIZED))
    {
      ShowIconTitle(hwnd,FALSE);
      ShowIconTitle(hwnd,TRUE);
    }
        else if (TestWF(hwnd, WFBORDERMASK) == (BYTE)LOBYTE(WFCAPTION))
    {
      hdc = GetWindowDC(hwnd);
      DrawCaption(hwnd, hdc, FALSE, TestWF(hwnd, WFFRAMEON));
      InternalReleaseDC(hdc);
    }
            }
    break;

      case WM_GETTEXT:
    if (wParam)
            {
              if (hwnd->hName)
            return (DWORD)TextCopy(hwnd->hName,(LPSTR)lParam,wParam);

              /* else Null terminate the text buffer since there is no text.
         */
        ((LPSTR)lParam)[0] = NULL;
            }
    return (0L);


      case WM_GETTEXTLENGTH:
    if (hwnd->hName)
        return(lstrlen(TextPointer(hwnd->hName)));

          /* else */
          return(0L);

      case WM_CLOSE:
    DestroyWindow(hwnd);
    break;

      case WM_PAINT:
    BeginPaint(hwnd, (LPPAINTSTRUCT)&ps);
    EndPaint(hwnd, (LPPAINTSTRUCT)&ps);
    break;

      case WM_PAINTICON:
          /* Draw the icon through the window DC if app used own DC. If own D
     * is used the mapping mode may not be MM_TEXT.
     */
          BeginPaint(hwnd, (LPPAINTSTRUCT)&ps);
          if (TestCF(hwnd, CFOWNDC) || TestCF(hwnd, CFCLASSDC))
            {
              /* If owndc, do the end paint now so that the
         * erasebackgrounds/validate regions go through properly. Then
         * we get a clean window dc to draw the icon into.
         */
              InternalEndPaint(hwnd, (LPPAINTSTRUCT)&ps, TRUE);
              hdc = GetWindowDC(hwnd);
            }
          else
            {
              hdc = ps.hdc;
            }

    /* wParam is TRUE to draw icon, FALSE to ignore paint. */
    if (wParam)
      {
              hIcon = (HICON)(PCLS)(hwnd->pcls)->hIcon;
              GetClientRect(hwnd, (LPRECT)&rc);

              rc.left = (rc.right - rgwSysMet[SM_CXICON]) >> 1;
        rc.top = (rc.bottom - rgwSysMet[SM_CYICON]) >> 1;

        DrawIcon(hdc, rc.left, rc.top, hIcon);
      }

    /* Delete the update region. */
          if (TestCF(hwnd, CFOWNDC) || TestCF(hwnd, CFCLASSDC))
            {
              InternalReleaseDC(hdc);
              /* ValidateRect(hwnd, NULL); */
            }
          else
              InternalEndPaint(hwnd, (LPPAINTSTRUCT)&ps, TRUE);
    break;

      case WM_ICONERASEBKGND:
          /* Erase the icon through the window DC if app used own DC. If own
     * DC is used the mapping mode may not be MM_TEXT.
     */
          if (TestCF(hwnd, CFOWNDC) || TestCF(hwnd, CFCLASSDC))
          hdc = GetWindowDC(hwnd);
          else
           hdc = (HDC)wParam;

          if (TestWF(hwnd, WFCHILD))    /* for MDI child icons */
            {
              if ((hbr = GetBackBrush(hwnd->hwndParent)) == NULL)
    {
                  /* No brush, punt. */
                  goto AbortIconEraseBkGnd;
                }
              else
                  goto ICantBelieveIUsedAGoToStatement;
      }

    if (hbmWallpaper)
            {
              /* Since desktop bitmaps are done on a wm_paint message (and no
         * erasebkgnd), we need to call the paint proc with our dc
         */
              PaintDesktop(hdc);
              /* SendMessage(hwndDesktop, WM_ERASEBKGND, hdc, 0L);*/
            }
    else
      {
        hbr = sysClrObjects.hbrDesktop;
ICantBelieveIUsedAGoToStatement:
        FillWindow(hwnd->hwndParent,hwnd,hdc,hbr);
      }

AbortIconEraseBkGnd:
          if (TestCF(hwnd, CFOWNDC) || TestCF(hwnd, CFCLASSDC))
          InternalReleaseDC(hdc);

    return((LONG)TRUE);

      case WM_ERASEBKGND:
    if ((hbr = GetBackBrush(hwnd)) != NULL)
      {
        FillWindow(hwnd, hwnd, (HDC)wParam, hbr);
        return((LONG)TRUE);
      }
    break;

      case WM_QUERYOPEN:
      case WM_QUERYENDSESSION:
    return((LONG)TRUE);

      case WM_SYSCOMMAND:
    SysCommand(hwnd, wParam, lParam);
    break;

      case WM_KEYDOWN:
    if (wParam == VK_F10)
        fF10Status = TRUE;
    break;

      case WM_SYSKEYDOWN:
    /* Is the ALT key down? */
    if (HIWORD(lParam) & SYS_ALTERNATE)
      {
        /* Toggle the fMenuStatus iff this is NOT a repeat KEYDOWN
         * message; Only if the prev key state was 0, then this is the
         * first KEYDOWN message and then we consider toggling menu
               * status;
         */
        if((HIWORD(lParam) & SYS_PREVKEYSTATE) == 0)
          {
            /* Don't have to lock hwndActive because it's processing this
         key. */
            if ((wParam == VK_MENU) && (!fMenuStatus))
          fMenuStatus = TRUE;
            else
          fMenuStatus = FALSE;
    }

        fF10Status = FALSE;

        DWP_ProcessVirtKey(wParam);
      }
    else
      {
        if (wParam == VK_F10)
      fF10Status = TRUE;
        else
    {
      if (wParam == VK_ESCAPE)
        {
          if(GetKeyState(VK_SHIFT) < 0)
               SendMessage(hwnd, WM_SYSCOMMAND,
               SC_KEYMENU, (DWORD)MENUSYSMENU);
        }
    }
      }
    break;

      case WM_KEYUP:
      case WM_SYSKEYUP:
    /* press and release F10 or ALT.Send this only to top-level windows,
     * otherwise MDI gets confused.  The fix in which DefMDIChildProc()
     * passed up the message was insufficient in the case a child window
     * of the MDI child had the focus.
     */
    if ((wParam == VK_MENU && (fMenuStatus == TRUE)) ||
        (wParam == VK_F10 && fF10Status) )
        SendMessage(GetTopLevelWindow(hwnd), WM_SYSCOMMAND, SC_KEYMENU,
      (DWORD)0);

    fF10Status = fMenuStatus = FALSE;
    break;

      case WM_SYSCHAR:
    /* If syskey is down and we have a char... */
    fMenuStatus = FALSE;
    if ((HIWORD(lParam) & SYS_ALTERNATE) && wParam)
      {
        if (wParam == VK_TAB || wParam == VK_ESCAPE)
      break;

        /* Send ALT-SPACE only to top-level windows. */
        if ((wParam == MENUSYSMENU) && (TestwndChild(hwnd)))
      SendMessage(hwnd->hwndParent, message, wParam, lParam);
        else
      SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, (DWORD)wParam);
      }
    else
        /* Ctrl-Esc produces a WM_SYSCHAR, But should not beep; */
        if(wParam != VK_ESCAPE)
            MessageBeep(0);
    break;

      case WM_CHARTOITEM:
      case WM_VKEYTOITEM:
          /* Do default processing for keystrokes into owner draw
             listboxes. */
          return(-1);


      case WM_ACTIVATE:
    if (wParam)
        SetFocus(hwnd);
    break;

      case WM_SETREDRAW:
    DWP_SetRedraw(hwnd, wParam);
    break;

      case WM_SHOWWINDOW:
    /* Non null descriptor implies popup hide or show. */
    /* We should check whether it is a popup window or Owned window */
    if (LOWORD(lParam) != 0 && (TestwndPopup(hwnd) || hwnd -> hwndOwner))
      {
        /* IF NOT(showing, invisible, and not set as hidden) AND
         *   NOT(hiding and not visible)
         */
        if (!(wParam != 0 && !TestWF(hwnd, WFVISIBLE) &&
      !TestWF(hwnd, WFHIDDENPOPUP)) &&
      !(wParam == 0 && !TestWF(hwnd, WFVISIBLE)))
    {
      /* Are we showing? */
      if (wParam)
          /* Yes, clear the hidden popup flag. */
          ClrWF(hwnd, WFHIDDENPOPUP);
      else
          /* No, Set the hidden popup flag. */
          SetWF(hwnd, WFHIDDENPOPUP);

      ShowWindow(hwnd,
           (wParam ? SHOW_OPENNOACTIVATE : HIDE_WINDOW));
    }
      }
    break;

      case WM_CTLCOLOR:
    if (HIWORD(lParam) != CTLCOLOR_SCROLLBAR)
      {
        SetBkColor((HDC)wParam, sysColors.clrWindow);
        SetTextColor((HDC)wParam, sysColors.clrWindowText);
        hbr = sysClrObjects.hbrWindow;
      }
    else
      {
              SetBkColor((HDC)wParam, 0x00ffffff);
              SetTextColor((HDC)wParam, (LONG)0x00000000);
        hbr = sysClrObjects.hbrScrollbar;
        UnrealizeObject(hbr);
      }

    return((DWORD)hbr);

      case WM_SETCURSOR:
    /* wParam  == hwnd that cursor is over
     * lParamL == Hit test area code (result of WM_NCHITTEST)
     * lParamH == Mouse message number
     */
    if (HIWORD(lParam) != 0 &&
        LOWORD(lParam) >= HTSIZEFIRST &&
        LOWORD(lParam) <= HTSIZELAST)
      {
        SetCursor(rghCursor[LOWORD(lParam) - HTSIZEFIRST + MVSIZEFIRST]);
        break;
      }

    if ((hwndT = GetChildParent(hwnd)) != NULL &&
           (BOOL)SendMessage(hwndT, WM_SETCURSOR, wParam, lParam))
        return((LONG)TRUE);

    if (HIWORD(lParam) == 0)
      {
        hCurs = hCursNormal;
        SetCursor(hCurs);
      }
    else
      {
        switch (LOWORD(lParam))
    {
      case HTCLIENT:
          if (((HWND)wParam)->pcls->hCursor != NULL)
        SetCursor(((HWND)wParam)->pcls->hCursor);
          break;

      case HTERROR:
          switch (HIWORD(lParam))
      {
        case WM_LBUTTONDOWN:
            if ((hwndT = DWP_GetEnabledPopup(hwnd)) != NULL)
        {
          if (hwndT != hwndDesktop->hwndChild)
            {
              SetWindowPos(hwnd, NULL,
               0, 0, 0, 0,
               SWP_NOMOVE | SWP_NOSIZE |
               SWP_NOACTIVATE);
              SetActiveWindow(hwndT);
              break;
            }
        }

            /*** FALL THRU ***/

        case WM_RBUTTONDOWN:
        case WM_MBUTTONDOWN:
                              MessageBeep(0);
            break;
      }
          /*** FALL THRU ***/

      default:
          SetCursor(hCursNormal);
    }
      }

    return((LONG)FALSE);

      case WM_MOUSEACTIVATE:
    if ((hwndT = GetChildParent(hwnd)) != NULL &&
        (i = (int)SendMessage(hwndT, WM_MOUSEACTIVATE, wParam, lParam))
          != 0)
        return((LONG)i);

    /* Moving, sizing or minimizing? Activate AFTER we take action. */
    if (LOWORD(lParam) == HTCAPTION)
        return((LONG)MA_NOACTIVATE);
    else
        return((LONG)MA_ACTIVATE);

      case WM_DRAWITEM:
          if (((LPDRAWITEMSTRUCT)lParam)->CtlType == ODT_LISTBOX)
              LBDefaultListboxDrawItem((LPDRAWITEMSTRUCT)lParam);
          break;

    }

  return(0L);
}


DEMO.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SELECT\DEMO.C

/****************************************************************************

    PROGRAM: Demo.c

    PURPOSE: Demonstrates how to manipulate a cursor and select a region

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  DemoInit() - initializes window data and registers window
  DemoWndProc() - processes messages
  About() - processes messages for "About" dialog box

    COMMENTS:
  This code is a modified version of the CURSOR.C program.  Instead of
  using inline code for drawing the shape, the routines from the Select
  library are called.

****************************************************************************/

#include "windows.h"
#include "demo.h"
#include "select.h"

HANDLE hInst;
BOOL bTrack = FALSE;
int OrgX = 0, OrgY = 0;
int PrevX = 0, PrevY = 0;
int X = 0, Y = 0;

RECT Rect;

int Shape = SL_BLOCK;             /* Shape to use for rectangle */
BOOL RetainShape = FALSE;           /* Retain or destroy shape    */

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{

    HWND hWnd;
    MSG msg;

    if (!hPrevInstance)
  if (!DemoInit(hInstance))
      return (NULL);

    hInst = hInstance;

    hWnd = CreateWindow("Demo",
  "Demo Sample Application",
  WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  NULL,
  NULL,
  hInstance,
  NULL);

    if (!hWnd)
  return (NULL);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, NULL, NULL)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
    }
    return (msg.wParam);
}

/****************************************************************************

    FUNCTION: DemoInit(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL DemoInit(hInstance)
HANDLE hInstance;
{
    HANDLE hMemory;
    PWNDCLASS pWndClass;
    BOOL bSuccess;

    hMemory = LocalAlloc(LPTR, sizeof(WNDCLASS));
    pWndClass = (PWNDCLASS) LocalLock(hMemory);
    pWndClass->hCursor = LoadCursor(NULL, IDC_ARROW);
    pWndClass->hIcon = LoadIcon(NULL, IDI_APPLICATION);
    pWndClass->lpszMenuName = (LPSTR) "Menu";
    pWndClass->lpszClassName = (LPSTR) "Demo";
    pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
    pWndClass->hInstance = hInstance;
    pWndClass->style = NULL;
    pWndClass->lpfnWndProc = DemoWndProc;

    bSuccess = RegisterClass(pWndClass);

    LocalUnlock(hMemory);
    LocalFree(hMemory);
    return (bSuccess);
}

/****************************************************************************

    FUNCTION: DemoWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_SYSCOMMAND - system menu (About dialog box)
  WM_CREATE     - create window
  WM_DESTROY    - destroy window
  WM_LBUTTONDOWN - left mouse button
  WM_MOUSEMOVE   - mouse movement
  WM_LBUTTONUP   - left button released

  WM_COMMAND messages:
      IDM_BOX    - use inverted box for selecting a region
      IDM_BLOCK  - use empty box for selecting a region
      IDM_RETAIN - retain/delete selection on button release

    COMMENTS:

  When the left mouse button is pressed, btrack is set to TRUE so that
  the code for WM_MOUSEMOVE will keep track of the mouse and update the
  box accordingly.  Once the button is released, btrack is set to
  FALSE, and the current position is saved.  Holding the SHIFT key
  while pressing the left button will extend the current box rather
  then erasing it and starting a new one.   The exception is when the
  retain shape option is enabled.   With this option, the rectangle is
  zeroed whenever the mouse is released so that it can not be erased or
  extended.

****************************************************************************/

long FAR PASCAL DemoWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;
    HMENU hMenu;

    switch (message) {

  case WM_COMMAND:
      switch (wParam) {
    case IDM_BOX:
        Shape = SL_BOX;
        hMenu = GetMenu(hWnd);
        CheckMenuItem(hMenu, IDM_BOX, MF_CHECKED);
        CheckMenuItem(hMenu, IDM_BLOCK, MF_UNCHECKED);
        break;

    case IDM_BLOCK:
        Shape = SL_BLOCK;
        hMenu = GetMenu(hWnd);
        CheckMenuItem(hMenu, IDM_BOX, MF_UNCHECKED);
        CheckMenuItem(hMenu, IDM_BLOCK, MF_CHECKED);
        break;

    case IDM_RETAIN:
        if (RetainShape) {
      hMenu = GetMenu(hWnd);
      CheckMenuItem(hMenu, IDM_RETAIN, MF_UNCHECKED);
      RetainShape = FALSE;
        }
        else {
      hMenu = GetMenu(hWnd);
      CheckMenuItem(hMenu, IDM_RETAIN, MF_CHECKED);
      RetainShape = TRUE;
        }
        break;

    case IDM_ABOUT:
        lpProcAbout = MakeProcInstance(About, hInst);
        DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
        FreeProcInstance(lpProcAbout);
        break;

      }
      break;

  case WM_LBUTTONDOWN:

      bTrack = TRUE;     /* user has pressed the left button */

      /* If you don't want the shape cleared, you must clear the Rect
       * coordinates before calling StartSelection
       */

      if (RetainShape)
    SetRectEmpty(&Rect);

      StartSelection(hWnd, MAKEPOINT(lParam), &Rect,
    (wParam & MK_SHIFT) ? SL_EXTEND | Shape : Shape);
      break;

  case WM_MOUSEMOVE:
      if (bTrack)
    UpdateSelection(hWnd, MAKEPOINT(lParam), &Rect, Shape);
      break;

  case WM_LBUTTONUP:
       if (bTrack)
         EndSelection(MAKEPOINT(lParam), &Rect);
      bTrack = FALSE;
      break;

   case WM_SIZE:
      switch (wParam) {
         case SIZEICONIC:

            /* If we aren't in retain mode we want to clear the
             * current rectangle now!
             */
            if (!RetainShape)
               SetRectEmpty(&Rect);
      }
      break;

  case WM_DESTROY:
      PostQuitMessage(NULL);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}

/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      return (TRUE);
    }
    return (FALSE);
}


DIB.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SHOWDIB\DIB.C

/****************************************************************************
 *                         *
 *  MODULE  : DIB.C                    *
 *                         *
 *  DESCRIPTION : Routines for dealing with Device Independent Bitmaps.
 *                         *
 *  FUNCTIONS  : OpenDIB()        - Opens DIB file and creates a memory DIB*
 *                         *
 *      WriteDIB()        - Writes a global handle in CF_DIB format*
 *          to a file.             *
 *                         *
 *      DibInfo()        - Retrieves the info. block associated   *
 *          with a CF_DIB format memory block.     *
 *                         *
 *      CreateBIPalette()   - Creates a GDI palette given a pointer  *
 *          to a BITMAPINFO structure.         *
 *                         *
 *      CreateDibPalette()  - Creates a GDI palette given a HANDLE   *
 *          to a BITMAPINFO structure.         *
 *                         *
 *      ReadDibBitmapInfo() - Reads a file in DIB format and returns *
 *          a global handle to it's BITMAPINFO     *
 *                         *
 *      PaletteSize()       - Calculates the palette size in bytes   *
 *          of given DIB             *
 *                         *
 *      DibNumColors()      - Determines the number of colors in DIB *
 *                         *
 *      BitmapFromDib()     - Creates a DDB given a global handle to *
 *          a block in CF_DIB format.         *
 *                         *
 *      DibFromBitmap()     - Creates a DIB repr. the DDB passed in. *
 *                         *
 *      DrawBitmap()        - Draws a bitmap at specified position   *
 *          in the DC.             *
 *                         *
 *      DibBlt()        - Draws a bitmap in CIF_DIB format using *
 *          SetDIBitsToDevice()           *
 *                         *
 *      StretchDibBlt()     - Draws a bitmap in CIF_DIB format using *
 *          StretchDIBits()            *
 *                         *
 *      lread()        - Private routine to read more than 64k  *
 *                         *
 *      lwrite()        - Private routine to write more than 64k *
 *                         *
 ****************************************************************************

#include <windows.h>
#include "showdib.h"
static   HCURSOR hcurSave;

/****************************************************************************
 *                      *
 *  FUNCTION   :OpenDIB(LPSTR szFile)              *
 *                      *
 *  PURPOSE    :Open a DIB file and create a MEMORY DIB, a memory handle    *
 *    containing BITMAPINFO, palette data and the bits.      *
 *                      *
 *  RETURNS    :A handle to the DIB.              *
 *                      *
 ****************************************************************************
HANDLE OpenDIB (szFile)
LPSTR szFile;
{
    unsigned    fh;
    BITMAPINFOHEADER  bi;
    LPBITMAPINFOHEADER  lpbi;
    DWORD    dwLen = 0;
    DWORD    dwBits;
    HANDLE    hdib;
    HANDLE              h;
    OFSTRUCT    of;

    /* Open the file and read the DIB information */
    fh = OpenFile(szFile, &of, OF_READ);
    if (fh == -1)
  return NULL;

    hdib = ReadDibBitmapInfo(fh);
    if (!hdib)
  return NULL;
    DibInfo(hdib,&bi);

    /* Calculate the memory needed to hold the DIB */
    dwBits = bi.biSizeImage;
    dwLen  = bi.biSize + (DWORD)PaletteSize (&bi) + dwBits;

    /* Try to increase the size of the bitmap info. buffer to hold the DIB */
    h = GlobalReAlloc(hdib, dwLen, GHND);
    if (!h){
  GlobalFree(hdib);
  hdib = NULL;
    }
    else
  hdib = h;

    /* Read in the bits */
    if (hdib){

        lpbi = (VOID FAR *)GlobalLock(hdib);
  lread(fh, (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize(lpbi), dwBits);
  GlobalUnlock(hdib);
    }
    _lclose(fh);

    return hdib;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : WriteDIB(LPSTR szFile,HANDLE hdib)          *
 *                      *
 *  PURPOSE    : Write a global handle in CF_DIB format to a file.      *
 *                      *
 *  RETURNS    : TRUE  - if successful.             *
 *     FALSE - otherwise              *
 *                      *
 ****************************************************************************
BOOL WriteDIB (szFile, hdib)
LPSTR szFile;
HANDLE hdib;
{
    BITMAPFILEHEADER  hdr;
    LPBITMAPINFOHEADER  lpbi;
    int                 fh;
    OFSTRUCT            of;

    if (!hdib)
  return FALSE;

    fh = OpenFile (szFile, &of, OF_CREATE|OF_READWRITE);
    if (fh == -1)
  return FALSE;

    lpbi = (VOID FAR *)GlobalLock (hdib);

    /* Fill in the fields of the file header */
    hdr.bfType    = BFT_BITMAP;
    hdr.bfSize    = GlobalSize (hdib) + sizeof (BITMAPFILEHEADER);
    hdr.bfReserved1     = 0;
    hdr.bfReserved2     = 0;
    hdr.bfOffBits       = (DWORD)sizeof(BITMAPFILEHEADER) + lpbi->biSize +
                          PaletteSize(lpbi);

    /* Write the file header */
    _lwrite (fh, (LPSTR)&hdr, sizeof (BITMAPFILEHEADER));

    /* Write the DIB header and the bits */
    lwrite (fh, (LPSTR)lpbi, GlobalSize (hdib));

    GlobalUnlock (hdib);
    _lclose (fh);
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : DibInfo(HANDLE hbi,LPBITMAPINFOHEADER lpbi)        *
 *                      *
 *  PURPOSE    : Retrieves the DIB info associated with a CF_DIB      *
 *     format memory block.              *
 *                      *
 *  RETURNS    : TRUE  - if successful.             *
 *     FALSE - otherwise              *
 *                      *
 ****************************************************************************
BOOL DibInfo (hbi, lpbi)
HANDLE hbi;
LPBITMAPINFOHEADER lpbi;
{
    if (hbi){
  *lpbi = *(LPBITMAPINFOHEADER)GlobalLock (hbi);

  /* fill in the default fields */
  if (lpbi->biSize != sizeof (BITMAPCOREHEADER)){
            if (lpbi->biSizeImage == 0L)
    lpbi->biSizeImage =
        WIDTHBYTES(lpbi->biWidth*lpbi->biBitCount) * lpbi->biHeight;

            if (lpbi->biClrUsed == 0L)
    lpbi->biClrUsed = DibNumColors (lpbi);
        }
  GlobalUnlock (hbi);
  return TRUE;
    }
    return FALSE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : CreateBIPalette(LPBITMAPINFOHEADER lpbi)        *
 *                      *
 *  PURPOSE    : Given a Pointer to a BITMAPINFO struct will create a      *
 *     a GDI palette object from the color table.        *
 *                      *
 *  RETURNS    : A handle to the palette.            *
 *                      *
 ****************************************************************************
HPALETTE CreateBIPalette (lpbi)
LPBITMAPINFOHEADER lpbi;
{
    LOGPALETTE          *pPal;
    HPALETTE            hpal = NULL;
    WORD                nNumColors;
    BYTE                red;
    BYTE                green;
    BYTE                blue;
    int                 i;
    RGBQUAD        FAR *pRgb;

    if (!lpbi)
  return NULL;

    if (lpbi->biSize != sizeof(BITMAPINFOHEADER))
  return NULL;

    /* Get a pointer to the color table and the number of colors in it */
    pRgb = (RGBQUAD FAR *)((LPSTR)lpbi + (WORD)lpbi->biSize);
    nNumColors = DibNumColors(lpbi);

    if (nNumColors){
  /* Allocate for the logical palette structure */
        pPal = (LOGPALETTE*)LocalAlloc(LPTR,sizeof(LOGPALETTE) + nNumColors *
  if (!pPal)
      return NULL;

        pPal->palNumEntries = nNumColors;
  pPal->palVersion    = PALVERSION;

  /* Fill in the palette entries from the DIB color table and
   * create a logical color palette.
   */
  for (i = 0; i < nNumColors; i++){
            pPal->palPalEntry[i].peRed   = pRgb[i].rgbRed;
            pPal->palPalEntry[i].peGreen = pRgb[i].rgbGreen;
            pPal->palPalEntry[i].peBlue  = pRgb[i].rgbBlue;
            pPal->palPalEntry[i].peFlags = (BYTE)0;
        }
        hpal = CreatePalette(pPal);
        LocalFree((HANDLE)pPal);
    }
    else if (lpbi->biBitCount == 24){
  /* A 24 bitcount DIB has no color table entries so, set the number of
   * to the maximum value (256).
   */
  nNumColors = MAXPALETTE;
        pPal = (LOGPALETTE*)LocalAlloc(LPTR,sizeof(LOGPALETTE) + nNumColors *
        if (!pPal)
      return NULL;

  pPal->palNumEntries = nNumColors;
  pPal->palVersion    = PALVERSION;

  red = green = blue = 0;

  /* Generate 256 (= 8*8*4) RGB combinations to fill the palette
   * entries.
   */
  for (i = 0; i < pPal->palNumEntries; i++){
            pPal->palPalEntry[i].peRed   = red;
            pPal->palPalEntry[i].peGreen = green;
            pPal->palPalEntry[i].peBlue  = blue;
            pPal->palPalEntry[i].peFlags = (BYTE)0;

      if (!(red += 32))
    if (!(green += 32))
        blue += 64;
        }
        hpal = CreatePalette(pPal);
        LocalFree((HANDLE)pPal);
    }
    return hpal;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : CreateDibPalette(HANDLE hbi)            *
 *                      *
 *  PURPOSE    : Given a Global HANDLE to a BITMAPINFO Struct        *
 *     will create a GDI palette object from the color table.     *
 *     (BITMAPINFOHEADER format DIBs only)           *
 *                      *
 *  RETURNS    : A handle to the palette.            *
 *                      *
 ****************************************************************************
HPALETTE CreateDibPalette (hbi)
HANDLE hbi;
{
    HPALETTE hpal;

    if (!hbi)
  return NULL;
    hpal = CreateBIPalette((LPBITMAPINFOHEADER)GlobalLock(hbi));
    GlobalUnlock(hbi);
    return hpal;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : ReadDibBitmapInfo(int fh)            *
 *                      *
 *  PURPOSE    : Will read a file in DIB format and return a global HANDLE  *
 *     to it's BITMAPINFO.  This function will work with both     *
 *     "old" (BITMAPCOREHEADER) and "new" (BITMAPINFOHEADER)      *
 *     bitmap formats, but will always return a "new" BITMAPINFO  *
 *                      *
 *  RETURNS    : A handle to the BITMAPINFO of the DIB in the file.      *
 *                      *
 ****************************************************************************
HANDLE ReadDibBitmapInfo (fh)
int fh;
{
    DWORD     off;
    HANDLE    hbi = NULL;
    int       size;
    int       i;
    WORD      nNumColors;

    RGBQUAD FAR       *pRgb;
    BITMAPINFOHEADER   bi;
    BITMAPCOREHEADER   bc;
    LPBITMAPINFOHEADER lpbi;
    BITMAPFILEHEADER   bf;
    DWORD         dwWidth = 0;
    DWORD         dwHeight = 0;
    WORD         wPlanes, wBitCount;

    if (fh == -1)
        return NULL;

    /* Reset file pointer and read file header */
    off = _llseek(fh, 0L, SEEK_CUR);
    if (sizeof (bf) != _lread (fh, (LPSTR)&bf, sizeof (bf)))
        return FALSE;

    /* Do we have a RC HEADER? */
    if (!ISDIB (bf.bfType)) {
        bf.bfOffBits = 0L;
  _llseek (fh, off, SEEK_SET);
    }
    if (sizeof (bi) != _lread (fh, (LPSTR)&bi, sizeof(bi)))
        return FALSE;

    nNumColors = DibNumColors (&bi);

    /* Check the nature (BITMAPINFO or BITMAPCORE) of the info. block
     * and extract the field information accordingly. If a BITMAPCOREHEADER,
     * transfer it's field information to a BITMAPINFOHEADER-style block
     */
    switch (size = (int)bi.biSize){
  case sizeof (BITMAPINFOHEADER):
            break;

  case sizeof (BITMAPCOREHEADER):

      bc = *(BITMAPCOREHEADER*)&bi;

      dwWidth   = (DWORD)bc.bcWidth;
      dwHeight  = (DWORD)bc.bcHeight;
      wPlanes   = bc.bcPlanes;
      wBitCount = bc.bcBitCount;

            bi.biSize               = sizeof(BITMAPINFOHEADER);
      bi.biWidth        = dwWidth;
      bi.biHeight       = dwHeight;
      bi.biPlanes       = wPlanes;
      bi.biBitCount      = wBitCount;

            bi.biCompression        = BI_RGB;
            bi.biSizeImage          = 0;
            bi.biXPelsPerMeter      = 0;
            bi.biYPelsPerMeter      = 0;
            bi.biClrUsed            = nNumColors;
            bi.biClrImportant       = nNumColors;

      _llseek (fh, (LONG)sizeof (BITMAPCOREHEADER) - sizeof (BITMAPINFOHEADER
            break;

  default:
      /* Not a DIB! */
      return NULL;
    }

    /*  Fill in some default values if they are zero */
    if (bi.biSizeImage == 0){
  bi.biSizeImage = WIDTHBYTES ((DWORD)bi.biWidth * bi.biBitCount)
       * bi.biHeight;
    }
    if (bi.biClrUsed == 0)
  bi.biClrUsed = DibNumColors(&bi);

    /* Allocate for the BITMAPINFO structure and the color table. */
    hbi = GlobalAlloc (GHND, (LONG)bi.biSize + nNumColors * sizeof(RGBQUAD));
    if (!hbi)
        return NULL;
    lpbi = (VOID FAR *)GlobalLock (hbi);
    *lpbi = bi;

    /* Get a pointer to the color table */
    pRgb = (RGBQUAD FAR *)((LPSTR)lpbi + bi.biSize);
    if (nNumColors){
  if (size == sizeof(BITMAPCOREHEADER)){
      /* Convert a old color table (3 byte RGBTRIPLEs) to a new
       * color table (4 byte RGBQUADs)
             */
      _lread (fh, (LPSTR)pRgb, nNumColors * sizeof(RGBTRIPLE));

      for (i = nNumColors - 1; i >= 0; i--){
                RGBQUAD rgb;

                rgb.rgbRed      = ((RGBTRIPLE FAR *)pRgb)[i].rgbtRed;
                rgb.rgbBlue     = ((RGBTRIPLE FAR *)pRgb)[i].rgbtBlue;
                rgb.rgbGreen    = ((RGBTRIPLE FAR *)pRgb)[i].rgbtGreen;
                rgb.rgbReserved = (BYTE)0;

                pRgb[i] = rgb;
            }
        }
  else
            _lread(fh,(LPSTR)pRgb,nNumColors * sizeof(RGBQUAD));
    }

    if (bf.bfOffBits != 0L)
        _llseek(fh,off + bf.bfOffBits,SEEK_SET);

    GlobalUnlock(hbi);
    return hbi;
}

/****************************************************************************
 *                      *
 *  FUNCTION   :  PaletteSize(VOID FAR * pv)            *
 *                      *
 *  PURPOSE    :  Calculates the palette size in bytes. If the info. block  *
 *      is of the BITMAPCOREHEADER type, the number of colors is  *
 *      multiplied by 3 to give the palette size, otherwise the   *
 *      number of colors is multiplied by 4.                *
 *                      *
 *  RETURNS    :  Palette size in number of bytes.          *
 *                      *
 ****************************************************************************
WORD PaletteSize (pv)
VOID FAR * pv;
{
    LPBITMAPINFOHEADER lpbi;
    WORD         NumColors;

    lpbi      = (LPBITMAPINFOHEADER)pv;
    NumColors = DibNumColors(lpbi);

    if (lpbi->biSize == sizeof(BITMAPCOREHEADER))
        return NumColors * sizeof(RGBTRIPLE);
    else
        return NumColors * sizeof(RGBQUAD);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : DibNumColors(VOID FAR * pv)            *
 *                      *
 *  PURPOSE    : Determines the number of colors in the DIB by looking at   *
 *     the BitCount filed in the info block.          *
 *                      *
 *  RETURNS    : The number of colors in the DIB.          *
 *                      *
 ****************************************************************************
WORD DibNumColors (pv)
VOID FAR * pv;
{
    int     bits;
    LPBITMAPINFOHEADER  lpbi;
    LPBITMAPCOREHEADER  lpbc;

    lpbi = ((LPBITMAPINFOHEADER)pv);
    lpbc = ((LPBITMAPCOREHEADER)pv);

    /*  With the BITMAPINFO format headers, the size of the palette
     *  is in biClrUsed, whereas in the BITMAPCORE - style headers, it
     *  is dependent on the bits per pixel ( = 2 raised to the power of
     *  bits/pixel).
     */
    if (lpbi->biSize != sizeof(BITMAPCOREHEADER)){
        if (lpbi->biClrUsed != 0)
            return (WORD)lpbi->biClrUsed;
        bits = lpbi->biBitCount;
    }
    else
        bits = lpbc->bcBitCount;

    switch (bits){
  case 1:
    return 2;
  case 4:
    return 16;
  case 8:
    return 256;
  default:
    /* A 24 bitcount DIB has no color table */
    return 0;
    }
}
/****************************************************************************
 *                      *
 *  FUNCTION   : DibFromBitmap()              *
 *                      *
 *  PURPOSE    : Will create a global memory block in DIB format that      *
 *     represents the Device-dependent bitmap (DDB) passed in.    *
 *                      *
 *  RETURNS    : A handle to the DIB              *
 *                      *
 ****************************************************************************
HANDLE DibFromBitmap (hbm, biStyle, biBits, hpal)
HBITMAP      hbm;
DWORD       biStyle;
WORD       biBits;
HPALETTE     hpal;
{
    BITMAP               bm;
    BITMAPINFOHEADER     bi;
    BITMAPINFOHEADER FAR *lpbi;
    DWORD                dwLen;
    HANDLE               hdib;
    HANDLE               h;
    HDC                  hdc;

    if (!hbm)
  return NULL;

    if (hpal == NULL)
        hpal = GetStockObject(DEFAULT_PALETTE);

    GetObject(hbm,sizeof(bm),(LPSTR)&bm);

    if (biBits == 0)
  biBits =  bm.bmPlanes * bm.bmBitsPixel;

    bi.biSize               = sizeof(BITMAPINFOHEADER);
    bi.biWidth              = bm.bmWidth;
    bi.biHeight             = bm.bmHeight;
    bi.biPlanes             = 1;
    bi.biBitCount           = biBits;
    bi.biCompression        = biStyle;
    bi.biSizeImage          = 0;
    bi.biXPelsPerMeter      = 0;
    bi.biYPelsPerMeter      = 0;
    bi.biClrUsed            = 0;
    bi.biClrImportant       = 0;

    dwLen  = bi.biSize + PaletteSize(&bi);

    hdc = GetDC(NULL);
    hpal = SelectPalette(hdc,hpal,FALSE);
   RealizePalette(hdc);

    hdib = GlobalAlloc(GHND,dwLen);

    if (!hdib){
  SelectPalette(hdc,hpal,FALSE);
  ReleaseDC(NULL,hdc);
  return NULL;
    }

    lpbi = (VOID FAR *)GlobalLock(hdib);

    *lpbi = bi;

    /*  call GetDIBits with a NULL lpBits param, so it will calculate the
     *  biSizeImage field for us
     */
    GetDIBits(hdc, hbm, 0, (WORD)bi.biHeight,
        NULL, (LPBITMAPINFO)lpbi, DIB_RGB_COLORS);

    bi = *lpbi;
    GlobalUnlock(hdib);

    /* If the driver did not fill in the biSizeImage field, make one up */
    if (bi.biSizeImage == 0){
        bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight

        if (biStyle != BI_RGB)
            bi.biSizeImage = (bi.biSizeImage * 3) / 2;
    }

    /*  realloc the buffer big enough to hold all the bits */
    dwLen = bi.biSize + PaletteSize(&bi) + bi.biSizeImage;
    if (h = GlobalReAlloc(hdib,dwLen,0))
        hdib = h;
    else{
        GlobalFree(hdib);
  hdib = NULL;

  SelectPalette(hdc,hpal,FALSE);
  ReleaseDC(NULL,hdc);
  return hdib;
    }

    /*  call GetDIBits with a NON-NULL lpBits param, and actualy get the
     *  bits this time
     */
    lpbi = (VOID FAR *)GlobalLock(hdib);

    if (GetDIBits( hdc,
       hbm,
       0,
       (WORD)bi.biHeight,
       (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize(lpbi),
       (LPBITMAPINFO)lpbi, DIB_RGB_COLORS) == 0){
   GlobalUnlock(hdib);
   hdib = NULL;
   SelectPalette(hdc,hpal,FALSE);
   ReleaseDC(NULL,hdc);
   return NULL;
    }

    bi = *lpbi;
    GlobalUnlock(hdib);

    SelectPalette(hdc,hpal,FALSE);
    ReleaseDC(NULL,hdc);
    return hdib;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : BitmapFromDib(HANDLE hdib, HPALETTE hpal)        *
 *                      *
 *  PURPOSE    : Will create a DDB (Device Dependent Bitmap) given a global *
 *     handle to a memory block in CF_DIB format        *
 *                      *
 *  RETURNS    : A handle to the DDB.              *
 *                      *
 ****************************************************************************
HBITMAP BitmapFromDib (hdib, hpal)
HANDLE     hdib;
HPALETTE   hpal;
{
    LPBITMAPINFOHEADER  lpbi;
    HPALETTE    hpalT;
    HDC     hdc;
    HBITMAP    hbm;

    StartWait();

    if (!hdib)
  return NULL;

    lpbi = (VOID FAR *)GlobalLock(hdib);

    if (!lpbi)
  return NULL;

    hdc = GetDC(NULL);

    if (hpal){
        hpalT = SelectPalette(hdc,hpal,FALSE);
        RealizePalette(hdc);     // GDI Bug...????
    }

    hbm = CreateDIBitmap(hdc,
                (LPBITMAPINFOHEADER)lpbi,
                (LONG)CBM_INIT,
                (LPSTR)lpbi + lpbi->biSize + PaletteSize(lpbi),
                (LPBITMAPINFO)lpbi,
                DIB_RGB_COLORS );

    if (hpal)
        SelectPalette(hdc,hpalT,FALSE);

    ReleaseDC(NULL,hdc);
    GlobalUnlock(hdib);

    EndWait();

    return hbm;
}
/****************************************************************************
 *                      *
 *  FUNCTION   : DrawBitmap(HDC hdc, int x, int y, HBITMAP hbm, DWORD rop)  *
 *                      *
 *  PURPOSE    : Draws bitmap <hbm> at the specifed position in DC <hdc>    *
 *                      *
 *  RETURNS    : Return value of BitBlt()            *
 *                      *
 ****************************************************************************
BOOL DrawBitmap (hdc, x, y, hbm, rop)
HDC     hdc;
int     x, y;
HBITMAP    hbm;
DWORD     rop;
{
    HDC       hdcBits;
    BITMAP    bm;
    HPALETTE  hpalT;
    BOOL      f;

    if (!hdc || !hbm)
        return FALSE;

    hdcBits = CreateCompatibleDC(hdc);
    GetObject(hbm,sizeof(BITMAP),(LPSTR)&bm);
    SelectObject(hdcBits,hbm);
    f = BitBlt(hdc,0,0,bm.bmWidth,bm.bmHeight,hdcBits,0,0,rop);
    DeleteDC(hdcBits);

    return f;
}
/****************************************************************************
 *                      *
 *  FUNCTION   : DibBlt( HDC hdc,              *
 *       int x0, int y0,            *
 *       int dx, int dy,            *
 *       HANDLE hdib,              *
 *       int x1, int y1,            *
 *       LONG rop)              *
 *                      *
 *  PURPOSE    : Draws a bitmap in CF_DIB format, using SetDIBits to device.*
 *     taking the same parameters as BitBlt().        *
 *                      *
 *  RETURNS    : TRUE  - if function succeeds.            *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************
BOOL DibBlt (hdc, x0, y0, dx, dy, hdib, x1, y1, rop)
HDC     hdc;
int     x0, y0, dx, dy;
HANDLE     hdib;
int     x1, y1;
LONG     rop;
{
    LPBITMAPINFOHEADER   lpbi;
    HPALETTE     hpal,hpalT;
    LPSTR     pBuf;
    HDC      hdcMem;
    HBITMAP     hbm,hbmT;

    if (!hdib)
  return PatBlt(hdc,x0,y0,dx,dy,rop);

    lpbi = (VOID FAR *)GlobalLock(hdib);

    if (!lpbi)
  return FALSE;

    pBuf = (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize(lpbi);
    SetDIBitsToDevice (hdc, x0, y0, dx, dy,
           x1,y1,
           x1,
           dy,
           pBuf, (LPBITMAPINFO)lpbi,
           DIB_RGB_COLORS );

    GlobalUnlock(hdib);
    return TRUE;
}
/****************************************************************************
 *                      *
 *  FUNCTION   : StretchDibBlt( HDC hdc,            *
 *        int x, int y,            *
 *        int dx, int dy,           *
 *        HANDLE hdib,            *
 *        int x0, int y0,           *
 *        int dx0, int dy0,          *
 *        LONG rop)            *
 *                      *
 *  PURPOSE    : Draws a bitmap in CF_DIB format, using StretchDIBits()     *
 *     taking the same parameters as StretchBlt().        *
 *                      *
 *  RETURNS    : TRUE  - if function succeeds.            *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************
BOOL StretchDibBlt (hdc, x, y, dx, dy, hdib, x0, y0, dx0, dy0, rop)
HDC hdc;
int x, y;
int dx, dy;
HANDLE hdib;
int x0, y0;
int dx0, dy0;
LONG rop;

{
    LPBITMAPINFOHEADER lpbi;
    LPSTR        pBuf;
    BOOL         f;

    if (!hdib)
        return PatBlt(hdc,x,y,dx,dy,rop);

    lpbi = (VOID FAR *)GlobalLock(hdib);

    if (!lpbi)
        return FALSE;

    pBuf = (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize(lpbi);

    f = StretchDIBits ( hdc,
      x, y,
      dx, dy,
      x0, y0,
      dx0, dy0,
      pBuf, (LPBITMAPINFO)lpbi,
      DIB_RGB_COLORS,
      rop);

    GlobalUnlock(hdib);
    return f;
}

 /************* PRIVATE ROUTINES TO READ/WRITE MORE THAN 64K ***************/
/****************************************************************************
 *                      *
 *  FUNCTION   : lread(int fh, VOID FAR *pv, DWORD ul)          *
 *                      *
 *  PURPOSE    : Reads data in steps of 32k till all the data has been read.*
 *                      *
 *  RETURNS    : 0 - If read did not proceed correctly.         *
 *     number of bytes read otherwise.          *
 *                      *
 ****************************************************************************
DWORD PASCAL lread (fh, pv, ul)
int        fh;
VOID far      *pv;
DWORD        ul;
{
    DWORD     ulT = ul;
    BYTE huge *hp = pv;

    while (ul > (DWORD)MAXREAD) {
  if (_lread(fh, (LPSTR)hp, (WORD)MAXREAD) != MAXREAD)
    return 0;
  ul -= MAXREAD;
  hp += MAXREAD;
    }
    if (_lread(fh, (LPSTR)hp, (WORD)ul) != (WORD)ul)
  return 0;
    return ulT;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : lwrite(int fh, VOID FAR *pv, DWORD ul)         *
 *                      *
 *  PURPOSE    : Writes data in steps of 32k till all the data is written.  *
 *                      *
 *  RETURNS    : 0 - If write did not proceed correctly.        *
 *     number of bytes written otherwise.          *
 *                      *
 ****************************************************************************
DWORD PASCAL lwrite (fh, pv, ul)
int       fh;
VOID FAR     *pv;
DWORD       ul;
{
    DWORD     ulT = ul;
    BYTE huge *hp = pv;

    while (ul > MAXREAD) {
  if (_lwrite(fh, (LPSTR)hp, (WORD)MAXREAD) != MAXREAD)
    return 0;
  ul -= MAXREAD;
  hp += MAXREAD;
    }
    if (_lwrite(fh, (LPSTR)hp, (WORD)ul) != (WORD)ul)
  return 0;
    return ulT;
}


DLGOPEN.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SHOWDIB\DLGOPEN.C

/****************************************************************************
 *                         *
 *  MODULE  : DLGOPEN.C                   *
 *                         *
 *  DESCRIPTION : Routines to display a standard File/Open and File/Save
 *      dialog boxes.                  *
 *                         *
 *  FUNCTIONS  : DlgOpenFile() - Displays a dialog box for opening or saving
 *          file.                *
 *                         *
 *      DlgfnOpen()  - Dialog function for the above dialog.        *
 *                         *
 *      AddExt()  - Adds an extension to a filename if not       *
 *          already present.             *
 *                         *
 *      FSearchSpec() - Checks if given string contains a wildcard   *
 *          character.               *
 *                         *
 *      FillListBox() - Fills listbox with files that match specs.   *
 *                         *
 *      DlgCheckOkEnable() - Enables <OK> button iff there's text in *
 *               the edit control.           *
 *                         *
 *      NOTE : These routines require that the app. be running       *
 *       SS = DS since they use near pointers into the stack.  *
 *                         *
 ****************************************************************************
#include <windows.h>
#include "showdib.h"

static PSTR         szExt;
static PSTR         szFileName;
static PSTR         szTitle;
static DWORD        flags;
static WORD         fOpt;

/* Forward declarations of helper functions */

static void  NEAR DlgCheckOkEnable(HWND hwnd, int idEdit, unsigned message);
static char *NEAR FillListBox (HWND,char*);
static BOOL  NEAR FSearchSpec (char*);
static void  NEAR AddExt (char*,char*);
extern PASCAL chdir(LPSTR);     /* in dlgopena.asm */

#define DLGOPEN_UNUSED   0

/* Mask to eliminate bogus style and bitcount combinations ...
 * RLE formats, if chosen should be matched with the bitcounts:
 *   RLE4 scheme should be used only for 4 bitcount DIBs.
 *   RLE8   "      "   "   "   "    "  8   "       "
 *
 * BITCOUNTMASK is indexed by DLGOPEN_RLE4 >> 4, DLGOPEN_RLE8 >> 4
 * and DLGOPEN_RLE8 >> 4
 */

static WORD BITCOUNTMASK[] = { DLGOPEN_UNUSED,
             DLGOPEN_1BPP | DLGOPEN_8BPP | DLGOPEN_24BPP,
             DLGOPEN_1BPP | DLGOPEN_4BPP | DLGOPEN_24BPP,
             DLGOPEN_UNUSED,
             0 };

/****************************************************************************
 *                         *
 *  FUNCTION   :DlgOpen(LPSTR szFile)                 *
 *                         *
 *  PURPOSE    :Display dialog box for opening files. Allow user to interact
 *    with dialogbox, change directories as necessary, and try to    *
 *    open file if user selects one. Automatically append         *
 *    extension to filename if necessary.             *
 *    This routine correctly parses filenames containing KANJI       *
 *    characters.                   *
 *                         *
 *  RETURNS    :  - Handle to the opened file if legal filename.         *
 *      - 0 if user presses <cancel>               *
 *      - 1 if filename entered is illegal             *
 *                         *
 ****************************************************************************
int FAR PASCAL DlgOpenFile (hwndParent, szTitleIn, flagsIn, szExtIn,
          szFileNameIn, pfOpt)

HWND    hwndParent;
char    *szTitleIn;
DWORD    flagsIn;
char    *szExtIn;
char    *szFileNameIn;
WORD    *pfOpt;
{
    int      fh;
    FARPROC  lpProc;
    char     achFile[128];
    char     achExt[128];
    HANDLE   hInstance;
    WORD     w;

    if (pfOpt == NULL)
        pfOpt = &w;

    flags    = flagsIn;
    fOpt     = *pfOpt;

    lstrcpy (szFileName = achFile, szFileNameIn);
    lstrcpy (szExt = achExt, szExtIn);
    szTitle = szTitleIn;

    hInstance = GetWindowWord (hwndParent, GWW_HINSTANCE);

    /* Show the dialog box */
    lpProc = MakeProcInstance ((FARPROC)DlgfnOpen, hInstance);
    fh = DialogBox (hInstance, "DlgOpenBox", hwndParent, lpProc);
    FreeProcInstance (lpProc);

    if (fh != 0){
  lstrcpy (szFileNameIn, szFileName);
        *pfOpt = fOpt;
    }
    return fh;
}

/****************************************************************************
 *                      *
 *  FUNCTION   :DlgfnOpen (hwnd, msg, wParam, lParam)          *
 *                      *
 *  PURPOSE    :Dialog function for File/Open dialog.          *
 *                      *
 ****************************************************************************
int far PASCAL DlgfnOpen (hwnd, msg, wParam, lParam)

HWND hwnd;
unsigned msg;
WORD wParam;
LONG lParam;
{
    int      result = -1;    /* Assume illegal filename initially */
    WORD     w;
    char     c;
    WORD     f;
    OFSTRUCT of;
    RECT     rc, rcCtl;
    HWND     hwndT;
    BOOL     fEnable;

    switch (msg) {
  case WM_INITDIALOG:
      if (szTitle && *szTitle)
    SetWindowText (hwnd, szTitle);

      /* Set text on <OK> button according to mode (File/Open or File/Save) *
      if (flags & OF_SAVE)
    SetDlgItemText (hwnd, IDOK, "&Save");
      if (flags & OF_OPEN)
    SetDlgItemText (hwnd, IDOK, "&Open");

      if ((flags & OF_NOOPTIONS) &&
    (hwndT = GetDlgItem(hwnd,DLGOPEN_FOLDOUT)))
    EnableWindow (hwndT,FALSE);

      if (hwndT = GetDlgItem (hwnd, DLGOPEN_SMALL)) {
    GetWindowRect (hwnd,&rc);
    GetWindowRect (GetDlgItem(hwnd,DLGOPEN_SMALL),&rcCtl);

    SetWindowPos (hwnd,
            NULL,
            0,
            0,
            rcCtl.left - rc.left,
            rc.bottom - rc.top,
            SWP_NOZORDER | SWP_NOMOVE);
      }
      /* fill list box with filenames that match specifications, and
       * fill static field with path name.
       */
      FillListBox(hwnd,szExt);

      /* If in Save mode, set the edit control with default (current)
       * file name,and select the corresponding entry in the listbox.
       */
      if ((flags & OF_SAVE) && *szFileName) {
    SetDlgItemText (hwnd, DLGOPEN_EDIT, szFileName);
    SendDlgItemMessage (hwnd,
            DLGOPEN_FILE_LISTBOX,
            LB_SELECTSTRING,
            0,
            (LONG)(LPSTR)szFileName);
      }
      else {
    /*  Set the edit field with the default extensions... */
    if (flags & OF_NOSHOWSPEC)
        SetDlgItemText (hwnd, DLGOPEN_EDIT, "");
    else
        SetDlgItemText (hwnd, DLGOPEN_EDIT, szExt);
      }
      /*  ...and select all text in the edit field */
      SendDlgItemMessage (hwnd, DLGOPEN_EDIT, EM_SETSEL, 0, 0x7FFF0000L);

      /*  check all options that are set */
      for ( f = DLGOPEN_1BPP; f; f<<=1)
    CheckDlgButton(hwnd, FID(f), fOpt & f);

      break;

  case WM_COMMAND:
      w = wParam;
      switch (w) {
    case IDOK:
        if (IsWindowEnabled (GetDlgItem(hwnd, IDOK))) {
      /* Get contents of edit field and add search spec. if it
       * does not contain one.
       */
      GetDlgItemText (hwnd, DLGOPEN_EDIT, (LPSTR)szFileName, 128);

      w = lstrlen(szFileName)-1;
      c = szFileName[w];
      switch (c) {
          case '\\':
          case '/':
        szFileName[w] = 0;
        break;
      }
      if (chdir ((LPSTR)szFileName))
          lstrcpy (szFileName,szExt);

      /*  Try to open path.  If successful, fill listbox with
       *  contents of new directory.  Otherwise, open datafile.
       */
      if (FSearchSpec(szFileName)) {
          lstrcpy (szExt, FillListBox (hwnd, szFileName));
          if (flags & OF_NOSHOWSPEC)
        SetDlgItemText (hwnd, DLGOPEN_EDIT, "");
          else
        SetDlgItemText (hwnd, DLGOPEN_EDIT, szExt);
          break;
      }

      /*  Make filename upper case and if it's a legal DOS
       *  name, try to open the file.
       */
      AnsiUpper(szFileName);
      AddExt(szFileName,szExt);
      result = OpenFile(szFileName, &of, (WORD)flags);

      if (result != -1) {
          lstrcpy(szFileName,of.szPathName);
      }
      else if (flags & OF_MUSTEXIST) {
          MessageBeep(0);
          return 0L;
      }

      /*  Get the state of all checked options */
      for (f = DLGOPEN_1BPP; f; f <<= 1){
          if (IsDlgButtonChecked (hwnd, FID (f)))
        fOpt |= f;
          else
        fOpt &= ~f;
      }

      EndDialog (hwnd, result);
        }
        break;

    case DLGOPEN_OPTION + DLGOPEN_RLE4:
    case DLGOPEN_OPTION + DLGOPEN_RLE8:
    case DLGOPEN_OPTION + DLGOPEN_RGB:
        /* Mask out incompatible bitcount options and gray the
         * appropriate radiobuttons.
         */
        for (f = DLGOPEN_1BPP; f <= DLGOPEN_24BPP; f <<= 1){
      fEnable = !(f & BITCOUNTMASK [IDF(w) >> 4 ]);
      EnableWindow (GetDlgItem (hwnd, FID(f)), fEnable);

      /* If the radiobutton is being grayed, uncheck it and
       * and check an "allowed" option so the bitcount group
       * is still accessible via the keyboard
       */
      if (!fEnable && IsDlgButtonChecked (hwnd, FID (f))){
          CheckDlgButton(hwnd, FID(f), FALSE);
          CheckDlgButton(hwnd, FID(IDF(w) >> 3), TRUE);
      }
        }
        break;

    case IDCANCEL:
        /* User pressed cancel.  Just take down dialog box. */
        EndDialog (hwnd, 0);
        break;

    /*  User single clicked or doubled clicked in listbox -
     *  Single click means fill edit box with selection.
     *  Double click means go ahead and open the selection.
     */
    case DLGOPEN_FILE_LISTBOX:
    case DLGOPEN_DIR_LISTBOX:

        switch (HIWORD(lParam)) {
      /* Single click case */
      case 1:
          /* Get selection, which may be either a prefix to a
           * new search path or a filename. DlgDirSelect parses
           * selection, and appends a backslash if selection
           * is a prefix
           */
          DlgDirSelect(hwnd,szFileName,wParam);
          w = lstrlen(szFileName)-1;
          c = szFileName[w];
          switch (c) {
        case ':':
            lstrcat (szFileName,".");
            break;
        case '\\':
        case '/':
            szFileName[w] = 0;
            break;
          }
          SetDlgItemText(hwnd, DLGOPEN_EDIT, szFileName);
          break;
      /* Double click case - first click has already been
       * processed as single click
       */
      case 2:
          PostMessage (hwnd,WM_COMMAND,IDOK,0L);
          break;
        }
        break;

    case DLGOPEN_EDIT:
        DlgCheckOkEnable(hwnd, DLGOPEN_EDIT, HIWORD(lParam));
        break;

    case DLGOPEN_FOLDOUT:
        GetWindowRect(hwnd,&rc);
        GetWindowRect(GetDlgItem(hwnd,DLGOPEN_BIG),&rcCtl);

        if ((rcCtl.left <= rc.right) && (rcCtl.top <= rc.bottom))
       GetWindowRect (GetDlgItem (hwnd, DLGOPEN_SMALL), &rcCtl);

        SetWindowPos (hwnd,
          NULL,
          0,
          0,
          rcCtl.left - rc.left,
          rc.bottom - rc.top,
          SWP_NOZORDER | SWP_NOMOVE);
        break;
      }
  default:
      return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : static void NEAR DlgCheckOkEnable(hwnd, idEdit, message)   *
 *                      *
 *  PURPOSE    : Enables the <OK> button in a dialog box iff the edit item  *
 *     contains text.               *
 *                      *
 ****************************************************************************
static void NEAR DlgCheckOkEnable(hwnd, idEdit, message)

HWND  hwnd;
int  idEdit;
unsigned message;
{
    if (message == EN_CHANGE) {
  EnableWindow ( GetDlgItem (hwnd, IDOK),
           (BOOL)SendMessage (GetDlgItem (hwnd, idEdit),
            WM_GETTEXTLENGTH,
            0, 0L));
    }
}

/****************************************************************************
 *                      *
 *  FUNCTION   : AddExt (pch, ext)              *
 *                      *
 *  PURPOSE    : Add an extension to a filename if none is already specified*
 *                      *
 ****************************************************************************
static void NEAR AddExt (pch,ext)

char *pch;    /* File name    */
char *ext;    /* Extension to add */
{
    char acExt[20];
    char *pext = acExt;

    while (*ext && *ext != '.')
  ext++;
    while (*ext && *ext != ';')
  *pext++ = *ext++;
    *pext = 0;
    pext = acExt;

    while (*pch == '.') {
  pch++;
  if ((*pch == '.') && pch[1] == '\\')
      pch += 2;          /* ..\ */
  if (*pch == '\\')
      pch++;         /* .\ */
    }
    while (*pch != '\0')
  if (*pch++ == '.')
      return;

    // *pch++ = '.';
    do
  *pch++ = *pext;
    while (*pext++ != '\0');
}
/****************************************************************************
 *                      *
 *  FUNCTION   : FSearchSpec (sz)              *
 *                      *
 *  PURPOSE    : Checks to see if NULL-terminated strings contains a "*" or *
 *     a "?".                 *
 *                      *
 *  RETURNS    : TRUE  - if the above characters are found in the string    *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************
static BOOL NEAR FSearchSpec(sz)
char  *sz;
{
    for (; *sz ;sz++) {
  if (*sz == '*' || *sz == '?')
      return TRUE;
    }
    return FALSE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : static char * NEAR FillListBox (hDlg,pFile)        *
 *                      *
 *  PURPOSE    : Fill list box with filenames that match specifications, and*
 *     fills the static field with the path name.        *
 *                      *
 *  RETURNS    : A pointer to the pathname.                    *
 *                      *
 ****************************************************************************
static char * NEAR FillListBox (hDlg,pFile)

HWND  hDlg;
char  *pFile;  /* [path]{list of file wild cards, separated by ';'} */
{
    char  ach[20];
    char  *pch;
    char  *pDir;   /* Directory name or path */

    pch  = pFile;
    pDir = ach;

    while (*pch && *pch != ';')
  pch++;
    while ((pch > pFile) && (*pch != '/') && (*pch != '\\'))
  pch--;
    if (pch > pFile) {
       *pch = 0;
       lstrcpy (pDir, pFile);
       pFile = pch + 1;
    }
    else {
       lstrcpy (pDir,".");
    }

    DlgDirList (hDlg, pDir, DLGOPEN_DIR_LISTBOX, DLGOPEN_PATH,ATTRDIRLIST);
    SendDlgItemMessage (hDlg, DLGOPEN_FILE_LISTBOX, LB_RESETCONTENT, 0, 0L);
    SendDlgItemMessage (hDlg, DLGOPEN_FILE_LISTBOX, WM_SETREDRAW, FALSE, 0L);
    pDir = pFile;       /* save pFile to return */
    while (*pFile) {
  pch = ach;
  while (*pFile==' ')
      pFile++;
  while (*pFile && *pFile != ';')
      *pch++ = *pFile++;
  *pch = 0;
  if (*pFile)
      pFile++;
  SendDlgItemMessage (hDlg,
          DLGOPEN_FILE_LISTBOX,
          LB_DIR,ATTRFILELIST,
          (LONG)(LPSTR)ach);
    }
    SendDlgItemMessage (hDlg, DLGOPEN_FILE_LISTBOX, WM_SETREDRAW, TRUE, 0L);
    InvalidateRect (GetDlgItem (hDlg, DLGOPEN_FILE_LISTBOX), NULL, TRUE);

    return pDir;
}


DRAWDIB.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SHOWDIB\DRAWDIB.C

/****************************************************************************
 *                         *
 *  MODULE  : DrawDIB.c                   *
 *                         *
 *  PURPOSE  : Handles most of the SHOWDIB's DIB drawing and clipboard      *
 *      operations.                   *
 *                         *
 *  FUNCTIONS  :                     *
 *      PrintDIB()     -  Sets the current DIB bits to the   *
 *              printer DC.            *
 *                         *
 *      AppPaint()     -  Sets the DIB/bitmap bits on the    *
 *              screen or the given device.        *
 *                         *
 *      DrawSelect()     -  Draws selected clip rectangle on   *
 *              the DC/screen.           *
 *                         *
 *      NormalizeRect()   -  Swaps reversed rectangle coords.   *
 *                         *
 *      TrackMouse()     -  Draws rubberbanding rectangle and  *
 *              displays it's dimensions.          *
 *                         *
 *      BandDIB()     -  Outputs DIB in bands to device.    *
 *                         *
 *      SizeWindow()     -  Sizes app. window based on client  *
 *              dimensions and style.         *
 *                         *
 *      GetRealClientRect()   -  Calculates client rectangle dimen- *
 *              sions if scrollbars are present.   *
 *                         *
 *      SetScrollRanges()   -  Sets global scroll ranges.         *
 *                         *
 *      CopyHandle()     -  Makes a copy of memory block.      *
 *                         *
 *      CopyPalette()    -  Makes a copy of the GDI logical    *
 *              palette.             *
 *                         *
 *      CopyBitmap()     -  Copies given bitmap to another.    *
 *                         *
 *      CropBitmap()     -  Crops a bitmap to the given size.  *
 *                         *
 *      RenderFormat()   -  renders currently displayed DIB    *
 *              in CF_BITMAP or CF_DIB format.     *
 *                         *
 *      RealizeDibFormat()   -  Realizes the DIB in given format.  *
 *                         *
 *      ErrMsg()     -  Pops an error message to user.     *
 *                         *
 *      fDialog()     -  Displays a dialog box.         *
 *                         *
 *      AppAbout()     -  Shows the About.. dialog box.      *
 *                         *
 ****************************************************************************
#include <windows.h>
#include <io.h>
#include <stdio.h>
#include "showdib.h"

POINT          ptSize;      /* Stores DIB dimensions           */

/****************************************************************************
 *                      *
 *  FUNCTION   :  PrintDIB(HWND hWnd, HDC hDC, int x, int y, int dx, int dy)*
 *                      *
 *  PURPOSE    :  Set the DIB bits to the printer DC.          *
 *                      *
 ****************************************************************************
void PrintDIB (hWnd, hDC, x, y, dx, dy)
HWND hWnd;
HDC hDC;
int x, y;
int dx, dy;

{
    BITMAPINFOHEADER bi;
    int dibX,  dibY;
    int dibDX, dibDY;

    if (!bLegitDraw)
        return;

    DibInfo (hbiCurrent, &bi);

    if (IsRectEmpty (&rcClip)){
        dibX  = 0;
        dibY  = 0;
        dibDX = (int)bi.biWidth;
        dibDY = (int)bi.biHeight;
    }
    else{
        dibX  = rcClip.left;
  dibY  = (int)bi.biHeight - 1 - rcClip.bottom;
        dibDX = rcClip.right  - rcClip.left;
        dibDY = rcClip.bottom - rcClip.top;
    }

    if (hdibCurrent){
  /* Stretch the DIB to printer DC */
        StretchDibBlt ( hDC,
      x,
      y,
      dx,
      dy,
      hdibCurrent,
      dibX,
      dibY,
      dibDX,
      dibDY,
      SRCCOPY);
    }
    else if (achFileName[0]) {

  SetMapMode (hDC, MM_ANISOTROPIC);
  SetViewportOrg (hDC, x, y);
  SetViewportExt (hDC, dx, dy);

  BandDIB (hWnd, hDC, 0, 0);
    }
}

/****************************************************************************
 *                      *
 *  FUNCTION   :  AppPaint(HWND hWnd, HDC hDC, int x, int y)        *
 *                      *
 *  PURPOSE    :  Sets the DIB/bitmap bits on the screen or the given device*
 *                      *
 ****************************************************************************
void AppPaint (hWnd, hDC, x, y)

HWND hWnd;
HDC hDC;
int x, y;
{
    HPALETTE hpalT;
    BITMAPINFOHEADER bi;
    LPBITMAPINFOHEADER lpbi;

    SetWindowOrg (hDC, x, y);
    SetBkMode (hDC, wTransparent);

    if (bLegitDraw) {
  hpalT = SelectPalette (hDC, hpalCurrent, FALSE);
  RealizePalette (hDC);

        if (hbmCurrent && !bDIBToDevice) {
      DrawBitmap (hDC, 0, 0, hbmCurrent, SRCCOPY);
        }
        else if (hdibCurrent) {
      DibInfo (hdibCurrent, &bi);
      DibBlt (hDC,
        0,
        0,
        (int)bi.biWidth,
        (int)bi.biHeight,
        hdibCurrent,
        0,
        0,
        SRCCOPY);
        }
        else if (achFileName[0]) {
      BandDIB (hWnd, hDC, 0, 0);
        }

        SelectPalette(hDC,hpalT,FALSE);
    }

    DrawSelect(hDC, TRUE);
}

/****************************************************************************
 *                      *
 *  FUNCTION   :  DrawSelect(HDC hdc, BOOL fDraw)          *
 *                      *
 *  PURPOSE    :  Draws the selected clip rectangle with its dimensions on  *
 *      the DC/screen               *
 *                      *
 ****************************************************************************
void DrawSelect( hdc, fDraw)

HDC hdc;
BOOL fDraw;
{
    char  sz[80];
    DWORD dw;
    int   x,y,len,dx,dy;
    HDC   hdcBits;
    HBITMAP hbm;

    if (!IsRectEmpty (&rcClip)) {

  /* If a rectangular clip region has been selected, draw it */
        PatBlt(hdc, rcClip.left,    rcClip.top,        rcClip.right-rcClip.le
        PatBlt(hdc, rcClip.left,    rcClip.bottom, 1, -(rcClip.bottom-rcClip.
        PatBlt(hdc, rcClip.right-1, rcClip.top, 1,   rcClip.bottom-rcClip.top
        PatBlt(hdc, rcClip.right,   rcClip.bottom-1, -(rcClip.right-rcClip.le

  /* Format the dimensions string ...*/
  wsprintf( sz,
      "%dx%d",
      rcClip.right  - rcClip.left,
      rcClip.bottom - rcClip.top );
        len = lstrlen(sz);

  /* ... and center it in the rectangle */
  dw = GetTextExtent (hdc, sz, len);
  dx = LOWORD (dw);
  dy = HIWORD (dw);
  x  =  (rcClip.right  + rcClip.left - dx) / 2;
  y  =  (rcClip.bottom + rcClip.top  - dy) / 2;

  hdcBits = CreateCompatibleDC (hdc);
  SetTextColor (hdcBits, 0xFFFFFFL);
  SetBkColor (hdcBits, 0x000000L);

  /* Output the text to the DC */
  if (hbm = CreateBitmap (dx, dy, 1, 1, NULL)){
      hbm = SelectObject (hdcBits, hbm);
      ExtTextOut (hdcBits, 0, 0, 0, NULL, sz, len, NULL);
      BitBlt (hdc, x, y, dx, dy, hdcBits, 0, 0, SRCINVERT);
      hbm = SelectObject (hdcBits, hbm);
      DeleteObject (hbm);
        }
  DeleteDC (hdcBits);
    }
}
/****************************************************************************
 *                      *
 *  FUNCTION   : NormalizeRect(RECT *prc)            *
 *                      *
 *  PURPOSE    : If the rectangle coordinates are reversed, swaps them      *
 *                      *
 ****************************************************************************
void PASCAL NormalizeRect (prc)
RECT *prc;
{
    if (prc->right < prc->left)
        SWAP(prc->right,prc->left);
    if (prc->bottom < prc->top)
        SWAP(prc->bottom,prc->top);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : TrackMouse(HWND hwnd, POINT pt)          *
 *                      *
 *  PURPOSE    : Draws a rubberbanding rectangle and displays it's          *
 *     dimensions till the mouse button is released        *
 *                      *
 ****************************************************************************
void TrackMouse (hwnd, pt)
HWND hwnd;
POINT pt;
{
    POINT ptBase;
    HDC   hdc;
    MSG   msg;
    POINT ptOrigin;
    RECT  rcClient;

    hdc = GetDC(hwnd);
    SetCapture(hwnd);

    GetClientRect(hwnd,&rcClient);

    /* Get mouse coordinates relative to origin of DIB */
    ptOrigin.x = GetScrollPos(hwnd,SB_HORZ);
    ptOrigin.y = GetScrollPos(hwnd,SB_VERT);

    pt.x += ptOrigin.x;
    pt.y += ptOrigin.y;

    /* Display the coordinates */
    SetWindowOrg(hdc,ptOrigin.x,ptOrigin.y);
    DrawSelect(hdc,FALSE);

    /* Initialize clip rectangle to the point */
    rcClip.left   = pt.x;
    rcClip.top    = pt.y;
    rcClip.right  = pt.x;
    rcClip.bottom = pt.y;

    /* Eat mouse messages until a WM_LBUTTONUP is encountered. Meanwhile
     * continue to draw a rubberbanding rectangle and display it's dimensions
     */
    for (;;){
  WaitMessage();
  if (PeekMessage(&msg,NULL,WM_MOUSEFIRST,WM_MOUSELAST,PM_REMOVE)){
            DrawSelect(hdc,FALSE);

            rcClip.left   = pt.x;
            rcClip.top    = pt.y;
            rcClip.right  = LOWORD(msg.lParam) + ptOrigin.x;
            rcClip.bottom = HIWORD(msg.lParam) + ptOrigin.y;

            NormalizeRect(&rcClip);
            DrawSelect(hdc,TRUE);

            if (msg.message == WM_LBUTTONUP)
    break;
        }
  else
      continue;
    }

    ReleaseCapture();
    ReleaseDC(hwnd,hdc);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : BandDIB(HWND hWnd, HDC hDC, int x, int y)        *
 *                      *
 *  PURPOSE    : Outputs the DIB in bands to a device or the screen, using  *
 *     the maximum possible band size.          *
 *                      *
 ****************************************************************************
void BandDIB (hWnd, hDC, x, y)
HWND hWnd;
HDC hDC;
int x, y;
{
    HBITMAP         hBitmap, hOld ;
    HDC          hMemDC ;
    LPSTR         pBuf;
    LPBITMAPINFOHEADER lpbi;
    WORD         wRead, wActualPosition, wScansLeft  ;
    DWORD         dwMapSize;
    DWORD         dwScans;
    WORD         wBitmapHeight;
    RECT         Rect;
    HANDLE         hBuf;
    BOOL         bSuccess = FALSE;
    int          nBandSize;
    HPALETTE         hOldMemPal;
    HPALETTE         hOldPal;
    int          fh;
    OFSTRUCT         of;

    /* Open the map file and get the information out */
    fh = OpenFile (achFileName, (LPOFSTRUCT)&of, OF_READ);

    if (fh == -1)
  return;
    lpbi = (VOID FAR *)GlobalLock(hbiCurrent);
    if (!lpbi){
  _lclose (fh);
  return;
    }

    /* Compute scan size in bytes */
    dwScans = WIDTHBYTES((DWORD)lpbi->biWidth * lpbi->biBitCount);

    wBitmapHeight = (WORD)lpbi->biHeight ;
    wScansLeft    = (WORD)lpbi->biHeight ;

    hMemDC = NULL;
    for ( nBandSize = wScansLeft;
    nBandSize >= MINBAND || nBandSize == wScansLeft;
    nBandSize -= BANDINCREMENT) {

  /* Attempt to maximize band size by trying to allocate a buffer
   * for the given band size. If allocation fails, try again with the
   * smaller band size.
   */
  hBuf = GlobalAlloc (GMEM_FIXED | GMEM_ZEROINIT, dwScans * nBandSize) ;
  if (!hBuf)
      continue;

  /* Show success and exit loop if we're going to set bits to device. */
  if (bDIBToDevice) {
      hMemDC = 1;
      break;
  }
  else {
      /* Create a device-dependent bitmap to hold the bits */
      hBitmap = CreateCompatibleBitmap (hDC,
                (WORD)lpbi->biWidth,
                nBandSize);
      if (!hBitmap) {
    /* Try again for the next smaller band size */
    GlobalFree (hBuf);
    continue;
      }

      /* Create a memory context for the bitmap */
      if (!(hMemDC = CreateCompatibleDC (hDC))) {
    GlobalFree (hBuf);
    DeleteObject (hBitmap);
    continue;
      } else
    /* Success in creating a DC */
    break;
  }
    }
    if (!hMemDC) {

  /* We failed allocation , so give error message and quit */
  if (GetFocus () == hWnd) {
      ErrMsg ("No memory available!");
      ValidateRect (hWnd, (LPRECT) (NULL));
  } else
      MessageBeep(0);

  GlobalUnlock(hbiCurrent);
  _lclose (fh);
  return;
    }
    pBuf = GlobalLock (hBuf);

    /* Calculate number of bytes to be transferred */
    dwMapSize = dwScans * nBandSize ;

    /* Manipulate palette appropriately */
    if (!bDIBToDevice)
  hOldMemPal = SelectPalette (hMemDC, hpalCurrent, 0) ;

    /* Now get to the start of the map in the file */
    _llseek(fh, dwOffset, SEEK_SET);

    /* we are now all set to start off */
    wActualPosition = wScansLeft ;

    Rect.left  = 0;
    Rect.right = (WORD)lpbi->biWidth;

    hOldPal = SelectPalette(hDC, hpalCurrent, 0);
    RealizePalette(hDC);

    do {
  /* Read in nBandSize scans or whatever is left */
  if (wScansLeft > nBandSize)
      wRead = nBandSize ;
  else
      wRead = wScansLeft ;

  Rect.bottom = wActualPosition;
  wActualPosition -= wRead ;
  Rect.top = wActualPosition;

  dwMapSize = ((DWORD) wRead) * dwScans ;

  /* Now read in the map to the global buffer */
  if (RectVisible (hDC, &Rect)) {
      lread(fh, (LPSTR)pBuf, dwMapSize);

      if (bDIBToDevice) {
    if (wRead != SetDIBitsToDevice (hDC, x, y,
            (WORD)lpbi->biWidth,
            wBitmapHeight,
            0,
            0,
            wBitmapHeight - wScansLeft,
            wRead,
            pBuf,
            (LPBITMAPINFO)lpbi,
            fPalColors ?
            DIB_PAL_COLORS :
            DIB_RGB_COLORS)){
        ErrMsg ("Could not draw DIB scans to device!");
        GlobalUnlock (hBuf);
        GlobalFree (hBuf);
        GlobalUnlock(hbiCurrent);
        _lclose (fh);
        return;
    }
      } else {
    lpbi->biHeight = wRead ;

    /* Set the DIB bits to a device-dependent format */
    if ((WORD)lpbi->biHeight != SetDIBits (hMemDC,
                   hBitmap,
                   0,
                   (WORD)lpbi->biHeight,
                   pBuf,
                   (LPBITMAPINFO)lpbi,
                   fPalColors ?
                   DIB_PAL_COLORS :
                   DIB_RGB_COLORS)){
        ErrMsg ("Could not draw DIB scans!");
        GlobalUnlock (hBuf);
        GlobalFree (hBuf);
        GlobalUnlock(hbiCurrent);
        _lclose (fh);
        return;
    }

    /* Blt own map onto the screen, remembering the point to start */
    hOld = SelectObject (hMemDC, hBitmap) ;
    if (!BitBlt (hDC, 0, wActualPosition,
           (WORD)lpbi->biWidth,
           (WORD)lpbi->biHeight,
           hMemDC, 0, 0, SRCCOPY)){
        ErrMsg ("Could not draw map to screen!");
        GlobalUnlock (hBuf);
        GlobalFree (hBuf);
        GlobalUnlock(hbiCurrent);
        _lclose (fh);
        return;
    }
    SelectObject (hMemDC, hOld) ;

    /* Restore the value of bitmap height */
    lpbi->biHeight = wBitmapHeight ;
      }
  }
  else {
      /* This chunk is not visible, seek over the data in the file */
      _llseek(fh, dwMapSize, SEEK_CUR);
  }
  wScansLeft -= wRead ;
    } while (wScansLeft > 0 ) ;

    /* Delete the objects just created above */
    GlobalUnlock (hBuf);
    GlobalFree (hBuf);
    SelectPalette (hDC, hOldPal, 0);

    /* Set success flag */
    bSuccess = TRUE;

    if (!bDIBToDevice) {
  SelectPalette (hMemDC, hOldMemPal, 0);
  DeleteDC (hMemDC) ;
  DeleteObject (hBitmap) ;
    }
    GlobalUnlock(hbiCurrent);

    /* Close the file */
    _lclose (fh);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : SizeWindow(HWND hWnd)              *
 *                      *
 *  PURPOSE    : Sizes the app. window based on client dimensions (DIB      *
 *     dimensions) and style. Sets the caption text.        *
 *                      *
 ****************************************************************************
void SizeWindow (hWnd)
HWND hWnd;
{
    char  *pstr;
    char  Name[60];
    RECT  Rectangle;
    int   dx,dy;
    POINT pt;
    BITMAPINFOHEADER bi;

    /* Get information about current DIB */
    DibInfo(hbiCurrent,&bi);

    /* Extract the filename from the full pathname */
    pstr = achFileName + lstrlen(achFileName) - 1;
    while ((*pstr != '\\') && (*pstr != ':') && (pstr >= achFileName))
      pstr--;
    pstr++;

    /* Format filename along with the DIB attributes */
    wsprintf (Name,
        "%ls (%ls %dx%dx%d%ls)",
        (LPSTR)szAppName,
        (LPSTR)pstr,
        (WORD)bi.biWidth,
        (WORD)bi.biHeight,
        (WORD)bi.biBitCount,
        bi.biCompression == BI_RGB  ? (LPSTR)" RGB" :
        bi.biCompression == BI_RLE8 ? (LPSTR)" RLE8" : (LPSTR)" RLE4" );

    /* Show formatted text in the caption bar */
    SetWindowText (hWnd, Name);

    /* Store the size in ptSize, so the scroll bars will work. */
    ptSize.x = (WORD)bi.biWidth;
    ptSize.y = (WORD)bi.biHeight;

    if (IsZoomed (hWnd))
  SetScrollRanges (hWnd);
    else {
  Rectangle.left   = 0;
  Rectangle.top   = 0;
  Rectangle.right  = (WORD)bi.biWidth;
  Rectangle.bottom = (WORD)bi.biHeight;

  /* Compute the size of the window rectangle based on the given
   * client rectangle size and the window style, then size the
   * window.
   */
  AdjustWindowRect (&Rectangle, dwStyle, TRUE);
  SetWindowPos (hWnd, (HWND)NULL, 0, 0,
          Rectangle.right  - Rectangle.left + 1,
          Rectangle.bottom - Rectangle.top + 1,
          SWP_NOMOVE | SWP_NOZORDER);
    }

    InvalidateRect(hWnd,NULL,TRUE);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : GetRealClientRect(HWND hwnd, LPRECT lprc)        *
 *                      *
 *  PURPOSE    : Calculates the client rectangle taking scrollbars into     *
 *     consideration.               *
 *                      *
 ****************************************************************************
void GetRealClientRect (hwnd, lprc)
HWND hwnd;
PRECT lprc;
{
    DWORD dwStyle;

    dwStyle = GetWindowLong (hwnd, GWL_STYLE);
    GetClientRect (hwnd,lprc);

    if (dwStyle & WS_HSCROLL)
  lprc->bottom += GetSystemMetrics (SM_CYHSCROLL);

    if (dwStyle & WS_VSCROLL)
  lprc->right  += GetSystemMetrics (SM_CXVSCROLL);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : SetScrollRanges(hwnd)              *
 *                      *
 *  PURPOSE    :                  *
 *                      *
 ****************************************************************************
void SetScrollRanges(hwnd)

HWND hwnd;
{
    RECT       rc;
    int        iRangeH, iRangeV, i;
    static int iSem = 0;

    if (!iSem){
        iSem++;
  GetRealClientRect (hwnd, &rc);

  for (i = 0; i < 2; i++){
            iRangeV = ptSize.y - rc.bottom;
            iRangeH = ptSize.x - rc.right;

            if (iRangeH < 0) iRangeH = 0;
            if (iRangeV < 0) iRangeV = 0;

      if (GetScrollPos ( hwnd,
             SB_VERT) > iRangeV ||
             GetScrollPos (hwnd, SB_HORZ) > iRangeH)
    InvalidateRect (hwnd, NULL, TRUE);

      SetScrollRange (hwnd, SB_VERT, 0, iRangeV, TRUE);
      SetScrollRange (hwnd, SB_HORZ, 0, iRangeH, TRUE);

      GetClientRect (hwnd, &rc);
        }
        iSem--;
    }
}

/*********** THE FOLLOWING FUNCTIONS ARE FOR CLIPBOARD SUPPORT **************
/****************************************************************************
 *                      *
 *  FUNCTION   : CopyHandle (HANDLE h)              *
 *                      *
 *  PURPOSE    : Makes a copy of the given global memory block.       *
 *                      *
 *  RETURNS    : A handle to the new block.            *
 *                      *
 ****************************************************************************
HANDLE CopyHandle (h)
HANDLE h;
{
    BYTE huge *lpCopy;
    BYTE huge *lp;
    HANDLE hCopy;
    DWORD  dwLen;

    dwLen = GlobalSize (h);
    if (hCopy = GlobalAlloc (GHND, dwLen)) {

  lpCopy = (BYTE huge *)GlobalLock (hCopy);
  lp     = (BYTE huge *)GlobalLock (h);
  while (dwLen--) *lpCopy++ = *lp++;
  GlobalUnlock (hCopy);
  GlobalUnlock (h);
    }
    return hCopy;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : CopyPalette(HPALETTE hpal)            *
 *                      *
 *  PURPOSE    : Makes a copy of a GDI logical palette          *
 *                      *
 *  RETURNS    : A handle to the new palette.            *
 *                      *
 ****************************************************************************
HPALETTE CopyPalette (hpal)
HPALETTE hpal;
{
    PLOGPALETTE ppal;
    int         nNumEntries;

    if (!hpal)
  return NULL;

    GetObject(hpal,sizeof(int),(LPSTR)&nNumEntries);

    if (nNumEntries == 0)
        return NULL;

    ppal = (PLOGPALETTE)LocalAlloc(LPTR,sizeof(LOGPALETTE) +
                nNumEntries * sizeof(PALETTEENTRY));

    if (!ppal)
        return NULL;

    ppal->palVersion    = PALVERSION;
    ppal->palNumEntries = nNumEntries;

    GetPaletteEntries(hpal,0,nNumEntries,ppal->palPalEntry);

    hpal = CreatePalette(ppal);

    LocalFree((HANDLE)ppal);
    return hpal;
}
/****************************************************************************
 *                      *
 *  FUNCTION   : CopyBitmap (HBITMAP hbm)            *
 *                      *
 *  PURPOSE    : Copies the given bitmap to another.          *
 *                      *
 *  RETURNS    : A handle to the new bitmap.            *
 *                      *
 ****************************************************************************
HBITMAP CopyBitmap (hbm)
HBITMAP hbm;
{
    BITMAP  bm;
    RECT    rc;

    if (!hbm)
         return NULL;

    GetObject (hbm, sizeof(BITMAP), (LPSTR)&bm);
    rc.left   = 0;
    rc.top    = 0;
    rc.right  = bm.bmWidth;
    rc.bottom = bm.bmHeight;

    return CropBitmap (hbm, &rc);
}
/****************************************************************************
 *                      *
 *  FUNCTION   :  CropBitmap (hbm,lprect)            *
 *                      *
 *  PURPOSE    :  Crops a bitmap to a new size specified by the lprect      *
 *      parameter.                *
 *                      *
 *  RETURNS    :  A handle to the new bitmap.            *
 *                      *
 ****************************************************************************
HBITMAP CropBitmap (hbm, prc)
HBITMAP hbm;
PRECT prc;
{
    HDC     hMemDCsrc;
    HDC     hMemDCdst;
    HDC     hdc;
    HBITMAP hNewBm;
    BITMAP  bm;
    int     dx,dy;

    if (!hbm)
         return NULL;

    hdc = GetDC (NULL);
    hMemDCsrc = CreateCompatibleDC (hdc);
    hMemDCdst = CreateCompatibleDC (hdc);

    GetObject (hbm, sizeof(BITMAP), (LPSTR)&bm);
    dx = prc->right  - prc->left;
    dy = prc->bottom - prc->top;

    hNewBm = CreateBitmap (dx, dy, bm.bmPlanes, bm.bmBitsPixel, NULL);
    if (hNewBm){
  SelectObject (hMemDCsrc, hbm);
  SelectObject (hMemDCdst, hNewBm);

  BitBlt (hMemDCdst,
    0,
    0,
    dx,
    dy,
    hMemDCsrc,
    prc->left,
    prc->top,
    SRCCOPY);
    }

    ReleaseDC (NULL,hdc);
    DeleteDC (hMemDCsrc);
    DeleteDC (hMemDCdst);
    return hNewBm;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : RenderFormat(int cf)              *
 *                      *
 *  PURPOSE    : Renders the currently displayed DIB in CF_DIB or      *
 *     CF_BITMAP format.The bitmap is clipped to the current      *
 *     rcClip.                *
 *                      *
 *  RETURNS    : A handle to the DIB              *
 *                      *
 ****************************************************************************
HANDLE RenderFormat (cf)
int cf;
{
    HANDLE  h = NULL;
    HBITMAP hbm;

    if (!bLegitDraw)
        return NULL;

    switch (cf){
        case CF_BITMAP:
      if (hbmCurrent && !IsRectEmpty (&rcClip))
    h = CropBitmap (hbmCurrent, &rcClip);
      else{
                if (hbmCurrent)
        h = CopyBitmap (hbmCurrent);
                else if (hdibCurrent)
        h = BitmapFromDib (hdibCurrent, hpalCurrent);
    else if (achFileName[0] && (hdibCurrent = OpenDIB (achFileName)))
        h = BitmapFromDib (hdibCurrent, hpalCurrent);
                else
                    h = NULL;

    if (h && !IsRectEmpty (&rcClip)){
        hbm = CropBitmap (h,&rcClip);
        DeleteObject (h);
                    h = hbm;
                }
            }
            break;

        case CF_DIB:
      if (!IsRectEmpty (&rcClip)){
    if (hbm = RenderFormat (CF_BITMAP)){
        h = DibFromBitmap (hbm, BI_RGB, 0, hpalCurrent);
        DeleteObject (hbm);
                }
            }
      else{
                if (!hdibCurrent && hbmCurrent)
        h = DibFromBitmap (hbmCurrent, BI_RGB, 0, hpalCurrent);
                else if (hdibCurrent)
        h = CopyHandle (hdibCurrent);
                else if (achFileName[0])
        h = OpenDIB (achFileName);
                else
                    h = NULL;
            }
            break;

        case CF_PALETTE:
            if (hpalCurrent)
    h = CopyPalette (hpalCurrent);
            break;
    }
    return h;
}
/****************************************************************************
 *                      *
 *  FUNCTION   :  RealizeDibFormat(DWORD biStyle, WORD biBits)        *
 *                      *
 *  PURPOSE    :  Realize the current DIB in the specifed format      *
 *      This function is used to get a specific format of CF_DIB  *
 *                      *
 *          biStyle    DIB format    RGB or RLE        *
 *          biBits    Bits per pixel  1,4,8,24        *
 *                      *
 *  RETURNS    :  A handle to the created DIB.            *
 *                      *
 ****************************************************************************
HANDLE RealizeDibFormat (biStyle, biBits)
DWORD biStyle;
WORD biBits;
{
    BITMAPINFOHEADER bi;

    if (!bLegitDraw)
        return NULL;

    DibInfo (hbiCurrent, &bi);

    /*  Do we have the requested format already? */
    if (bi.biCompression == biStyle && bi.biBitCount == biBits){
        if (!hdibCurrent)
      hdibCurrent = RenderFormat (CF_DIB);
    }
    else{
        if (!hbmCurrent)
      hbmCurrent = RenderFormat (CF_BITMAP);

  if (hbmCurrent){
            if (hdibCurrent)
    GlobalFree (hdibCurrent);

      hdibCurrent = DibFromBitmap (hbmCurrent, biStyle, biBits, hpalCurrent);
        }
    }

    return hdibCurrent;
}
/****************************************************************************
 *                      *
 *  FUNCTION   : ErrMsg (PSTR sz,...)              *
 *                      *
 *  PURPOSE    : Opens a Message box with a error message in it.The user can*
 *     select the OK button to continue          *
 *                      *
 *  RETURNS    : FALSE to indicate an error has occured.        *
 *                      *
 ****************************************************************************
int ErrMsg (PSTR sz,...)
{
    char ach[128];

    wvsprintf (ach, sz, (LPSTR)(&sz+1));   /* Format the string */
    MessageBox (NULL, ach, NULL, MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
    return FALSE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : fDialog(int id,HWND hwnd,FARPROC fpfn)         *
 *                      *
 *  PURPOSE    : This function displays a dialog box          *
 *                      *
 *  RETURNS    : The exit code.               *
 *                      *
 ****************************************************************************
BOOL fDialog (id, hwnd, fpfn)
int id;
HWND hwnd;
FARPROC fpfn;
{
    BOOL  f;
    HANDLE  hInst;

    hInst = GetWindowWord (hwnd, GWW_HINSTANCE);
    fpfn  = MakeProcInstance (fpfn, hInst);
    f = DialogBox (hInst, MAKEINTRESOURCE(id), hwnd, fpfn);
    FreeProcInstance (fpfn);
    return f;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : AppAbout( hDlg, uiMessage, wParam, lParam )        *
 *                      *
 *  PURPOSE    : Dialog function for the About... dialog box        *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL AppAbout( hDlg, uiMessage, wParam, lParam )

HWND   hDlg;
unsigned uiMessage;
WORD   wParam;
long   lParam;
{
    switch (uiMessage) {
        case WM_COMMAND:
            if (wParam == IDOK)
    EndDialog (hDlg, TRUE);
      break;

  case WM_INITDIALOG:
      return TRUE;
    }
    return FALSE;
}


EDITCNTL.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\EDITCNTL\EDITCNTL.C

/****************************************************************************

    PROGRAM: EditCntl.c

    PURPOSE: Creates an edit window

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box

    COMMENTS:

  After setting up the application's window, the size of the client
  area is determined and a child window is created to use for editing.

****************************************************************************/

#include "windows.h"
#include "editcntl.h"

HANDLE hInst;

HANDLE hAccTable;                                /* handle to accelerator tab
HWND hEditWnd;              /* handle to edit window */
HWND hwnd;                    /* handle to main windows  */

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {

    /* Only translate message if it is not an accelerator message */

        if (!TranslateAccelerator(hwnd, hAccTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "EditCntlMenu";
    wc.lpszClassName = "EditCntlWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    RECT            Rect;

    hInst = hInstance;

    hAccTable = LoadAccelerators(hInst, "EditCntlAcc");

    hwnd = CreateWindow(
        "EditCntlWClass",
        "EditCntl Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

    GetClientRect(hwnd, (LPRECT) &Rect);

    /* Create a child window */

    hEditWnd = CreateWindow("Edit",
  NULL,
  WS_CHILD | WS_VISIBLE |
  ES_MULTILINE |
  WS_VSCROLL | WS_HSCROLL |
  ES_AUTOHSCROLL | ES_AUTOVSCROLL,
  0,
  0,
  (Rect.right-Rect.left),
  (Rect.bottom-Rect.top),
  hwnd,
  IDC_EDIT,                          /* Child control i.d. */
  hInst,
  NULL);

    if (!hEditWnd) {
  DestroyWindow(hwnd);
  return (NULL);
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND    - application menu (About dialog box)
  WM_DESTROY    - destroy window

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;

    switch (message) {
  case WM_COMMAND:
            switch (wParam) {
                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance(About, hInst);
                    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
                    FreeProcInstance(lpProcAbout);
                    break;

                /* file menu commands */

                case IDM_NEW:
                case IDM_OPEN:
                case IDM_SAVE:
                case IDM_SAVEAS:
                case IDM_PRINT:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "EditCntl Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;

                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;

                /* edit menu commands */

                case IDM_UNDO:
                case IDM_CUT:
                case IDM_COPY:
                case IDM_PASTE:
                case IDM_CLEAR:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "EditCntl Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;

                case IDC_EDIT:
                    if (HIWORD (lParam) == EN_ERRSPACE) {
                        MessageBox (
                              GetFocus ()
                            , "Out of memory."
                            , "EditCntl Sample Application"
                            , MB_ICONHAND | MB_OK
                        );
                    }
                    break;

            }
            break;

        case WM_SETFOCUS:
            SetFocus (hEditWnd);
            break;

        case WM_SIZE:
            MoveWindow(hEditWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;

  case WM_DESTROY:
      PostQuitMessage(0);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


EDITFILE.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\EDITFILE\EDITFILE.C

/****************************************************************************

    PROGRAM: EditFile.c

    PURPOSE: Loads, saves, and edits text files

    FUNCTIONS:

        WinMain() - calls initialization function, processes message loop
        InitApplication() - initializes window data and registers window
        InitInstance() - saves instance handle and creates main window
        MainWndProc() - processes messages
        About() - processes messages for "About" dialog box
        SaveAsDlg() - save file under different name
        OpenDlg() - let user select a file, and open it.
        UpdateListBox() - Update the list box of OpenDlg
        ChangeDefExt() - Change the default extension
        SeparateFile() - Separate filename and pathname
        AddExt() - Add default extension
        CheckFileName() - Check for wildcards, add extension if needed
        SaveFile() - Save current file
        QuerySaveFile() - Called when some action might lose current contents
        SetNewBuffer() - Set new buffer for edit window

****************************************************************************/

#include "windows.h"
#include "editfile.h"

HANDLE hInst;

HANDLE hAccTable;                                /* handle to accelerator tab
HWND hEditWnd;                                      /* handle to edit window
/* Additional includes needed for the fstat() function */

#include <sys\types.h>
#include <sys\stat.h>

char FileName[128];
char PathName[128];
char OpenName[128];
char DefPath[128];
char DefSpec[13] = "*.*";
char DefExt[] = ".txt";
char str[255];

HANDLE hEditBuffer;                       /* handle to editing buffer      */
HANDLE hOldBuffer;                        /* old buffer handle        */
HANDLE hHourGlass;                        /* handle to hourglass cursor
HANDLE hSaveCursor;                       /* current cursor handle      */
int hFile;                                /* file handle              */
int count;                                /* number of chars read or written
PSTR pBuffer;                             /* address of read/write buffer
OFSTRUCT OfStruct;                        /* information from OpenFile()
struct stat FileStatus;                   /* information from fstat()      */
BOOL bChanges = FALSE;                    /* TRUE if the file is changed
BOOL bSaveEnabled = FALSE;                /* TRUE if text in the edit buffer
PSTR pEditBuffer;                         /* address of the edit buffer
RECT Rect;                                /* dimension of the client window
HWND hwnd;                                /* handle to main window


char Untitled[] =                         /* default window title      */
     "Edit File - (untitled)";

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
        if (!InitApplication(hInstance))
            return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {

    /* Only translate message if it is not an accelerator message */

        if (!TranslateAccelerator(hwnd, hAccTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "EditFileMenu";
    wc.lpszClassName = "EditFileWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    RECT            Rect;

    hInst = hInstance;

    hAccTable = LoadAccelerators(hInst, "EditFileAcc");

    hwnd = CreateWindow(
        "EditFileWClass",
        "EditFile Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

    GetClientRect(hwnd, (LPRECT) &Rect);

    /* Create a child window */

    hEditWnd = CreateWindow("Edit",
        NULL,
        WS_CHILD | WS_VISIBLE |
        ES_MULTILINE |
        WS_VSCROLL | WS_HSCROLL |
        ES_AUTOHSCROLL | ES_AUTOVSCROLL,
        0,
        0,
        (Rect.right-Rect.left),
        (Rect.bottom-Rect.top),
        hwnd,
        IDC_EDIT,                          /* Child control i.d. */
        hInst,
        NULL);

    if (!hEditWnd) {
        DestroyWindow(hwnd);
        return (NULL);
    }

    /* Get an hourglass cursor to use during file transfers */

    hHourGlass = LoadCursor(NULL, IDC_WAIT);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

        WM_COMMAND    - application menu (About dialog box)
        WM_DESTROY    - destroy window
        WM_SIZE       - window size has changed
        WM_QUERYENDSESSION - willing to end session?
        WM_ENDSESSION - end Windows session
        WM_CLOSE      - close the window
        WM_SIZE       - window resized

    COMMENTS:

        WM_COMMAND processing:

            IDM_NEW - query to save current file if there is one and it has
                      been changed, clear buffer and start new file.

            IDM_OPEN - query to save current file if there is one and it
                       has been changed, open a new file.

            IDM_SAVE - save current file, prompt for name if none exists.

            IDM_SAVEAS - prompt for new filename to save to, save file.

            IDC_EDIT - change "bChanges" flag to indicate if edit buffer has
                      been modified.  Affects actions of IDM_NEW and
                      IDM_OPEN.  Reset when file is saved.

            IDM_EXIT - query to save current file if there is one and it
                       has been changed, then exit.

            IDM_ABOUT - display "About" box.

        When more then one string needs to be sent to a message box,
        sprintf() is used to combine the strings into str[], and then str[]
        is passed to the MessageBox() function.  A message box string cannot
        exceed 255 characters, but may contain \n to generate separate
        lines.

        After the size of the file is determined, only enough memory to store
        the file is allocated for the Edit buffer.  The edit control will
        automatically expand this memory as needed.  Once the file has been
        read into the edit buffer, unlock the memory.  Use whatever was
        obtained from the read() function, even if an error occured.  This
        allows partial salvage of a file with a bad sector.

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout, lpOpenDlg, lpSaveAsDlg;

    int Success;                            /* return value from SaveAsDlg()
    int IOStatus;                           /* result of file i/o      */


    switch (message) {
        case WM_COMMAND:
            switch (wParam) {
                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance(About, hInst);
                    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
                    FreeProcInstance(lpProcAbout);
                    break;

                case IDM_NEW:

                    /* If current file has been modified, query user about
                     * saving it.
                     */

                    if (!QuerySaveFile(hWnd))
                        return (NULL);

                    /* bChanges is set to FALSE to indicate there have been
                     * no changes since the last file save.
                     */

                    bChanges = FALSE;
                    FileName[0] = 0;

                    /* Update the edit buffer */

                    SetNewBuffer(hWnd, NULL, Untitled);
                    break;

                case IDM_OPEN:
                    if (!QuerySaveFile(hWnd))
                        return (NULL);

                    lpOpenDlg = MakeProcInstance((FARPROC) OpenDlg, hInst);

                    /* Open the file and get its handle */

                    hFile = DialogBox(hInst, "Open", hWnd, lpOpenDlg);
                    FreeProcInstance(lpOpenDlg);
                    if (!hFile)
                        return (NULL);

                    /* Allocate edit buffer to the size of the file + 1 */

                    hEditBuffer =
                        LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,
          (WORD)(FileStatus.st_size+1));

                    if (!hEditBuffer) {
                        MessageBox(hWnd, "Not enough memory.",
                            NULL, MB_OK | MB_ICONHAND);
                        return (NULL);
                    }
                    hSaveCursor = SetCursor(hHourGlass);
                    pEditBuffer = LocalLock(hEditBuffer);

                    IOStatus = read(hFile, pEditBuffer, FileStatus.st_size);
                    close(hFile);

                    /* # bytes read must equal file size */

                    if (IOStatus != FileStatus.st_size) {

                        sprintf(str, "Error reading %s.", FileName);
                        SetCursor(hSaveCursor);      /* Remove the hourglass
                        MessageBox(hWnd, str, NULL, MB_OK | MB_ICONEXCLAMATIO
                    }

                    LocalUnlock(hEditBuffer);

                    /* Set up a new buffer and window title */

                    sprintf(str, "EditFile - %s", FileName);
                    SetNewBuffer(hWnd, hEditBuffer, str);
                    SetCursor(hSaveCursor);            /* restore the cursor
                    break;

                case IDM_SAVE:

                    /* If there is no filename, use the saveas command to get
                     * one.  Otherwise, save the file using the current
                     * filename.
                     */

                    if (!FileName[0])
                        goto saveas;
                    if (bChanges)
                        SaveFile(hWnd);
                    break;

                case IDM_SAVEAS:
saveas:
                    lpSaveAsDlg = MakeProcInstance(SaveAsDlg, hInst);

                    /* Call the SaveAsDlg() function to get the new filename

                    Success = DialogBox(hInst, "SaveAs", hWnd, lpSaveAsDlg);
                    FreeProcInstance(lpSaveAsDlg);

                    /* If successful, update the window title, save the file

                    if (Success == IDOK) {
                        sprintf(str, "EditFile - %s", FileName);
                        SetWindowText(hWnd, str);
                        SaveFile(hWnd);
                    }
                    break;                                  /* User canceled
                case IDM_PRINT:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "EditFile Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;

                /* edit menu commands */

                case IDM_UNDO:
                case IDM_CUT:
                case IDM_COPY:
                case IDM_PASTE:
                case IDM_CLEAR:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "EditFile Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;

                case IDM_EXIT:
                    QuerySaveFile(hWnd);
                    DestroyWindow(hWnd);
                    break;

                case IDC_EDIT:
                    if (HIWORD (lParam) == EN_ERRSPACE) {
                        MessageBox (
                              GetFocus ()
                            , "Out of memory."
                            , "EditFile Sample Application"
                            , MB_ICONHAND | MB_OK
                        );
                    }
                    break;

            }
            break;

        case WM_SETFOCUS:
            SetFocus (hEditWnd);
            break;

        case WM_SIZE:
            MoveWindow(hEditWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;

        case WM_QUERYENDSESSION:             /* message: to end the session?
            return (QuerySaveFile(hWnd));

        case WM_CLOSE:                       /* message: close the window
            if (QuerySaveFile(hWnd))
                DestroyWindow(hWnd);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}

/****************************************************************************

    FUNCTION: SaveAsDlg(HWND, unsigned, WORD, LONG)

    PURPOSE: Allows user to change name to save file to

    COMMENTS:

        This will initialize the window class if it is the first time this
        application is run.  It then creates the window, and processes the
        message loop until a PostQuitMessage is received.  It exits the
        application by returning the value passed by the PostQuitMessage.

****************************************************************************/

int FAR PASCAL SaveAsDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    char TempName[128];

    switch (message) {
        case WM_INITDIALOG:

            /* If no filename is entered, don't allow the user to save to it

            if (!FileName[0])
                bSaveEnabled = FALSE;
            else {
                bSaveEnabled = TRUE;

                /* Process the path to fit within the IDC_PATH field */

                DlgDirList(hDlg, DefPath, NULL, IDC_PATH, 0x4010);

                /* Send the current filename to the edit control */

                SetDlgItemText(hDlg, IDC_EDIT, FileName);

                /* Accept all characters in the edit control */

                SendDlgItemMessage(hDlg, IDC_EDIT, EM_SETSEL, 0,
                    MAKELONG(0, 0x7fff));
            }

            /* Enable or disable the save control depending on whether the
             * filename exists.
             */

            EnableWindow(GetDlgItem(hDlg, IDOK), bSaveEnabled);

            /* Set the focus to the edit control within the dialog box */

            SetFocus(GetDlgItem(hDlg, IDC_EDIT));
            return (FALSE);                 /* FALSE since Focus was changed

        case WM_COMMAND:
            switch (wParam) {
                case IDC_EDIT:

                    /* If there was previously no filename in the edit
                     * control, then the save control must be enabled as soon
                     * a character is entered.
                     */

                    if (HIWORD(lParam) == EN_CHANGE && !bSaveEnabled)
                    EnableWindow(GetDlgItem(hDlg, IDOK), bSaveEnabled = TRUE)
                    return (TRUE);

                case IDOK:

                   /* Get the filename from the edit control */

                    GetDlgItemText(hDlg, IDC_EDIT, TempName, 128);

                    /* If there are no wildcards, then separate the name into
                     * path and name.  If a path was specified, replace the
                     * default path with the new path.
                     */

                    if (CheckFileName(hDlg, FileName, TempName)) {
                        SeparateFile(hDlg, (LPSTR) str, (LPSTR) DefSpec,
                            (LPSTR) FileName);
                        if (str[0])
                            strcpy(DefPath, str);

                        /* Tell the caller a filename was selected */

                        EndDialog(hDlg, IDOK);
                    }
                    return (TRUE);

                case IDCANCEL:

                    /* Tell the caller the user canceled the SaveAs function

                    EndDialog(hDlg, IDCANCEL);
                    return (TRUE);
            }
            break;

    }
    return (FALSE);
}

/****************************************************************************

    FUNCTION: OpenDlg(HWND, unsigned, WORD, LONG)

    PURPOSE: Let user select a file, and open it.

****************************************************************************/

HANDLE FAR PASCAL OpenDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    WORD index;
    PSTR pTptr;
    HANDLE hFile;

    switch (message) {
        case WM_COMMAND:
            switch (wParam) {

                case IDC_LISTBOX:
                    switch (HIWORD(lParam)) {

                        case LBN_SELCHANGE:
                            /* If item is a directory name, append "*.*" */
                            if (DlgDirSelect(hDlg, str, IDC_LISTBOX))
                                strcat(str, DefSpec);

                            SetDlgItemText(hDlg, IDC_EDIT, str);
                            SendDlgItemMessage(hDlg,
                                IDC_EDIT,
                                EM_SETSEL,
                                NULL,
                                MAKELONG(0, 0x7fff));
                            break;

                        case LBN_DBLCLK:
                            goto openfile;
                    }
                    return (TRUE);

                case IDOK:
openfile:
                    GetDlgItemText(hDlg, IDC_EDIT, OpenName, 128);
                    if (strchr(OpenName, '*') || strchr(OpenName, '?')) {
                        SeparateFile(hDlg, (LPSTR) str, (LPSTR) DefSpec,
                            (LPSTR) OpenName);
                        if (str[0])
                            strcpy(DefPath, str);
                        ChangeDefExt(DefExt, DefSpec);
                        UpdateListBox(hDlg);
                        return (TRUE);
                    }

                    if (!OpenName[0]) {
                        MessageBox(hDlg, "No filename specified.",
                            NULL, MB_OK | MB_ICONHAND);
                        return (TRUE);
                    }

                    AddExt(OpenName, DefExt);

                    /* Open the file */

                    if ((hFile = OpenFile(OpenName, (LPOFSTRUCT) &OfStruct,
                            OF_READ)) == -1) {
                        sprintf(str, "Error %d opening %s.",
                            OfStruct.nErrCode, OpenName);
                        MessageBox(hDlg, str, NULL,
                            MB_OK | MB_ICONHAND);
                    }
                    else {

                        /* Make sure there's enough room for the file */

                        fstat(hFile, &FileStatus);
                        if (FileStatus.st_size > MAXFILESIZE) {
                            sprintf(str,
                    "Not enough memory to load %s.\n%s exceeds %ld bytes.",
                                OpenName, OpenName, MAXFILESIZE);
                            MessageBox(hDlg, str, NULL,
                                MB_OK | MB_ICONHAND);
                            return (TRUE);
                        }

                        /* File is opened and there is enough room so return
                         * the handle to the caller.
                         */

                        strcpy(FileName, OpenName);
                        EndDialog(hDlg, hFile);
                        return (TRUE);
                    }
                    return (TRUE);

                case IDCANCEL:
                    EndDialog(hDlg, NULL);
                    return (TRUE);
            }
            break;

        case WM_INITDIALOG:                        /* message: initialize
            UpdateListBox(hDlg);
            SetDlgItemText(hDlg, IDC_EDIT, DefSpec);
            SendDlgItemMessage(hDlg,               /* dialog handle      */
                IDC_EDIT,                          /* where to send message
                EM_SETSEL,                         /* select characters
                NULL,                              /* additional information
                MAKELONG(0, 0x7fff));              /* entire contents      */
            SetFocus(GetDlgItem(hDlg, IDC_EDIT));
            return (FALSE); /* Indicates the focus is set to a control */
    }
    return FALSE;
}

/****************************************************************************

    FUNCTION: UpdateListBox(HWND);

    PURPOSE: Update the list box of OpenDlg

****************************************************************************/

void UpdateListBox(hDlg)
HWND hDlg;
{
    strcpy(str, DefPath);
    strcat(str, DefSpec);
    DlgDirList(hDlg, str, IDC_LISTBOX, IDC_PATH, 0x4010);

    /* To ensure that the listing is made for a subdir. of
     * current drive dir...
     */
    if (!strchr (DefPath, ':'))
  DlgDirList(hDlg, DefSpec, IDC_LISTBOX, IDC_PATH, 0x4010);

    /* Remove the '..' character from path if it exists, since this
     * will make DlgDirList move us up an additional level in the tree
     * when UpdateListBox() is called again.
     */
    if (strstr (DefPath, ".."))
  DefPath[0] = '\0';

    SetDlgItemText(hDlg, IDC_EDIT, DefSpec);
}

/****************************************************************************

    FUNCTION: ChangeDefExt(PSTR, PSTR);

    PURPOSE: Change the default extension

****************************************************************************/

void ChangeDefExt(Ext, Name)
PSTR Ext, Name;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr)
        if (!strchr(pTptr, '*') && !strchr(pTptr, '?'))
            strcpy(Ext, pTptr);
}

/****************************************************************************

    FUNCTION: SeparateFile(HWND, LPSTR, LPSTR, LPSTR)

    PURPOSE: Separate filename and pathname

****************************************************************************/

void SeparateFile(hDlg, lpDestPath, lpDestFileName, lpSrcFileName)
HWND hDlg;
LPSTR lpDestPath, lpDestFileName, lpSrcFileName;
{
    LPSTR lpTmp;
    char  cTmp;

    lpTmp = lpSrcFileName + (long) lstrlen(lpSrcFileName);
    while (*lpTmp != ':' && *lpTmp != '\\' && lpTmp > lpSrcFileName)
        lpTmp = AnsiPrev(lpSrcFileName, lpTmp);
    if (*lpTmp != ':' && *lpTmp != '\\') {
        lstrcpy(lpDestFileName, lpSrcFileName);
        lpDestPath[0] = 0;
        return;
    }
    lstrcpy(lpDestFileName, lpTmp + 1);
    cTmp = *(lpTmp + 1);
    lstrcpy(lpDestPath, lpSrcFileName);
     *(lpTmp + 1) = cTmp;
    lpDestPath[(lpTmp - lpSrcFileName) + 1] = 0;
}

/****************************************************************************

    FUNCTION: AddExt(PSTR, PSTR);

    PURPOSE: Add default extension

/***************************************************************************/

void AddExt(Name, Ext)
PSTR Name, Ext;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr != '.')
        strcat(Name, Ext);
}

/****************************************************************************

    FUNCTION: CheckFileName(HWND, PSTR, PSTR)

    PURPOSE: Check for wildcards, add extension if needed

    COMMENTS:

        Make sure you have a filename and that it does not contain any
        wildcards.  If needed, add the default extension.  This function is
        called whenever your application wants to save a file.

****************************************************************************/

BOOL CheckFileName(hWnd, pDest, pSrc)
HWND hWnd;
PSTR pDest, pSrc;
{
    PSTR pTmp;

    if (!pSrc[0])
        return (FALSE);               /* Indicates no filename was specified

    pTmp = pSrc;
    while (*pTmp) {                     /* Searches the string for wildcards
        switch (*pTmp++) {
            case '*':
            case '?':
                MessageBox(hWnd, "Wildcards not allowed.",
                    NULL, MB_OK | MB_ICONEXCLAMATION);
                return (FALSE);
        }
    }

    AddExt(pSrc, DefExt);            /* Adds the default extension if needed

    if (OpenFile(pSrc, (LPOFSTRUCT) &OfStruct, OF_EXIST) >= 0) {
        sprintf(str, "Replace existing %s?", pSrc);
        if (MessageBox(hWnd, str, "EditFile",
                MB_OKCANCEL | MB_ICONHAND) == IDCANCEL)
            return (FALSE);
    }
    strcpy(pDest, pSrc);
    return (TRUE);
}

/****************************************************************************

    FUNCTION: SaveFile(HWND)

    PURPOSE: Save current file

    COMMENTS:

        This saves the current contents of the Edit buffer, and changes
        bChanges to indicate that the buffer has not been changed since the
        last save.

        Before the edit buffer is sent, you must get its handle and lock it
        to get its address.  Once the file is written, you must unlock the
        buffer.  This allows Windows to move the buffer when not in immediate
        use.

****************************************************************************/

BOOL SaveFile(hWnd)
HWND hWnd;
{
    BOOL bSuccess;
    int IOStatus;                                  /* result of a file write

    if ((hFile = OpenFile(FileName, &OfStruct,
        OF_PROMPT | OF_CANCEL | OF_CREATE)) < 0) {

        /* If the file can't be saved */

        sprintf(str, "Cannot write to %s.", FileName);
        MessageBox(hWnd, str, NULL, MB_OK | MB_ICONEXCLAMATION);
        return (FALSE);
    }


    hEditBuffer = SendMessage(hEditWnd, EM_GETHANDLE, 0, 0L);
    pEditBuffer = LocalLock(hEditBuffer);

    /* Set the cursor to an hourglass during the file transfer */

    hSaveCursor = SetCursor(hHourGlass);
    IOStatus = write(hFile, pEditBuffer, strlen(pEditBuffer));
    close(hFile);
    SetCursor(hSaveCursor);
    if (IOStatus != strlen(pEditBuffer)) {
        sprintf(str, "Error writing to %s.", FileName);
        MessageBox(hWnd, str,
            NULL, MB_OK | MB_ICONHAND);
        bSuccess = FALSE;
    }
    else {
        bSuccess = TRUE;                /* Indicates the file was saved
        bChanges = FALSE;               /* Indicates changes have been saved
    }

    LocalUnlock(hEditBuffer);
    return (bSuccess);
}

/****************************************************************************

    FUNCTION: QuerySaveFile(HWND);

    PURPOSE: Called when some action might lose current contents

    COMMENTS:

        This function is called whenever we are about to take an action that
        would lose the current contents of the edit buffer.

****************************************************************************/

BOOL QuerySaveFile(hWnd)
HWND hWnd;
{
    int Response;
    FARPROC lpSaveAsDlg;

    if (bChanges) {
        sprintf(str, "Save current changes: %s", FileName);
        Response = MessageBox(hWnd, str,
            "EditFile",  MB_YESNOCANCEL | MB_ICONEXCLAMATION);
        if (Response == IDYES) {
check_name:

            /* Make sure there is a filename to save to */

            if (!FileName[0]) {
                lpSaveAsDlg = MakeProcInstance(SaveAsDlg, hInst);
                Response = DialogBox(hInst, "SaveAs",
                    hWnd, lpSaveAsDlg);
                FreeProcInstance(lpSaveAsDlg);
                if (Response == IDOK)
                    goto check_name;
                else
                    return (FALSE);
            }
            SaveFile(hWnd);
        }
        else if (Response == IDCANCEL)
            return (FALSE);
    }
    else
        return (TRUE);
}

/****************************************************************************

    FUNCTION: SetNewBuffer(HWND, HANDLE, PSTR)

    PURPOSE: Set new buffer for edit window

    COMMENTS:

        Point the edit window to the new buffer, update the window title, and
        redraw the edit window.  If hNewBuffer is NULL, then create an empty
        1K buffer, and return its handle.

****************************************************************************/

void SetNewBuffer(hWnd, hNewBuffer, Title)
HWND hWnd;
HANDLE hNewBuffer;
PSTR Title;
{
    HANDLE hOldBuffer;

    hOldBuffer = SendMessage(hEditWnd, EM_GETHANDLE, 0, 0L);
    LocalFree(hOldBuffer);
    if (!hNewBuffer)                    /* Allocates a buffer if none exists
        hNewBuffer = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, 1);

    /* Updates the buffer and displays new buffer */
    SendMessage(hEditWnd, EM_SETHANDLE, hNewBuffer, 0L);

    SetWindowText(hWnd, Title);
    SetFocus(hEditWnd);
    bChanges = FALSE;
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
        case WM_INITDIALOG:
            return (TRUE);

        case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
                EndDialog(hDlg, TRUE);
                return (TRUE);
            }
            break;
    }
    return (FALSE);
}


EDITMENU.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\EDITMENU\EDITMENU.C

/****************************************************************************

    PROGRAM: EditMenu.c

    PURPOSE: EditMenu template for Windows applications

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box

****************************************************************************/

#include "windows.h"
#include "editmenu.h"

HANDLE hInst;

HANDLE hAccTable;                                /* handle to accelerator tab

HWND   hwnd;                                    /* handle to main window */

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {

    /* Only translate message if it is not an accelerator message */

        if (!TranslateAccelerator(hwnd, hAccTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "EditMenuMenu";
    wc.lpszClassName = "EditMenuWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{

    hInst = hInstance;

    hAccTable = LoadAccelerators(hInst, "EditMenuAcc");

    hwnd = CreateWindow(
        "EditMenuWClass",
        "EditMenu Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND    - application menu (About dialog box)
  WM_DESTROY    - destroy window

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;

    switch (message) {
  case WM_COMMAND:
            switch (wParam) {
                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance(About, hInst);
                    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
                    FreeProcInstance(lpProcAbout);
                    break;

                /* file menu commands */

                case IDM_NEW:
                case IDM_OPEN:
                case IDM_SAVE:
                case IDM_SAVEAS:
                case IDM_PRINT:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "EditMenu Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;

                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;

                /* edit menu commands */

                case IDM_UNDO:
                case IDM_CUT:
                case IDM_COPY:
                case IDM_PASTE:
                case IDM_CLEAR:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "EditMenu Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;
            }
            break;

  case WM_DESTROY:
      PostQuitMessage(0);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


FILEOPEN.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\FILEOPEN\FILEOPEN.C

/****************************************************************************

    PROGRAM: FileOpen.c

    PURPOSE: Loads, saves, and edits text files

    FUNCTIONS:

        WinMain() - calls initialization function, processes message loop
        InitApplication() - initializes window data and registers window
        InitInstance() - saves instance handle and creates main window
        MainWndProc() - processes messages
        About() - processes messages for "About" dialog box
        OpenDlg() - let user select a file, and open it.
        UpdateListBox() - Update the list box of OpenDlg
        ChangeDefExt() - Change the default extension
        SeparateFile() - Separate filename and pathname
        AddExt() - Add default extension

****************************************************************************/

#include "windows.h"
#include "fileopen.h"

HANDLE hInst;

HANDLE hAccTable;                                /* handle to accelerator tab
HWND hEditWnd;                                      /* handle to edit window
HWND hwnd;                                      /* handle to main window */

char FileName[128];
char PathName[128];
char OpenName[128];
char DefPath[128];
char DefSpec[13] = "*.*";
char DefExt[] = ".txt";
char str[255];

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
        if (!InitApplication(hInstance))
            return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {

    /* Only translate message if it is not an accelerator message */

        if (!TranslateAccelerator(hwnd, hAccTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "FileOpenMenu";
    wc.lpszClassName = "FileOpenWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    RECT            Rect;

    hInst = hInstance;

    hAccTable = LoadAccelerators(hInst, "FileOpenAcc");

    hwnd = CreateWindow(
        "FileOpenWClass",
        "FileOpen Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

    GetClientRect(hwnd, (LPRECT) &Rect);

    /* Create a child window */

    hEditWnd = CreateWindow("Edit",
        NULL,
        WS_CHILD | WS_VISIBLE |
        ES_MULTILINE |
        WS_VSCROLL | WS_HSCROLL |
        ES_AUTOHSCROLL | ES_AUTOVSCROLL,
        0,
        0,
        (Rect.right-Rect.left),
        (Rect.bottom-Rect.top),
        hwnd,
        IDC_EDIT,                          /* Child control i.d. */
        hInst,
        NULL);

    if (!hEditWnd) {
        DestroyWindow(hwnd);
        return (NULL);
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

        WM_COMMAND    - application menu (About dialog box)
        WM_DESTROY    - destroy window

    COMMENTS:

        WM_COMMAND processing:

            IDM_OPEN - query to save current file if there is one and it
                       has been changed, open a new file.

            IDM_ABOUT - display "About" box.

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout, lpOpenDlg, lpSaveAsDlg;

    int Success;                            /* return value from SaveAsDlg()
    int IOStatus;                           /* result of file i/o      */
    int Return;

    switch (message) {
        case WM_COMMAND:
            switch (wParam) {
                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance(About, hInst);
                    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
                    FreeProcInstance(lpProcAbout);
                    break;

                case IDM_OPEN:
                    /* Call OpenDlg() to get the filename */

                    lpOpenDlg = MakeProcInstance((FARPROC) OpenDlg, hInst);
                    Return = DialogBox(hInst, "Open", hWnd, lpOpenDlg);
                    FreeProcInstance(lpOpenDlg);

                    /* Let user know that "open" functionality is not yet
                       implemented */
                    if (Return)
                       MessageBox (
                             GetFocus(),
                             "Command not implemented",
                             "FileOpen Sample Application",
                             MB_ICONASTERISK | MB_OK);
                    break;

                case IDM_NEW:
                case IDM_SAVE:
                case IDM_SAVEAS:
                case IDM_PRINT:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "FileOpen Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;

                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;

                /* edit menu commands */

                case IDM_UNDO:
                case IDM_CUT:
                case IDM_COPY:
                case IDM_PASTE:
                case IDM_CLEAR:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "FileOpen Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;

                case IDC_EDIT:
                    if (HIWORD (lParam) == EN_ERRSPACE) {
                        MessageBox (
                              GetFocus ()
                            , "Out of memory."
                            , "FileOpen Sample Application"
                            , MB_ICONHAND | MB_OK
                        );
                    }
                    break;

            }
            break;

        case WM_SETFOCUS:
            SetFocus (hEditWnd);
            break;

        case WM_SIZE:
            MoveWindow(hEditWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}

/****************************************************************************

    FUNCTION: OpenDlg(HWND, unsigned, WORD, LONG)

    PURPOSE: Let user select a file, and return.  Open code not provided.

****************************************************************************/

HANDLE FAR PASCAL OpenDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    WORD index;
    PSTR pTptr;
    HANDLE hFile=1;     /* Temp value for return */

    switch (message) {
        case WM_COMMAND:
            switch (wParam) {

                case IDC_LISTBOX:
                    switch (HIWORD(lParam)) {

                        case LBN_SELCHANGE:
                            /* If item is a directory name, append "*.*" */
                            if (DlgDirSelect(hDlg, str, IDC_LISTBOX))
                                strcat(str, DefSpec);

                            SetDlgItemText(hDlg, IDC_EDIT, str);
                            SendDlgItemMessage(hDlg,
                                IDC_EDIT,
                                EM_SETSEL,
                                NULL,
                                MAKELONG(0, 0x7fff));
                            break;

                        case LBN_DBLCLK:
                            goto openfile;
                    }
                    return (TRUE);

                case IDOK:
openfile:
                    GetDlgItemText(hDlg, IDC_EDIT, OpenName, 128);
                    if (strchr(OpenName, '*') || strchr(OpenName, '?')) {
                        SeparateFile(hDlg, (LPSTR) str, (LPSTR) DefSpec,
                            (LPSTR) OpenName);
                        if (str[0])
                            strcpy(DefPath, str);
                        ChangeDefExt(DefExt, DefSpec);
                        UpdateListBox(hDlg);
                        return (TRUE);
                    }

                    if (!OpenName[0]) {
                        MessageBox(hDlg, "No filename specified.",
                            NULL, MB_OK | MB_ICONHAND);
                        return (TRUE);
                    }

                    AddExt(OpenName, DefExt);

                    /* The routine to open the file would go here, and the */
                    /* file handle would be returned instead of NULL.
                    EndDialog(hDlg, hFile);
                    return (TRUE);

                case IDCANCEL:
                    EndDialog(hDlg, NULL);
                    return (FALSE);
            }
            break;

        case WM_INITDIALOG:                        /* message: initialize
            UpdateListBox(hDlg);
            SetDlgItemText(hDlg, IDC_EDIT, DefSpec);
            SendDlgItemMessage(hDlg,               /* dialog handle      */
                IDC_EDIT,                          /* where to send message
                EM_SETSEL,                         /* select characters
                NULL,                              /* additional information
                MAKELONG(0, 0x7fff));              /* entire contents      */
            SetFocus(GetDlgItem(hDlg, IDC_EDIT));
            return (FALSE); /* Indicates the focus is set to a control */
    }
    return FALSE;
}

/****************************************************************************

    FUNCTION: UpdateListBox(HWND);

    PURPOSE: Update the list box of OpenDlg

****************************************************************************/

void UpdateListBox(hDlg)
HWND hDlg;
{
    strcpy(str, DefPath);
    strcat(str, DefSpec);
    DlgDirList(hDlg, str, IDC_LISTBOX, IDC_PATH, 0x4010);

    /* To ensure that the listing is made for a subdir. of
     * current drive dir...
     */
    if (!strchr (DefPath, ':'))
  DlgDirList(hDlg, DefSpec, IDC_LISTBOX, IDC_PATH, 0x4010);

    /* Remove the '..' character from path if it exists, since this
     * will make DlgDirList move us up an additional level in the tree
     * when UpdateListBox() is called again.
     */
    if (strstr (DefPath, ".."))
  DefPath[0] = '\0';

    SetDlgItemText(hDlg, IDC_EDIT, DefSpec);
}

/****************************************************************************

    FUNCTION: ChangeDefExt(PSTR, PSTR);

    PURPOSE: Change the default extension

****************************************************************************/

void ChangeDefExt(Ext, Name)
PSTR Ext, Name;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr)
        if (!strchr(pTptr, '*') && !strchr(pTptr, '?'))
            strcpy(Ext, pTptr);
}

/****************************************************************************

    FUNCTION: SeparateFile(HWND, LPSTR, LPSTR, LPSTR)

    PURPOSE: Separate filename and pathname

****************************************************************************/

void SeparateFile(hDlg, lpDestPath, lpDestFileName, lpSrcFileName)
HWND hDlg;
LPSTR lpDestPath, lpDestFileName, lpSrcFileName;
{
    LPSTR lpTmp;
    char  cTmp;

    lpTmp = lpSrcFileName + (long) lstrlen(lpSrcFileName);
    while (*lpTmp != ':' && *lpTmp != '\\' && lpTmp > lpSrcFileName)
        lpTmp = AnsiPrev(lpSrcFileName, lpTmp);
    if (*lpTmp != ':' && *lpTmp != '\\') {
        lstrcpy(lpDestFileName, lpSrcFileName);
        lpDestPath[0] = 0;
        return;
    }
    lstrcpy(lpDestFileName, lpTmp + 1);
    cTmp = *(lpTmp + 1);
    lstrcpy(lpDestPath, lpSrcFileName);
     *(lpTmp + 1) = cTmp;
    lpDestPath[(lpTmp - lpSrcFileName) + 1] = 0;
}

/****************************************************************************

    FUNCTION: AddExt(PSTR, PSTR);

    PURPOSE: Add default extension

/***************************************************************************/

void AddExt(Name, Ext)
PSTR Name, Ext;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr != '.')
        strcat(Name, Ext);
}

/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
        case WM_INITDIALOG:
            return (TRUE);

        case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
                EndDialog(hDlg, TRUE);
                return (TRUE);
            }
            break;
    }
    return (FALSE);
}


GENERIC.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\GENERIC\GENERIC.C

/****************************************************************************

    PROGRAM: Generic.c

    PURPOSE: Generic template for Windows applications

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box

    COMMENTS:

        Windows can have several copies of your application running at the
        same time.  The variable hInst keeps track of which instance this
        application is so that processing will be to the correct window.

****************************************************************************/

 "windows.h"        /* required for all Windows applications */
 "generic.h"        /* specific to this program         */

HANDLE hInst;          /* current instance           */

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

    COMMENTS:

        Windows recognizes this function by name as the initial entry point
        for the program.  This function calls the application initialization
        routine, if no other instance of the program is running, and always
        calls the instance initialization routine.  It then executes a messag
        retrieval and dispatch loop that is the top-level control structure
        for the remainder of execution.  The loop is terminated when a WM_QUI
        message is received, at which time this function exits the applicatio
        instance by returning the value passed by PostQuitMessage().

        If this function must abort before entering the message loop, it
        returns the conventional value NULL.

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;           /* current instance       */
HANDLE hPrevInstance;           /* previous instance       */
LPSTR lpCmdLine;           /* command line         */
int nCmdShow;             /* show-window type (open/icon) */
{
    MSG msg;             /* message           */

    if (!hPrevInstance)       /* Other instances of app running? */
  if (!InitApplication(hInstance)) /* Initialize shared things */
      return (FALSE);     /* Exits if unable to initialize     */

    /* Perform initializations that apply to a specific instance */

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    /* Acquire and dispatch messages until a WM_QUIT message is received. */

    while (GetMessage(&msg,     /* message structure           */
      NULL,       /* handle of window receiving the message */
      NULL,       /* lowest message to examine         */
      NULL))       /* highest message to examine       */
  {
  TranslateMessage(&msg);     /* Translates virtual key codes       */
  DispatchMessage(&msg);     /* Dispatches message to window       */
    }
    return (msg.wParam);     /* Returns the value from PostQuitMessage */
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

    COMMENTS:

        This function is called at initialization time only if no other
        instances of the application are running.  This function performs
        initialization tasks that can be done once for any number of running
        instances.

        In this case, we initialize a window class by filling out a data
        structure of type WNDCLASS and calling the Windows RegisterClass()
        function.  Since all instances of this application use the same windo
        class, we only need to do this when the first instance is initialized


****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;             /* current instance       */
{
    WNDCLASS  wc;

    /* Fill in window class structure with parameters that describe the
    /* main window.

    wc.style = NULL;                    /* Class style(s).
    wc.lpfnWndProc = MainWndProc;       /* Function to retrieve messages for
                                        /* windows of this class.
    wc.cbClsExtra = 0;                  /* No per-class extra data.
    wc.cbWndExtra = 0;                  /* No per-window extra data.
    wc.hInstance = hInstance;           /* Application that owns the class.
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "GenericMenu";   /* Name of menu resource in .RC file.
    wc.lpszClassName = "GenericWClass"; /* Name used in call to CreateWindow.

    /* Register the window class and return success/failure code. */

    return (RegisterClass(&wc));

}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

    COMMENTS:

        This function is called at initialization time for every instance of
        this application.  This function performs initialization tasks that
        cannot be shared by multiple instances.

        In this case, we save the instance handle in a static variable and
        create and display the main program window.

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;          /* Current instance identifier.
    int             nCmdShow;           /* Param for first ShowWindow() call.
{
    HWND            hWnd;               /* Main window handle.

    /* Save the instance handle in static variable, which will be used in  */
    /* many subsequence calls from this application to Windows.            */

    hInst = hInstance;

    /* Create a main window for this application instance.  */

    hWnd = CreateWindow(
        "GenericWClass",                /* See RegisterClass() call.
        "Generic Sample Application",   /* Text for window title bar.
        WS_OVERLAPPEDWINDOW,            /* Window style.
        CW_USEDEFAULT,                  /* Default horizontal position.
        CW_USEDEFAULT,                  /* Default vertical position.
        CW_USEDEFAULT,                  /* Default width.
        CW_USEDEFAULT,                  /* Default height.
        NULL,                           /* Overlapped windows have no parent.
        NULL,                           /* Use the window class menu.
        hInstance,                      /* This instance owns this window.
        NULL                            /* Pointer not needed.
    );

    /* If window could not be created, return "failure" */

    if (!hWnd)
        return (FALSE);

    /* Make the window visible; update its client area; and return "success"

    ShowWindow(hWnd, nCmdShow);  /* Show the window                        */
    UpdateWindow(hWnd);          /* Sends WM_PAINT message                 */
    return (TRUE);               /* Returns the value from PostQuitMessage */

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND    - application menu (About dialog box)
  WM_DESTROY    - destroy window

    COMMENTS:

  To process the IDM_ABOUT message, call MakeProcInstance() to get the
  current instance address of the About() function.  Then call Dialog
  box which will create the box according to the information in your
  generic.rc file and turn control over to the About() function.  When
  it returns, free the intance address.

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;          /* window handle         */
unsigned message;        /* type of message         */
WORD wParam;          /* additional information       */
LONG lParam;          /* additional information       */
{
    FARPROC lpProcAbout;      /* pointer to the "About" function */

    switch (message) {
  case WM_COMMAND:     /* message: command from application menu */
      if (wParam == IDM_ABOUT) {
    lpProcAbout = MakeProcInstance(About, hInst);

    DialogBox(hInst,     /* current instance       */
        "AboutBox",       /* resource to use       */
        hWnd,       /* parent handle       */
        lpProcAbout);     /* About() instance address */

    FreeProcInstance(lpProcAbout);
    break;
      }
      else          /* Lets Windows process it       */
    return (DefWindowProc(hWnd, message, wParam, lParam));

  case WM_DESTROY:      /* message: window being destroyed */
      PostQuitMessage(0);
      break;

  default:        /* Passes it on if unproccessed    */
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

    COMMENTS:

  No initialization is needed for this particular dialog box, but TRUE
  must be returned to Windows.

  Wait for user to click on "Ok" button, then close the dialog box.

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;                                /* window handle of the dialog box
unsigned message;                         /* type of message
WORD wParam;                              /* message-specific information
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:       /* message: initialize dialog box */
      return (TRUE);

  case WM_COMMAND:          /* message: received a command */
      if (wParam == IDOK                /* "OK" box selected?       */
                || wParam == IDCANCEL) {      /* System menu close command? *
    EndDialog(hDlg, TRUE);        /* Exits the dialog box       */
    return (TRUE);
      }
      break;
    }
    return (FALSE);            /* Didn't process a message    */
}


HELPEX.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\HELPEX\HELPEX.C

/****************************************************************************

   PROGRAM:    HelpEx.c

   PURPOSE:    Illustrates calls to WinHelp and context-sensitive help.
               HelpEx loads library MenuHook, which detects F1 keystrokes in
               the HelpEx application menus.

   FUNCTIONS:

   WinMain() - Calls initialization function, processes message loop.
   InitApplication() - Initializes window data and registers window class.
   InitInstance() - Saves instance handle and creates main window.
   MainWndProc() - Processes window messages.
   About() - Processes messages for "About" dialog box.
   MakeHelpPathName() - Derives path name of help file.

****************************************************************************/

#include <windows.h>
#include "helpex.h"
#include "helpids.h"

HWND       hWnd;               /* Handle to main window */
HANDLE     hInst;              /* Handle to instance data*/
BOOL       bHelp = FALSE;      /* Help mode flag; TRUE = "ON"*/
HCURSOR    hHelpCursor;        /* Cursor displayed when in help mode*/
char       szHelpFileName[EXE_NAME_MAX_SIZE+1];    /* Help file name*/
HANDLE     hAccTable;                              /* handle to accelerator t

void MakeHelpPathName(char*);  /* Function deriving help file path */


/****************************************************************************

   FUNCTION:   WinMain(HANDLE, HANDLE, LPSTR, int)

   PURPOSE:    Calls initialization function, processes message loop.

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR  lpCmdLine;
int    nCmdShow;
{
   MSG msg;

   if (!hPrevInstance)
       if (!InitApplication(hInstance))
       return (FALSE);

   if (!InitInstance(hInstance, nCmdShow))
       return (FALSE);

   while (GetMessage(&msg, NULL, NULL, NULL)) {

      /* Only translate message if it is not an accelerator message */
      if (!TranslateAccelerator(hWnd, hAccTable, &msg)) {

          TranslateMessage(&msg);
          DispatchMessage(&msg);
      }
   }
   return (msg.wParam);
}


/****************************************************************************

   FUNCTION:   InitApplication(HANDLE)

   PURPOSE:    Initializes window data and registers window class.

   RETURNS:    Status of RegisterClass() call.

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
   WNDCLASS wc;

   wc.style = NULL;
   wc.lpfnWndProc = MainWndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = GetStockObject(WHITE_BRUSH);
   wc.lpszMenuName ="HelpexMenu";
   wc.lpszClassName = "HelpexWClass";

   return (RegisterClass(&wc));
}


/****************************************************************************

   FUNCTION:   InitInstance(HANDLE, int)

   PURPOSE:    Saves instance handle in global variable and creates main
               window.

   RETURNS:    Status of CreateWindow() call.

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
HANDLE hInstance;
int    nCmdShow;
{

   hInst = hInstance;

   hAccTable = LoadAccelerators(hInst, "HelpexAcc");

   hWnd = CreateWindow(
       "HelpexWClass",
       "Help Example ",
       WS_OVERLAPPEDWINDOW,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       NULL,
       NULL,
       hInstance,
       NULL
       );

   if (!hWnd)
       return (FALSE);

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   EnableMenuItem(GetSubMenu(GetMenu(hWnd), 1), IDM_CLEAR, MF_ENABLED);

   MakeHelpPathName(szHelpFileName);
   hHelpCursor = LoadCursor(hInst,"HelpCursor");

   return (TRUE);

}

/****************************************************************************

   FUNCTION:   MainWndProc(HWND, unsigned, WORD, LONG)

   PURPOSE:    Processes window messages.

   MESSAGES:

       WM_COMMAND- Application menu item
       WM_DESTROY- Destroy window

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND       hWnd;
unsigned   message;
WORD       wParam;
LONG       lParam;
{
   FARPROC lpProcAbout;
   DWORD   dwHelpContextId;
   RECT    rect;
   POINT   pt;
   DWORD   dword;
   WORD    wFormat;
   HCURSOR hArrow;

   switch (message) {

       case WM_COMMAND:
           /* Was F1 just pressed in a menu, or are we in help mode */
           /* (Shift-F1)? */

           if (bHelp) {
               dwHelpContextId =
                   (wParam == IDM_NEW)    ? (DWORD) HELPID_FILE_NEW     :
                   (wParam == IDM_OPEN)   ? (DWORD) HELPID_FILE_OPEN    :
                   (wParam == IDM_SAVE)   ? (DWORD) HELPID_FILE_SAVE    :
                   (wParam == IDM_SAVEAS) ? (DWORD) HELPID_FILE_SAVE_AS :
                   (wParam == IDM_PRINT)  ? (DWORD) HELPID_FILE_PRINT   :
                   (wParam == IDM_EXIT)   ? (DWORD) HELPID_FILE_EXIT    :
                   (wParam == IDM_UNDO)   ? (DWORD) HELPID_EDIT_UNDO    :
                   (wParam == IDM_CUT)    ? (DWORD) HELPID_EDIT_CUT     :
                   (wParam == IDM_CLEAR)  ? (DWORD) HELPID_EDIT_CLEAR   :
                   (wParam == IDM_COPY)   ? (DWORD) HELPID_EDIT_COPY    :
                   (wParam == IDM_PASTE)  ? (DWORD) HELPID_EDIT_PASTE   :
                                            (DWORD) 0L;

               if (!dwHelpContextId)
         {
                   MessageBox( hWnd, "Help not available for Help Menu item",
             "Help Example", MB_OK                          );
                   return (DefWindowProc(hWnd, message, wParam, lParam));
         }

               bHelp = FALSE;
               WinHelp(hWnd,szHelpFileName,HELP_CONTEXT,dwHelpContextId);
               break;
           }

           switch (wParam) {
               case IDM_NEW:
               case IDM_OPEN:
               case IDM_SAVE:
               case IDM_SAVEAS:
               case IDM_PRINT:
               case IDM_UNDO:
               case IDM_CUT:
               case IDM_CLEAR:
               case IDM_COPY:
               case IDM_PASTE:
                   MessageBox(hWnd,
                   "Command not implemented",
                   "Help Example",
                   MB_OK);
                   break;

               case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;

               case IDM_HELP_INDEX:
                   WinHelp(hWnd,szHelpFileName,HELP_INDEX,0L);
                   break;

               case IDM_HELP_KEYBOARD:
       WinHelp(hWnd,szHelpFileName,HELP_KEY,(DWORD)(LPSTR)"keys");
                   break;

               case IDM_HELP_HELP:
       WinHelp(hWnd,"WINHELP.HLP",HELP_INDEX,0L);
                   break;

               case IDM_ABOUT:
                   lpProcAbout = MakeProcInstance(About, hInst);
                   DialogBox(hInst,
                       "AboutBox",
                       hWnd,
                       lpProcAbout);
                   FreeProcInstance(lpProcAbout);
                   break;

               default:
                   return (DefWindowProc(hWnd, message, wParam, lParam));
           }

           break;

       case WM_LBUTTONDOWN:
     if (bHelp)
     {
         bHelp = FALSE;
         WinHelp( hWnd, szHelpFileName, HELP_CONTEXT,
      (DWORD) HELPID_EDIT_WINDOW          );
         break;
     }

           return (DefWindowProc(hWnd, message, wParam, lParam));

       case WM_NCLBUTTONDOWN:
           /* If we are in help mode (Shift-F1) then display context- */
           /* sensitive help for non-client area. */

           if (bHelp) {
               dwHelpContextId =
                   (wParam == HTCAPTION)     ? (DWORD) HELPID_TITLE_BAR     :
                   (wParam == HTREDUCE)      ? (DWORD) HELPID_MINIMIZE_ICON :
                   (wParam == HTZOOM)        ? (DWORD) HELPID_MAXIMIZE_ICON :
                   (wParam == HTSYSMENU)     ? (DWORD) HELPID_SYSTEM_MENU   :
                   (wParam == HTBOTTOM)      ? (DWORD) HELPID_SIZING_BORDER :
                   (wParam == HTBOTTOMLEFT)  ? (DWORD) HELPID_SIZING_BORDER :
                   (wParam == HTBOTTOMRIGHT) ? (DWORD) HELPID_SIZING_BORDER :
                   (wParam == HTTOP)         ? (DWORD) HELPID_SIZING_BORDER :
                   (wParam == HTLEFT)        ? (DWORD) HELPID_SIZING_BORDER :
                   (wParam == HTRIGHT)       ? (DWORD) HELPID_SIZING_BORDER :
                   (wParam == HTTOPLEFT)     ? (DWORD) HELPID_SIZING_BORDER :
                   (wParam == HTTOPRIGHT)    ? (DWORD) HELPID_SIZING_BORDER :
                                               (DWORD) 0L;

               if (!((BOOL)dwHelpContextId))
                   return (DefWindowProc(hWnd, message, wParam, lParam));

               bHelp = FALSE;
               WinHelp(hWnd,szHelpFileName,HELP_CONTEXT,dwHelpContextId);
               break;
           }

           return (DefWindowProc(hWnd, message, wParam, lParam));

       case WM_KEYDOWN:
           if (wParam == VK_F1) {

               /* If Shift-F1, turn help mode on and set help cursor */

               if (GetKeyState(VK_SHIFT)<0) {
                   bHelp = TRUE;
                   SetCursor(hHelpCursor);
                   return (DefWindowProc(hWnd, message, wParam, lParam));
               }

               /* If F1 without shift, then call up help main index topic */
               else {
                   WinHelp(hWnd,szHelpFileName,HELP_INDEX,0L);
               }
           }

           else if (wParam == VK_ESCAPE && bHelp) {

               /* Escape during help mode: turn help mode off */
               bHelp = FALSE;
               SetCursor((HCURSOR)GetClassWord(hWnd,GCW_HCURSOR));
           }

           break;

       case WM_SETCURSOR:
           /* In help mode it is necessary to reset the cursor in response */
           /* to every WM_SETCURSOR message.Otherwise, by default, Windows */
           /* will reset the cursor to that of the window class. */

           if (bHelp) {
               SetCursor(hHelpCursor);
               break;
           }
           return (DefWindowProc(hWnd, message, wParam, lParam));
           break;

       case WM_INITMENU:
           if (bHelp) {
               SetCursor(hHelpCursor);
           }
           return (TRUE);

       case WM_ENTERIDLE:
           if ((wParam == MSGF_MENU) && (GetKeyState(VK_F1) & 0x8000)) {
               bHelp = TRUE;
               PostMessage(hWnd, WM_KEYDOWN, VK_RETURN, 0L);
           }
           break;

       case WM_DESTROY:
           WinHelp(hWnd,szHelpFileName,HELP_QUIT,0L);
           PostQuitMessage(0);
           break;

       default:
           return (DefWindowProc(hWnd, message, wParam, lParam));
   }

   return (NULL);
}


/****************************************************************************

   FUNCTION:   About(HWND, unsigned, WORD, LONG)

   PURPOSE:    Processes messages for "About" dialog box

   MESSAGES:

       WM_INITDIALOG - Initialize dialog box
       WM_COMMAND- Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND       hDlg;
unsigned   message;
WORD       wParam;
LONG       lParam;
{
   switch (message) {
       case WM_INITDIALOG:
           return (TRUE);

       case WM_COMMAND:
           if (wParam == IDOK) {
               EndDialog(hDlg, TRUE);
               return (TRUE);
           }
           break;
   }

   return (FALSE);
}


/****************************************************************************

   FUNCTION:   MakeHelpPathName

   PURPOSE:    HelpEx assumes that the .HLP help file is in the same
               directory as the HelpEx executable.This function derives
               the full path name of the help file from the path of the
               executable.

****************************************************************************/

void MakeHelpPathName(szFileName)
char * szFileName;
{
   char *  pcFileName;
   int     nFileNameLen;

   nFileNameLen = GetModuleFileName(hInst,szFileName,EXE_NAME_MAX_SIZE);
   pcFileName = szFileName + nFileNameLen;

   while (pcFileName > szFileName) {
       if (*pcFileName == '\\' || *pcFileName == ':') {
           *(++pcFileName) = '\0';
           break;
       }
   nFileNameLen--;
   pcFileName--;
   }

   if ((nFileNameLen+13) < EXE_NAME_MAX_SIZE) {
       lstrcat(szFileName, "helpex.hlp");
   }

   else {
       lstrcat(szFileName, "?");
   }

   return;
}


ICON.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\ICON\ICON.C

/****************************************************************************

    PROGRAM: Icon.c

    PURPOSE: Demonstrates using an icon for the About box and minimize box

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box

****************************************************************************/

#include "windows.h"
#include "icon.h"

HANDLE hInst;

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(hInstance, "MyIcon");           /* loads icon */
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "IconMenu";
    wc.lpszClassName = "IconWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    HWND            hWnd;

    hInst = hInstance;

    hWnd = CreateWindow(
        "IconWClass",
        "Icon Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hWnd)
        return (FALSE);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND    - application menu (About dialog box)
  WM_DESTROY    - destroy window

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;

    switch (message) {
  case WM_COMMAND:
      if (wParam == IDM_ABOUT) {
    lpProcAbout = MakeProcInstance(About, hInst);

    DialogBox(hInst,
        "AboutBox",
        hWnd,
        lpProcAbout);

    FreeProcInstance(lpProcAbout);
    break;
      }
      else
    return (DefWindowProc(hWnd, message, wParam, lParam));

  case WM_DESTROY:
      PostQuitMessage(0);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


INPUT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\INPUT\INPUT.C

/****************************************************************************

    PROGRAM: Input.c

    PURPOSE: Input template for Windows applications

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box

****************************************************************************/

#include "windows.h"
#include "input.h"

HANDLE hInst;

char MouseText[48];                                   /* mouse state
char ButtonText[48];                                  /* mouse-button state
char KeyboardText[48];                                /* keyboard state
char CharacterText[48];                               /* latest character
char ScrollText[48];                                  /* scroll status
char TimerText[48];                                   /* timer state
RECT rectMouse;
RECT rectButton;
RECT rectKeyboard;
RECT rectCharacter;
RECT rectScroll;
RECT rectTimer;
int idTimer;                                          /* timer ID
int nTimerCount = 0;                                  /* current timer count

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = CS_DBLCLKS;          /* double-click messages */
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "InputMenu";
    wc.lpszClassName = "InputWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    HWND            hWnd;
    HDC             hDC;
    TEXTMETRIC      textmetric;
    RECT            rect;
    int             nLineHeight;


    hInst = hInstance;

    hWnd = CreateWindow(
        "InputWClass",
        "Input Sample Application",
        WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,  /* horz & vert scroll
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hWnd)
        return (FALSE);


    hDC = GetDC(hWnd);
    GetTextMetrics(hDC, &textmetric);
    ReleaseDC(hWnd, hDC);
    nLineHeight = textmetric.tmExternalLeading + textmetric.tmHeight;

    rect.left   = GetDeviceCaps(hDC, LOGPIXELSX) / 4;   /* 1/4 inch */
    rect.right  = GetDeviceCaps(hDC, HORZRES);
    rect.top    = GetDeviceCaps(hDC, LOGPIXELSY) / 4;   /* 1/4 inch */
    rect.bottom = rect.top + nLineHeight;
    rectMouse   = rect;

    rect.top += nLineHeight;
    rect.bottom += nLineHeight;
    rectButton = rect;

    rect.top += nLineHeight;
    rect.bottom += nLineHeight;
    rectKeyboard = rect;

    rect.top += nLineHeight;
    rect.bottom += nLineHeight;
    rectCharacter = rect;

    rect.top += nLineHeight;
    rect.bottom += nLineHeight;
    rectScroll = rect;

    rect.top += nLineHeight;
    rect.bottom += nLineHeight;
    rectTimer = rect;

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND    - application menu (About dialog box)
  WM_CREATE     - create window
        WM_MOUSEMOVE  - mouse movement
        WM_LBUTTONDOWN - left mouse button pressed
        WM_LBUTTONUP  - left mouse button released
        WM_LBUTTONDBLCLK - left mouse button double clicked
        WM_KEYDOWN    - key pressed
        WM_KEYUP      - key released
        WM_CHAR       - ASCII character received
        WM_TIMER      - timer has elapsed
        WM_HSCROLL    - mouse click in horizontal scroll bar
        WM_VSCROLL    - mouse click in vertical scroll bar
        WM_PAINT      - update window, draw objects
        WM_DESTROY    - destroy window

    COMMENTS:

        This demonstrates how input messages are received, and what the
        additional information is that comes with the message.

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;

    HDC hDC;                         /* display-context variable     */
    PAINTSTRUCT ps;                  /* paint structure              */
    char HorzOrVertText[12];
    char ScrollTypeText[20];
    RECT rect;

    switch (message) {
  case WM_COMMAND:
      if (wParam == IDM_ABOUT) {
    lpProcAbout = MakeProcInstance(About, hInst);

    DialogBox(hInst,
        "AboutBox",
        hWnd,
        lpProcAbout);

    FreeProcInstance(lpProcAbout);
    break;
      }
      else
    return (DefWindowProc(hWnd, message, wParam, lParam));

        case WM_CREATE:

            /* Set the timer for five-second intervals */

            idTimer =  SetTimer(hWnd, NULL, 5000, (FARPROC) NULL);

            break;

        case WM_MOUSEMOVE:
            wsprintf(MouseText, "WM_MOUSEMOVE: %x, %d, %d",
                wParam, LOWORD(lParam), HIWORD(lParam));
            InvalidateRect(hWnd, &rectMouse, TRUE);
            break;

        case WM_LBUTTONDOWN:
            wsprintf(ButtonText, "WM_LBUTTONDOWN: %x, %d, %d",
                wParam, LOWORD(lParam), HIWORD(lParam));
            InvalidateRect(hWnd, &rectButton, TRUE);
            break;

        case WM_LBUTTONUP:
            wsprintf(ButtonText, "WM_LBUTTONUP: %x, %d, %d",
                wParam, LOWORD(lParam), HIWORD(lParam));
            InvalidateRect(hWnd, &rectButton, TRUE);
            break;

        case WM_LBUTTONDBLCLK:
            wsprintf(ButtonText, "WM_LBUTTONDBLCLK: %x, %d, %d",
                wParam, LOWORD(lParam), HIWORD(lParam));
            InvalidateRect(hWnd, &rectButton, TRUE);
            break;

        case WM_KEYDOWN:
            wsprintf(KeyboardText, "WM_KEYDOWN: %x, %x, %x",
                wParam, LOWORD(lParam), HIWORD(lParam));
            InvalidateRect(hWnd, &rectKeyboard, TRUE);
            break;

        case WM_KEYUP:
            wsprintf(KeyboardText, "WM_KEYUP: %x, %x, %x",
                wParam, LOWORD(lParam), HIWORD(lParam));
            InvalidateRect(hWnd, &rectKeyboard, TRUE);
            break;

        case WM_CHAR:
            wsprintf(CharacterText, "WM_CHAR: %c, %x, %x",
                wParam, LOWORD(lParam), HIWORD(lParam));
            InvalidateRect(hWnd, &rectCharacter, TRUE);
            break;

        case WM_TIMER:
            wsprintf(TimerText, "WM_TIMER: %d seconds",
                nTimerCount += 5);
            InvalidateRect(hWnd, &rectTimer, TRUE);
            break;

        case WM_HSCROLL:
        case WM_VSCROLL:
            strcpy(HorzOrVertText,
                (message == WM_HSCROLL) ? "WM_HSCROLL" : "WM_VSCROLL");
            strcpy(ScrollTypeText,
                (wParam == SB_LINEUP) ? "SB_LINEUP" :
                (wParam == SB_LINEDOWN) ? "SB_LINEDOWN" :
                (wParam == SB_PAGEUP) ? "SB_PAGEUP" :
                (wParam == SB_PAGEDOWN) ? "SB_PAGEDOWN" :
                (wParam == SB_THUMBPOSITION) ? "SB_THUMBPOSITION" :
                (wParam == SB_THUMBTRACK) ? "SB_THUMBTRACK" :
                (wParam == SB_ENDSCROLL) ? "SB_ENDSCROLL" : "unknown");
            wsprintf(ScrollText, "%s: %s, %x, %x",
                (LPSTR)HorzOrVertText,
                (LPSTR)ScrollTypeText,
                LOWORD(lParam),
                HIWORD(lParam));
            InvalidateRect(hWnd, &rectScroll, TRUE);
            break;

        case WM_PAINT:
            hDC = BeginPaint (hWnd, &ps);

            if (IntersectRect(&rect, &rectMouse, &ps.rcPaint))
                TextOut(hDC, rectMouse.left, rectMouse.top,
                        MouseText, strlen(MouseText));
            if (IntersectRect(&rect, &rectButton, &ps.rcPaint))
                TextOut(hDC, rectButton.left, rectButton.top,
                        ButtonText, strlen(ButtonText));
            if (IntersectRect(&rect, &rectKeyboard, &ps.rcPaint))
                TextOut(hDC, rectKeyboard.left, rectKeyboard.top,
                        KeyboardText, strlen(KeyboardText));
            if (IntersectRect(&rect, &rectCharacter, &ps.rcPaint))
                TextOut(hDC, rectCharacter.left, rectCharacter.top,
                        CharacterText, strlen(CharacterText));
            if (IntersectRect(&rect, &rectTimer, &ps.rcPaint))
                TextOut(hDC, rectTimer.left, rectTimer.top,
                        TimerText, strlen(TimerText));
            if (IntersectRect(&rect, &rectScroll, &ps.rcPaint))
                TextOut(hDC, rectScroll.left, rectScroll.top,
                        ScrollText, strlen(ScrollText));

            EndPaint(hWnd, &ps);
            break;

        case WM_DESTROY:
            KillTimer(hWnd, idTimer);                     /* Stops the timer
            PostQuitMessage(0);
            break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


MEMORY1.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MEMORY\MEMORY1.C

#include "windows.h"
#include "memory.h"

HANDLE hInst;

/****************************************************************************
    MODULE:  memory1.c

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
    }
    return (msg.wParam);
}


MEMORY2.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MEMORY\MEMORY2.C

#include "windows.h"
#include "memory.h"

/****************************************************************************
    MODULE:  memory2.c

    FUNCTION: MemoryInit(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "MemoryMenu";
    wc.lpszClassName = "MemoryWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************
    MODULE:  memory3.c


    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    HWND            hwnd;

    hInst = hInstance;

    hwnd = CreateWindow(
        "MemoryWClass",
        "Memory Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return (TRUE);

}


MEMORY3.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MEMORY\MEMORY3.C

#include "windows.h"
#include "memory.h"

/****************************************************************************
    MODULE:  memory3.c

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND    - application menu (About dialog box)
  WM_DESTROY    - destroy window

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;

    switch (message) {
  case WM_COMMAND:
      if (wParam == IDM_ABOUT) {
    lpProcAbout = MakeProcInstance(About, hInst);

    DialogBox(hInst,
        "AboutBox",
        hWnd,
        lpProcAbout);

    FreeProcInstance(lpProcAbout);
    break;
      }
      else
    return (DefWindowProc(hWnd, message, wParam, lParam));

  case WM_DESTROY:
      PostQuitMessage(0);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


MEMORY4.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MEMORY\MEMORY4.C

#include "windows.h"
#include "memory.h"

/****************************************************************************
    MODULE:  memory4.c

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


MENU.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MENU\MENU.C

/***************************************************************************
 *                     *
 *  PROGRAM  : Menu.c               *
 *                     *
 *  PURPOSE  : To give a demonstration of the use of popup menus, user  *
 *      defined menus and menu functions.         *
 *                     *
 *  FUNCTIONS  : WinMain()        - Calls the initialization function  *
 *          and enters the message loop.     *
 *                     *
 *      MenuInit()        - Registers the app. window class.   *
 *                     *
 *      About()        - Dialog function for the About..    *
 *          dialog.          *
 *                     *
 *      ShrinkBitmap()      - Shrinks a 64x64 bitmap to a size   *
 *          useable for a user-defined menu    *
 *          checkmark.         *
 *                     *
 *      HandleCreate()      - Creates a new menu and appends it  *
 *          to the main menu       *
 *                     *
 *      HandlePaint()       - Handles repainting the app's client*
 *          area           *
 *                     *
 *      HandleChangeColors()- Changes the state of the "colors"  *
 *          menu item.         *
 *                     *
 *      HandleDrawItem()    - Redraws the menu items in the     *
 *          "colors" menu         *
 *                     *
 *      HandlePopupMenu()   - handles display of the "floating"  *
 *          popup.           *
 *                     *
 *      MenuWndProc()       - Window function for the app.     *
 *                     *
 *                     *
 ***************************************************************************/
#include "windows.h"
#include "menu.h"

HANDLE   hInst;
HMENU   hBigMenu;
HBITMAP  hbmCheckOn;
HBITMAP  hbmCheckOff;

/****************************************************************************
 *                      *
 *  FUNCTION   : WinMain(HANDLE, HANDLE, LPSTR, int)          *
 *                      *
 *  PURPOSE    : Creates the main app. window, calls an initialization      *
 *     function and enters the message loop.          *
 *                      *
 ****************************************************************************
int PASCAL WinMain (hInstance, hPrevInstance, lpCmdLine, nCmdShow)

HANDLE hInstance, hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;

{
    HWND  hWnd;
    MSG   msg;

    /* Register main window class if this is the first instance of the app. *
    if (!hPrevInstance)
  if (!MenuInit (hInstance))
      return NULL;

    hInst = hInstance;

    /* Create the app. window */
    hWnd = CreateWindow ("menu",
       "Menu Example",
       WS_OVERLAPPEDWINDOW,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       (HWND) NULL,
       NULL,
       hInstance,
       (LPSTR) NULL);

    if (!hWnd)
  return NULL;

    ShowWindow (hWnd, nCmdShow);
    UpdateWindow (hWnd);

    while (GetMessage (&msg, NULL, NULL, NULL)){
  /* Since we have no accelerators, no need to call
   * TranslateAccelerator here.
   */
  TranslateMessage (&msg);
  DispatchMessage (&msg);
    }
    return(msg.wParam);
}


/****************************************************************************
 *                      *
 *  FUNCTION   : MenuInit (hInstance)              *
 *                      *
 *  PURPOSE    : Registers the main window class.          *
 *                      *
 *  RETURNS    : TRUE  -  if RegisterClass() went off ok        *
 *     FALSE  -  otherwise.              *
 *                      *
 ****************************************************************************
BOOL NEAR PASCAL MenuInit (hInstance)

HANDLE hInstance;

{
    HANDLE    hMemory;
    PWNDCLASS pWndClass;
    BOOL      bSuccess;

    /* Initialize the menu window class */
    hMemory   = LocalAlloc(LPTR, sizeof(WNDCLASS));
    pWndClass = (PWNDCLASS) LocalLock(hMemory);

    pWndClass->style       = NULL;
    pWndClass->lpfnWndProc   = MenuWndProc;
    pWndClass->hInstance     = hInstance;
    pWndClass->hIcon       = LoadIcon (hInstance, "menu");
    pWndClass->hCursor       = LoadCursor (NULL, IDC_ARROW);
    pWndClass->hbrBackground = GetStockObject (WHITE_BRUSH);
    pWndClass->lpszMenuName  = (LPSTR) "MenuMenu",
    pWndClass->lpszClassName = (LPSTR) "menu";

    bSuccess = RegisterClass (pWndClass);
    LocalUnlock (hMemory);
    LocalFree (hMemory);

    return bSuccess;
}


/****************************************************************************
 *                      *
 *  FUNCTION   : About (hDlg, message, wParam, lParam)          *
 *                      *
 *  PURPOSE    : Dialog function for the About menu... dialog.        *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL About(hDlg, message, wParam, lParam)

HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;

{
    switch (message){
  case WM_INITDIALOG:
      return(TRUE);

  case WM_COMMAND:
      if (wParam == IDOK){
    EndDialog(hDlg,NULL);
    return(TRUE);
      }
      break;
    }
    return(FALSE);
}


/****************************************************************************
 *                      *
 *  FUNCTION   : ShrinkBitmap(hwnd, hbm)            *
 *                      *
 *  PURPOSE    : This function shrinks a 64x64 bitmap into a bitmap useable *
 *     for the user-defined checkmark for menu items. This can be *
 *     easily generalized to shrink bitmaps of any size.      *
 *                      *
 *  RETURNS    : HBITMAP - A handle to the new bitmap.          *
 *                      *
 ****************************************************************************
HBITMAP FAR PASCAL ShrinkBitmap (hwnd, hbm)

HWND hwnd;
HBITMAP hbm;

{
    HDC     hdc;
    HDC     hmemorydcNew;
    HDC     hmemorydcOld;
    LONG    checkMarkSize;
    HBITMAP hCheckBitmap;
    HBITMAP hOldBitmapSave;
    HBITMAP hNewBitmapSave;

    hdc = GetDC (hwnd);

    /* Create DCs for the source (old) and target (new) bitmaps */
    hmemorydcNew = CreateCompatibleDC (hdc);
    hmemorydcOld = CreateCompatibleDC (hdc);

    /* Determine the dimensions of the default menu checkmark and
     * create a target bitmap of the same dimensions
     */
    checkMarkSize = GetMenuCheckMarkDimensions ();
    hCheckBitmap  = CreateCompatibleBitmap (hdc,
              LOWORD (checkMarkSize),
              HIWORD (checkMarkSize));

    /* Select the source bitmap and the target bitmap into their
     * respective DCs.
     */
    hOldBitmapSave = SelectObject (hmemorydcNew, hCheckBitmap);
    hNewBitmapSave = SelectObject (hmemorydcOld, hbm);

    /* Shrink the source bitmap into the target DC */
    StretchBlt (hmemorydcNew,
    0,
    0,
    LOWORD(checkMarkSize),
    HIWORD(checkMarkSize),
    hmemorydcOld,
    0,
    0,
    64,
    64,
    SRCCOPY);

    /* De-select the bitmaps and clean up .. */
    SelectObject (hmemorydcNew, hOldBitmapSave);
    SelectObject (hmemorydcOld, hNewBitmapSave);
    DeleteDC (hmemorydcNew);
    DeleteDC (hmemorydcOld);
    ReleaseDC (hwnd, hdc);

    /* .. and return a handle to the target bitmap */
    return hCheckBitmap;
}


/****************************************************************************
 *                      *
 *  FUNCTION   : HandleCreate ( hwnd )              *
 *                      *
 *  PURPOSE    : Creates a new (empty) menu and appends to it the "State"   *
 *     menu items. It sets up the user-defined checkmarks for the *
 *     menu. It then inserts this menu into the main menu bar.    *
 *                      *
 ****************************************************************************
void FAR PASCAL HandleCreate (hwnd)
HWND hwnd;
{
    HMENU   hMenu;
    HMENU   hWndMenu;
    HBITMAP hbmOn;
    HBITMAP hbmOff;

    /* Create a new menu into the menubar on the fly */
    hMenu = CreateMenu ();
    if (!hMenu)
  return;

    /* Append the state menu items to it */
    AppendMenu (hMenu, MF_STRING, IDM_STATE1, "South Dakota");
    AppendMenu (hMenu, MF_STRING, IDM_STATE2, "Washington");
    AppendMenu (hMenu, MF_STRING, IDM_STATE3, "California");
    if (!AppendMenu (hMenu, MF_STRING, IDM_STATE4, "Oregon")){
  /* It is unlikely the other appends will fail and this will succeed.
   * So just check this one. And if it fails, Destroy the menu for
   * good measure and return.
   */
  DestroyMenu(hMenu);
  return;
    }
    hbmCheckOn  = ShrinkBitmap (hwnd, LoadBitmap (hInst, "checkon"));
    hbmCheckOff = ShrinkBitmap (hwnd, LoadBitmap (hInst, "checkoff"));

    /* Set up the user-defined check marks */
    SetMenuItemBitmaps (hMenu, 0, MF_BYPOSITION, hbmCheckOff, hbmCheckOn);
    SetMenuItemBitmaps (hMenu, 1, MF_BYPOSITION, hbmCheckOff, hbmCheckOn);
    SetMenuItemBitmaps (hMenu, 2, MF_BYPOSITION, hbmCheckOff, hbmCheckOn);
    SetMenuItemBitmaps (hMenu, 3, MF_BYPOSITION, hbmCheckOff, hbmCheckOn);

    /* Now insert the menu into the main menu bar. */
    hWndMenu = GetMenu (hwnd);
    InsertMenu (hWndMenu, 2, MF_POPUP|MF_BYPOSITION, (WORD)hMenu, "States");

    return;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : HandlePaint ( hwnd )              *
 *                      *
 *  PURPOSE    : Handles the repainting of the main app's client area.      *
 *                      *
 ****************************************************************************
void FAR PASCAL HandlePaint (hwnd)
HWND hwnd;
{
    HDC   hdc;
    PAINTSTRUCT ps;
    RECT  rc;

    hdc = BeginPaint (hwnd, (LPPAINTSTRUCT)&ps);

    /* Center the text in the client area */
    GetClientRect (hwnd, (LPRECT)&rc);
    DrawText (hdc,
        "Down click in the window for a popup menu",
         41,
         (LPRECT)&rc,
         DT_CENTER | DT_WORDBREAK);
    EndPaint(hwnd, (LPPAINTSTRUCT)&ps);
}


/****************************************************************************
 *                      *
 *  FUNCTION   : HandleChangeColors (hwnd)            *
 *                      *
 *  PURPOSE    : Toggles the state of the Owner Draw item in the Colors     *
 *     menu. If it is on, the "Black", "Blue", "Red", and "Green" *
 *               individual menu text items are modified so that they will  *
 *     contain bands of color. Otherwise, the colors are replaced *
 *     by the text.                                               *
 *                      *
 ****************************************************************************
void FAR PASCAL HandleChangeColors(hwnd)
HWND hwnd;
{
    HMENU hMenu;
    BOOL  fOwnerDraw;

    /* Get a handle to the Colors menu. This is at position 1. */
    hMenu = GetSubMenu (GetMenu (hwnd), IDCOLORS_POS);

    /* Get the current state of the item */
    fOwnerDraw = GetMenuState ( hMenu,
        IDM_COLOROWNERDR, MF_BYCOMMAND) & MF_CHECKED;

    /* Toggle the state of the item. */
    CheckMenuItem ( hMenu,
        IDM_COLOROWNERDR,
        MF_BYCOMMAND | (fOwnerDraw ? MF_UNCHECKED : MF_CHECKED));

    if (!fOwnerDraw){
  /* Change the items to owner-draw items. Pass the RGB value for the
   * color as the application-supplied data. This makes it easier for
   * us to draw the items.
   */
  ModifyMenu(hMenu,
       IDM_BLACK,
       MF_OWNERDRAW | MF_BYCOMMAND,
       IDM_BLACK,
       (LPSTR)RGB (0,0,0));

  ModifyMenu(hMenu,
       IDM_BLUE,
       MF_OWNERDRAW | MF_BYCOMMAND,
       IDM_BLUE,
       (LPSTR)RGB (0,0,255));

  ModifyMenu(hMenu,
       IDM_RED,
       MF_OWNERDRAW | MF_BYCOMMAND,
       IDM_RED,
       (LPSTR)RGB (255,0,0));

  ModifyMenu(hMenu,
       IDM_GREEN,
       MF_OWNERDRAW | MF_BYCOMMAND,
       IDM_GREEN,
       (LPSTR)RGB (0,255,0));
    }
    else {
  /* Change the items to normal text items. */
  ModifyMenu(hMenu, IDM_BLACK, MF_BYCOMMAND, IDM_BLACK, "Black");

  ModifyMenu(hMenu, IDM_BLUE, MF_BYCOMMAND, IDM_BLUE, "Blue");

  ModifyMenu(hMenu, IDM_RED, MF_BYCOMMAND, IDM_RED, "Red");

  ModifyMenu(hMenu, IDM_GREEN, MF_BYCOMMAND, IDM_GREEN, "Green");
    }
}


/****************************************************************************
 *                      *
 *  FUNCTION   : HandleDrawItem ( hwnd, lpdis)            *
 *                      *
 *  PURPOSE    : Called in response to a WM_DRAWITEM message, i.e. when the *
 *     colors menu is being modified to an owner-draw menu, or    *
 *     one of the items is selected. It sizes the checkmark bitmap*
 *     to fit next to a color band and draws the color bands and  *
 *     the checkmark on the popup menu.          *
 *                      *
 ****************************************************************************
void FAR PASCAL HandleDrawItem(hwnd, lpdis)
HWND     hwnd;
LPDRAWITEMSTRUCT lpdis;

{
    HDC     hdcBitmap;
    HBITMAP hbmSave;
    HBRUSH  hbr;
    RECT    rc;
    LONG    checkMarkSize;
    DWORD   textColorSave;
    DWORD   bkColorSave;

    /* Get the size of the checkmark so we can leave room for it since we
     * want to be able to check the selected color.
     */
    checkMarkSize = GetMenuCheckMarkDimensions ();

    if (lpdis->itemAction == ODA_SELECT ||
  lpdis->itemAction == ODA_DRAWENTIRE){

  CopyRect ((LPRECT)&rc, (LPRECT)&lpdis->rcItem);
  InflateRect ((LPRECT)&rc, (-2 - LOWORD(checkMarkSize)), -2);

  if (lpdis->itemState & ODS_SELECTED)
  {
      /* Item has been selected -- hilite with a gray frame */
      hbr = GetStockObject (GRAY_BRUSH);
      FrameRect (lpdis->hDC, (LPRECT)&rc, hbr);
  }
  else if (lpdis->itemAction == ODA_SELECT)
  {
      /* Item has been de-selected -- remove gray frame */
      hbr = CreateSolidBrush (GetSysColor (COLOR_MENU));
      FrameRect (lpdis->hDC, (LPRECT)&rc, hbr);
      DeleteObject (hbr);
  }
    }

    if (lpdis->itemAction == ODA_DRAWENTIRE){

  /* Paint the color item in the color requested. */
  hbr = CreateSolidBrush (lpdis->itemData);
  CopyRect ((LPRECT)&rc, (LPRECT)&lpdis->rcItem);
  InflateRect ((LPRECT)&rc, -10-LOWORD(checkMarkSize), -10);
  FillRect (lpdis->hDC, (LPRECT)&rc, hbr);
  DeleteObject (hbr);

  if (lpdis->itemState & ODS_CHECKED){
      /* Draw the check mark if the item is checked. */
      hdcBitmap = CreateCompatibleDC (lpdis->hDC);
      hbmSave = SelectObject (hdcBitmap, hbmCheckOn);

      textColorSave = SetTextColor (lpdis->hDC, 0x00000000L);
      bkColorSave   = SetBkColor (lpdis->hDC, 0x00FFFFFFL);

      /* Use Magic bitblt op so that monochrome bitmaps preserve
         background and foreground colors. */
      BitBlt (lpdis->hDC,
        lpdis->rcItem.left,
        lpdis->rcItem.top+
         (MEASUREITEMHEIGHT - HIWORD (checkMarkSize)) / 2,
        LOWORD (checkMarkSize),
        HIWORD (checkMarkSize),
        hdcBitmap,
        0,
        0,
        ROP_PSDPxax);

      /* Restore colors and bitmap and clean up */
      SetTextColor (lpdis->hDC, textColorSave);
      SetBkColor (lpdis->hDC, bkColorSave);
      SelectObject (hdcBitmap, hbmSave);
      DeleteDC (hdcBitmap);

  }
    }
}

/****************************************************************************
 *                      *
 *  FUNCTION   : HandlePopupMenu (hwnd, point)            *
 *                      *
 *  PURPOSE    : Handles the display of the "floating" popup that appears   *
 *     on a mouse click in the app's client area.                 *
 *                      *
 ****************************************************************************
void FAR PASCAL HandlePopupMenu (hwnd, point)
HWND   hwnd;
POINT  point;

{
    HMENU hMenu;
    HMENU hMenuTrackPopup;

    /* Get the menu for the popup from the resource file. */
    hMenu = LoadMenu (hInst, "PopupMenu");
    if (!hMenu)
  return;

    /* Get the first menu in it which we will use for the call to
     * TrackPopup(). This could also have been created on the fly using
     * CreatePopupMenu and then we could have used InsertMenu() or
     * AppendMenu.
     */
    hMenuTrackPopup = GetSubMenu (hMenu, 0);

    /* Convert the mouse point to screen coordinates since that is what
     * TrackPopup expects.
     */
    ClientToScreen (hwnd, (LPPOINT)&point);

    /* Draw and track the "floating" popup */
    TrackPopupMenu (hMenuTrackPopup, 0, point.x, point.y, 0, hwnd, NULL);

    /* Destroy the menu since were are done with it. */
    DestroyMenu (hMenu);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : MenuWndProc (hWnd, message, wParam, lParam)        *
 *                      *
 *  PURPOSE    : Window function for the main app. window. Processes all the*
 *     menu selections and oter messages.          *
 *                      *
 ****************************************************************************
long FAR PASCAL MenuWndProc (hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;

{
    FARPROC lpProc;
    HMENU hMenu;
    LPDRAWITEMSTRUCT dis;
    RECT rc;


    switch (message){
  case WM_SYSCOMMAND:
      /* Show the About ... dialog */
      if (wParam == ID_ABOUT){
    lpProc = MakeProcInstance (About, hInst);
    DialogBox (hInst,
         "AboutBox",
         hWnd,
         lpProc);

    FreeProcInstance (lpProc);
    break;
      }
      else
        return DefWindowProc (hWnd, message, wParam, lParam);

  case WM_COMMAND:
      switch (wParam){
     case IDM_EXIT:
       DestroyWindow (hWnd);
       break;

     case IDM_ABOUT:
       /* Bring up the About.. dialog box */
       lpProc = MakeProcInstance (About, hInst);
       DialogBox (hInst,
            "AboutBox",
            hWnd,
            lpProc);

       FreeProcInstance (lpProc);
       break;

     case IDM_COLOROWNERDR:
         /* Change colors in color menu depending on state of this
      menu item. */
         HandleChangeColors (hWnd);
         break;

     case IDM_STATE1:
     case IDM_STATE2:
     case IDM_STATE3:
     case IDM_STATE4:
          /* Get a handle to the states menu... */
          hMenu = GetSubMenu (GetMenu (hWnd), IDSTATES_POS);

          /* Uncheck all the items. */
          CheckMenuItem (hMenu, IDM_STATE1,
             MF_BYCOMMAND | MF_UNCHECKED);
          CheckMenuItem (hMenu, IDM_STATE2,
             MF_BYCOMMAND | MF_UNCHECKED);
          CheckMenuItem (hMenu, IDM_STATE3,
             MF_BYCOMMAND | MF_UNCHECKED);
          CheckMenuItem (hMenu, IDM_STATE4,
             MF_BYCOMMAND | MF_UNCHECKED);

          /* ...and just check the selected one.*/
          CheckMenuItem (hMenu, wParam,
             MF_BYCOMMAND | MF_CHECKED);
         break;

     case IDM_BLACK:
     case IDM_RED:
     case IDM_BLUE:
     case IDM_GREEN:
          /* Get a handle to the Colors menu. */
          hMenu = GetSubMenu (GetMenu (hWnd),IDCOLORS_POS);

          /* Uncheck all the items. */
          CheckMenuItem (hMenu, IDM_BLACK,
             MF_BYCOMMAND | MF_UNCHECKED);
          CheckMenuItem (hMenu, IDM_RED,
             MF_BYCOMMAND | MF_UNCHECKED);
          CheckMenuItem (hMenu, IDM_BLUE,
             MF_BYCOMMAND | MF_UNCHECKED);
          CheckMenuItem (hMenu, IDM_GREEN,
             MF_BYCOMMAND | MF_UNCHECKED);

          /* ...and just check the selected one.*/
          CheckMenuItem (hMenu, wParam,
             MF_BYCOMMAND | MF_CHECKED);
          break;

     case IDM_FONT:
          /* Messages sent to us from TrackPopupMenu when
           * items are selected from the "floating" popups
           */
          MessageBox (hWnd,
          "A font was selected",
          "Popup Menu Alert",
          MB_APPLMODAL|MB_OK);
          break;

     case IDM_SIZE:
          MessageBox (hWnd,
          "A size was selected",
          "Popup Menu Alert",
          MB_APPLMODAL|MB_OK);
          break;

     case IDM_STYLE:
          MessageBox (hWnd,
          "A style was selected",
          "Popup Menu Alert",
          MB_APPLMODAL|MB_OK);
          break;
      }
      break;

  case WM_SIZE:
      if (lParam){
    /* If window is being sized to a non zero value...
     * invalidate it's client area.
     */
    InvalidateRect (hWnd, NULL, TRUE);
      }
      break;

  case WM_PAINT:
      HandlePaint (hWnd);
      break;

  case WM_MEASUREITEM:
      /* Use the same width for all items. We could examine the item id
         and use different widths/heights for each item. */
      ((LPMEASUREITEMSTRUCT)lParam)->itemWidth  = MEASUREITEMWIDTH;
      ((LPMEASUREITEMSTRUCT)lParam)->itemHeight = MEASUREITEMHEIGHT;
      return TRUE;

  case WM_DRAWITEM:
      /* Redraw the "colors" menu in normal/ownerdrawmode */
      HandleDrawItem (hWnd,(LPDRAWITEMSTRUCT)lParam);
      return TRUE;
      break;

  case WM_CREATE:
      /* Create the menu */
      HandleCreate (hWnd);
      break;

  case WM_DESTROY:
      /* Delete the on/off bitmaps so that they don't waste memory. */
      DeleteObject (hbmCheckOn);
      DeleteObject (hbmCheckOff);

      PostQuitMessage (0);
      break;

  case WM_LBUTTONDOWN:
      /* Draw the "floating" popup in the app's client area */
      GetClientRect (hWnd, (LPRECT)&rc);
      if (PtInRect ((LPRECT)&rc, MAKEPOINT (lParam)))
    HandlePopupMenu (hWnd, lParam);
      break;

  default:
      return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return(NULL);
}


MPFILE.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MULTIPAD\MPFILE.C

/***************************************************************************
 *                                                                         *
 *  MODULE    : MpFile.c                                                   *
 *                                                                         *
 *  PURPOSE   : Contains the code for File I/O for Multipad.               *
 *                                                                         *
 *  FUNCTIONS : AlreadyOpen   - Determines if a file is already open.      *
 *                                                                         *
 *              AddFile       - Creates a new MDI window and, if specified,*
 *        loads a file into it.                      *
 *                                                                         *
 *              LoadFile      - Loads a file into a MDI window.            *
 *                                                                         *
 *              ReadFile      - Calls File/Open dialog and appropriately   *
 *                              responds to the user's input.              *
 *                                                                         *
 *              SaveFile      - Saves the contents of a MDI window's edit  *
 *                              control to a file.                         *
 *                                                                         *
 *              SetSaveFrom   - Formats the "Save 'file' to" string.       *
 *                                                                         *
 *              SaveAsDlgProc - Dialog function for the File/SaveAs dialog.*
 *                                                                         *
 *              ChangeFile    - Calls File/SaveAs dialog.                  *
 *                                                                         *
 ***************************************************************************/
#include "multipad.h"

VOID NEAR PASCAL GetFileName(PSTR);
int FAR PASCAL DialogBoxParam(HANDLE,LPSTR,HWND,FARPROC,LONG);

OFSTRUCT  of;
/****************************************************************************
 *                                                                          *
 *  FUNCTION   : AlreadyOpen(szFile)                                        *
 *                                                                          *
 *  PURPOSE    : Checks to see if the file described by the string pointed  *
 *               to by 'szFile' is already open.                            *
 *                                                                          *
 *  RETURNS    : a handle to the described file's window if that file is    *
 *               already open;  NULL otherwise.                             *
 *                                                                          *
 ****************************************************************************

HWND AlreadyOpen(char *szFile)
{
    int     iDiff;
    HWND    hwndCheck;
    char    szChild[64];
    LPSTR   lpChild, lpFile;
    int     wFileTemp;

    /* Open the file with the OF_PARSE flag to obtain the fully qualified
     * pathname in the OFSTRUCT structure.
     */
    wFileTemp = OpenFile ((LPSTR)szFile, (LPOFSTRUCT)&of, OF_PARSE);
    if (! wFileTemp)
  return(NULL);
    _lclose (wFileTemp);

    /* Check each MDI child window in Multipad */
    for (   hwndCheck = GetWindow(hwndMDIClient, GW_CHILD);
      hwndCheck;
      hwndCheck = GetWindow(hwndCheck, GW_HWNDNEXT)   ) {
  /* Initialization  for comparison */
  lpChild = szChild;
  lpFile = AnsiUpper((LPSTR) of.szPathName);
  iDiff = 0;

  /* Skip icon title windows */
  if (GetWindow(hwndCheck, GW_OWNER))
      continue;

  /* Get current child window's name */
  GetWindowText(hwndCheck, lpChild, 64);

  /* Compare window name with given name */
  while ((*lpChild) && (*lpFile) && (!iDiff)){
      if (*lpChild++ != *lpFile++)
    iDiff = 1;
  }

  /* If the two names matched, the file is already   */
  /* open -- return handle to matching child window. */
  if (!iDiff)
      return(hwndCheck);
    }
    /* No match found -- file is not open -- return NULL handle */
    return(NULL);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : AddFile (lpName)              *
 *                      *
 *  PURPOSE    : Creates a new MDI window. If the lpName parameter is not   *
 *     NULL, it loads a file into the window.         *
 *                      *
 *  RETURNS    : HWND  - A handle to the new window.          *
 *                      *
 ****************************************************************************

HWND FAR PASCAL AddFile(pName)
char * pName;
{
    HWND hwnd;

    char      sz[160];
    MDICREATESTRUCT mcs;

    if (!pName) {
  /* The pName parameter is NULL -- load the "Untitled" string from */
  /* STRINGTABLE and set the title field of the MDI CreateStruct.    */
  LoadString (hInst, IDS_UNTITLED, sz, sizeof(sz));
  mcs.szTitle = (LPSTR)sz;
    }
    else
  /* Title the window with the fully qualified pathname obtained by
   * calling OpenFile() with the OF_PARSE flag (in function
   * AlreadyOpen(), which is called before AddFile().
   */
  mcs.szTitle = of.szPathName;

    mcs.szClass = szChild;
    mcs.hOwner  = hInst;

    /* Use the default size for the window */
    mcs.x = mcs.cx = CW_USEDEFAULT;
    mcs.y = mcs.cy = CW_USEDEFAULT;

    /* Set the style DWORD of the window to default */
    mcs.style = styleDefault;

    /* tell the MDI Client to create the child */
    hwnd = (WORD)SendMessage (hwndMDIClient,
            WM_MDICREATE,
            0,
            (LONG)(LPMDICREATESTRUCT)&mcs);

    /* Did we get a file? Read it into the window */
    if (pName){
  if (!LoadFile(hwnd, pName)){
      /* File couldn't be loaded -- close window */
      SendMessage(hwndMDIClient, WM_MDIDESTROY, (WORD) hwnd, 0L);
  }
    }

    return hwnd;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : LoadFile (lpName)              *
 *                      *
 *  PURPOSE    : Given the handle to a MDI window and a filename, reads the *
 *     file into the window's edit control child.                 *
 *                      *
 *  RETURNS    : TRUE  - If file is sucessfully loaded.         *
 *     FALSE - Otherwise.              *
 *                      *
 ****************************************************************************

int FAR PASCAL LoadFile (hwnd, pName)
HWND hwnd;
char * pName;
{
    WORD   wLength;
    HANDLE hT;
    LPSTR  lpB;
    HWND   hwndEdit;
    int    fh;

    hwndEdit = GetWindowWord (hwnd, GWW_HWNDEDIT);

    /* The file has a title, so reset the UNTITLED flag. */
    SetWindowWord(hwnd, GWW_UNTITLED, FALSE);

    fh = _lopen (pName, 0);

    /* Make sure file has been opened correctly */
    if ( fh < 0 )
  goto error;

    /* Find the length of the file */
    wLength = (WORD)_llseek (fh, 0L, 2);
    _llseek (fh, 0L, 0);

    /* Attempt to reallocate the edit control's buffer to the file size */
    hT = (HANDLE)SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L);
    if (LocalReAlloc(hT, wLength+1, LHND) == NULL) {
  /* Couldn't reallocate to new size -- error */
  _lclose (fh);
  goto error;
    }

    /* read the file into the buffer */
    if (wLength != _lread (fh, (lpB = (LPSTR)LocalLock (hT)), wLength))
  MPError (hwnd, MB_OK|MB_ICONHAND, IDS_CANTREAD, (LPSTR)pName);

    /* Zero terminate the edit buffer */
    lpB[wLength] = 0;
    LocalUnlock (hT);

    SendMessage (hwndEdit, EM_SETHANDLE, hT, 0L);
    _lclose (fh);

    return TRUE;

error:
    /* Report the error and quit */
    MPError(hwnd, MB_OK | MB_ICONHAND, IDS_CANTOPEN, (LPSTR)pName);
    return FALSE;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : ReadFile(hwnd)                                             *
 *                                                                          *
 *  PURPOSE    : Called in response to a File/Open menu selection. It asks  *
 *               the user for a file name and responds appropriately.       *
 *                                                                          *
 ****************************************************************************

VOID FAR PASCAL ReadFile(HWND hwnd)
{
    char    szFile[128];
    HWND    hwndFile;

    GetFileName (szFile);

    /* If the result is not the empty string -- take appropriate action */
    if (*szFile) {
  /* Is file already open?? */
  if (hwndFile = AlreadyOpen(szFile)) {
      /* Yes -- bring the file's window to the top */
      BringWindowToTop(hwndFile);
  }
  else {
      /* No -- make a new window and load file into it */
      AddFile(szFile);
  }
    }
}

/****************************************************************************
 *                      *
 *  FUNCTION   : SaveFile (hwnd)              *
 *                      *
 *  PURPOSE    : Saves contents of current edit control to disk.      *
 *                      *
 ****************************************************************************

VOID FAR PASCAL SaveFile( hwnd )

HWND hwnd;
{
    HANDLE   hT;
    LPSTR    lpT;
    char     szFile[128];
    WORD     cch;
    int      fh;
    OFSTRUCT of;
    HWND     hwndEdit;

    PSTR     pch;

    hwndEdit = GetWindowWord ( hwnd, GWW_HWNDEDIT);
    GetWindowText (hwnd, szFile, sizeof(szFile));

    /* If there is no extension (control is 'Untitled') add .TXT as extension
    for (cch = FALSE, lpT = szFile; *lpT; lpT++)
  switch (*lpT){
      case '.':
     cch = TRUE;
     break;

      case '\\':
      case ':' :
     cch = FALSE;
     break;
  }
    if (!cch)
  LoadString (hInst, IDS_ADDEXT, lpT, lpT - (LPSTR)szFile);

    fh = OpenFile (szFile, &of, OF_WRITE | OF_CREATE);

    /* If file could not be opened, quit */
    if (fh < 0){
  MPError (hwnd, MB_OK | MB_ICONHAND, IDS_CANTCREATE, (LPSTR)szFile);
  return;
    }

    /* Find out the length of the text in the edit control */
    cch = GetWindowTextLength (hwndEdit);

    /* Obtain a handle to the text buffer */
    hT  = (HANDLE)SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L);
    lpT = (LPSTR)LocalLock (hT);

    /* Write out the contents of the buffer to the file. */
    if (cch != _lwrite (fh, lpT, cch))
  MPError (hwnd, MB_OK | MB_ICONHAND, IDS_CANTWRITE, (LPSTR)szFile);

    /* Clean up */
    LocalUnlock (hT);
    SendMessage (hwndEdit, EM_SETHANDLE, hT, 0L);

    _lclose (fh);

    return;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : SetSaveFrom ()               *
 *                      *
 *  PURPOSE    : Formats the "Save 'file' to .." string.        *
 *                      *
 ****************************************************************************

VOID NEAR PASCAL SetSaveFrom ( hwnd , psz)
HWND hwnd;
PSTR psz;
{
    char szFmt[32];
    char szText[160];

    /* The text string in the .RC file contains the format string... */
    GetDlgItemText( hwnd, IDD_SAVEFROM, szFmt, sizeof(szFmt));

    /* NOTE: this (LPSTR) cast MUST be here... wsprintf() is a cdecl
     * (C calling conventions) function with varying args... there is
     * no way for the compiler to know that all strings must be LPSTR's
     * and do the conversion, so we have to be careful about wsprintf()'s.
     */
    wsprintf ( szText, szFmt, (LPSTR)psz);

    /* set the text in the static control */
    SetDlgItemText (hwnd, IDD_SAVEFROM, szText);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : SaveAsDlgProc(hwnd, message, wParam, lParam)        *
 *                      *
 *  PURPOSE    : Dialog function File/SaveAs. It waits for a filename, and  *
 *     then calls SaveFile or cancels the operation.        *
 *                      *
 ****************************************************************************

BOOL FAR PASCAL SaveAsDlgProc( hwnd, message, wParam, lParam)
HWND hwnd;
WORD message;
WORD wParam;
LONG lParam;
{
    char   sz[64];
    char   *pch;
    BOOL   fExt;
    HWND   hwndSave;

    switch (message){

  case WM_INITDIALOG:

      /* Identify the window whose contents we're saving */
      hwndSave = LOWORD (lParam);

      /* Set it's name in the property list */
      SetProp (hwnd, PROP_FILENAME, hwndSave);

      GetWindowText (hwndSave, sz, sizeof(sz));

      /* Set the save from string... */
      SetSaveFrom (hwnd,sz);

      /* Generate a filename complete with extension */
      AnsiUpper (sz);
      for (fExt = FALSE, pch = sz; *pch; pch++)
    if (*pch == '.')
        fExt = TRUE;
    else if (*pch == '\\')
        fExt = FALSE;
      if (!fExt)
    LoadString (hInst, IDS_ADDEXT, (LPSTR)pch, pch - sz);

      /* Display the filename in the edit control */
      SetDlgItemText (hwnd, IDD_SAVETO, sz);

      /* Select the entire range of text */
      SendDlgItemMessage (hwnd, IDD_SAVETO, EM_SETSEL, 0, MAKELONG (0,100));

      DlgDirList (hwnd, "*.*", IDD_DIRS, IDD_PATH, ATTR_DIRS);

      /* enable OK butto iff edit control is nonempty */
      if (!*sz)
    EnableWindow (GetDlgItem (hwnd, IDOK), FALSE);
      break;

  case WM_COMMAND:
      switch (wParam){
    case IDCANCEL:
        /* Abort operation */
        EndDialog(hwnd,1);
        break;

    case IDOK:
       /*  Just change the title of the MDI child. The calling
        *  function of ChangeFile(), which uses the title text
        *  for the filename, will do the actual save.
        */
        hwndSave = GetProp (hwnd, PROP_FILENAME);
        GetDlgItemText (hwnd, IDD_SAVETO, sz, sizeof(sz));
        AnsiUpper ((LPSTR)sz);
        SetWindowText (hwndSave, sz);
        EndDialog (hwnd, 0);
        break;

    case IDD_SAVETO:
       /* If the edit control changes, check to see if its empty.
        * enable OK if it contains something
        */
        if (HIWORD (lParam) != EN_CHANGE)
      return FALSE;
        EnableWindow (GetDlgItem (hwnd, IDOK),
      (WORD) SendDlgItemMessage (hwnd,
               IDD_SAVETO,
               WM_GETTEXTLENGTH,
               0,
               0L));
        break;

    case IDD_DIRS:
        if (HIWORD(lParam)==LBN_DBLCLK){
      char szT[64];

      DlgDirSelect ( hwnd, szT, IDD_DIRS);
      lstrcat ( szT, "*.*");
      DlgDirList (hwnd, szT, IDD_DIRS, IDD_PATH, ATTR_DIRS);
      break;
        }
        return FALSE;

    default:
        return FALSE;
      }

  default:
      return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : ChangeFile (hwnd)              *
 *                      *
 *  PURPOSE    : Invokes the File/SaveAs dialog.          *
 *                      *
 *  RETURNS    : TRUE  - if user selected OK or NO.          *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************

BOOL FAR PASCAL ChangeFile (hwnd)
HWND hwnd;
{
    FARPROC lpfn;
    int     i;

    lpfn = MakeProcInstance (SaveAsDlgProc, hInst);
    i = DialogBoxParam (hInst, IDD_SAVEAS, hwnd, lpfn, MAKELONG (hwnd, 0));
    FreeProcInstance (lpfn);
    if (!i)
  SetWindowWord (hwnd, GWW_UNTITLED, 0);
    return !i;
}


MPFIND.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MULTIPAD\MPFIND.C

/***************************************************************************
 *                     *
 *  MODULE  : MpFind.c                   *
 *                     *
 *  PURPOSE  : Code to do text searches in MultiPad.        *
 *                     *
 *  FUNCTIONS  : RealSlowCompare () - Compares subject string with target *
 *               string.           *
 *                     *
 *      FindText ()       - Looks for the search string in the  *
 *               active window.         *
 *                     *
 *      FindPrev ()       - Find previous occurence of search   *
 *               string.           *
 *                     *
 *      FindNext ()       - Find next occurence of search string*
 *                     *
 *      FindDlgProc ()     - Dialog function for Search/Find.    *
 *                     *
 *      Find ()       - Invokes FindDlgProc ()       *
 *                     *
 ***************************************************************************/
#include "multipad.h"

#undef HIWORD
#undef LOWORD

#define HIWORD(l) (((WORD*)&(l))[1])
#define LOWORD(l) (((WORD*)&(l))[0])

BOOL fCase     = FALSE;    /* Turn case sensitivity off */
char szSearch[160] = "";       /* Initialize search string  */

/****************************************************************************
 *                      *
 *  FUNCTION   : RealSlowCompare ()              *
 *                      *
 *  PURPOSE    : Compares subject string with the target string. This fn/   *
 *     is called repeatedly so that all substrings are compared,  *
 *     which makes it O(n ** 2), hence it's name.                 *
 *                      *
 *  RETURNS    : TRUE  - If pSubject is identical to pTarget.        *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************

BOOL NEAR PASCAL RealSlowCompare (pSubject, pTarget )
register PSTR pSubject;
register PSTR pTarget;
{
    if (fCase){
  while (*pTarget)
      if (*pTarget++ != *pSubject++)
    return FALSE;
    }
    else{
  /* If case-insensitive, convert both subject and target to lowercase
   * before comparing.
   */
  AnsiLower ((LPSTR)pTarget);
  while (*pTarget)
      if (*pTarget++ != (char)(DWORD)AnsiLower ((LPSTR)(DWORD)(BYTE)*pSubject
    return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : FindText ()                *
 *                      *
 *  PURPOSE    : Finds the search string in the active window according to  *
 *     search direction (dch) specified ( -1 for reverse and 1 for*
 *     forward searches).              *
 *                      *
 ****************************************************************************
VOID NEAR PASCAL FindText( dch )
register int dch;

{
    register PSTR pText;
    HANDLE    hT;
    LONG    l;
    WORD    cch;
    int     i;

    if (!*szSearch)
  return;

    /* Find the current selection range */
    l = SendMessage(hwndActiveEdit, EM_GETSEL, 0, 0L);

    /* Get the handle to the text buffer and lock it */
    hT    = (HANDLE)SendMessage (hwndActiveEdit, EM_GETHANDLE, 0, 0L);
    pText = LocalLock(hT);

    /* Get the length of the text */
    cch = (WORD)SendMessage (hwndActiveEdit, WM_GETTEXTLENGTH, 0, 0L);

    /* Start with the next char. in selected range ... */
    pText += LOWORD (l) + dch;

    /* Compute how many characters are before/after the current selection*/
    if (dch < 0)
  i = LOWORD (l);
    else
  i = cch - LOWORD (l) + 1 - lstrlen (szSearch);

    /* While there are uncompared substrings... */
    while ( i > 0){
  LOWORD(l)+=dch;

  /* Does this substring match? */
  if (RealSlowCompare(pText,szSearch)){

      /* Yes, unlock the buffer.*/
      LocalUnlock(hT);

      /* Select the located string */
      HIWORD(l) = LOWORD(l) + lstrlen (szSearch);
      SendMessage (hwndActiveEdit, EM_SETSEL, 0, l);
      return;
  }
  i--;

  /* increment/decrement start position by 1 */
  pText += dch;
    }

    /* Not found... unlock buffer. */
    LocalUnlock (hT);

    /* Give a message */
    MPError (hwndFrame, MB_OK | MB_ICONEXCLAMATION, IDS_CANTFIND, (LPSTR)szSe

    return;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : FindPrev ()                *
 *                      *
 *  PURPOSE    : Finds the previous occurence of the search string. Calls   *
 *     FindText () with a negative search direction.        *
 *                      *
 ****************************************************************************
VOID FAR PASCAL FindPrev(void)
{
    FindText(-1);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : FindNext ()                *
 *                      *
 *  PURPOSE    : Finds the next occurence of search string. Calls      *
 *     FindText () with a positive search direction.        *
 *                      *
 ****************************************************************************
VOID FAR PASCAL FindNext(void)
{
    FindText(1);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : FindDlgProc(hwnd, message, wParam, lParam)        *
 *                      *
 *  PURPOSE    : Dialog function for the Search/Find command. Prompts user  *
 *     for target string, case flag and search direction.      *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL FindDlgProc(hwnd, msg, wParam, lParam)
HWND hwnd;
WORD msg;
WORD wParam;
LONG lParam;
{
    switch (msg){
  case WM_INITDIALOG:{

      /* Check/uncheck case button */
      CheckDlgButton (hwnd, IDD_CASE, fCase);

      /* Set default search string to most recently searched string */
      SetDlgItemText (hwnd, IDD_SEARCH, szSearch);

      /* Allow search only if target is nonempty */
      if (!lstrlen (szSearch)){
    EnableWindow (GetDlgItem (hwnd, IDOK), FALSE);
    EnableWindow (GetDlgItem (hwnd, IDD_PREV), FALSE);
      }
      break;
  }

  case WM_COMMAND:{

      /* Search forward by default (see IDOK below) */
      int i = 1;

      switch (wParam){
    /* if the search target becomes non-empty, enable searching */
    case IDD_SEARCH:
        if (HIWORD (lParam) == EN_CHANGE){
      if (!(WORD) SendDlgItemMessage (hwnd,
              IDD_SEARCH,
              WM_GETTEXTLENGTH,
              0,
              0L))
          i = FALSE;
      else
          i = TRUE;
      EnableWindow (GetDlgItem (hwnd, IDOK), i);
      EnableWindow (GetDlgItem (hwnd, IDD_PREV), i);
        }
        break;

    case IDD_CASE:
        /* Toggle state of case button */
        CheckDlgButton (hwnd,
            IDD_CASE,
            !IsDlgButtonChecked (hwnd, IDD_CASE));
        break;

    case IDD_PREV:
        /* Set direction to backwards */
        i=-1;
        /*** FALL THRU ***/

    case IDOK:
        /* Save case selection */
        fCase = IsDlgButtonChecked( hwnd, IDD_CASE);

        /* Get search string */
        GetDlgItemText (hwnd, IDD_SEARCH, szSearch, sizeof (szSearch));

        /* Find the text */
        FindText (i);
        /*** FALL THRU ***/

    /* End the dialog */
    case IDCANCEL:
        EndDialog (hwnd, 0);
        break;

    default:
        return FALSE;
      }
      break;
  }
  default:
      return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : Find()                 *
 *                      *
 *  PURPOSE    : Invokes the Search/Find dialog.          *
 *                      *
 ****************************************************************************

VOID FAR PASCAL Find()
{
    FARPROC lpfn;

    lpfn = MakeProcInstance (FindDlgProc, hInst);
    DialogBox (hInst, IDD_FIND, hwndFrame, lpfn);
    FreeProcInstance (lpfn);
}


MPINIT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MULTIPAD\MPINIT.C

/***************************************************************************
 *                     *
 *  MODULE  : MpInit.c               *
 *                     *
 *  PURPOSE  : Contains initialization code for MultiPad.       *
 *                     *
 *  FUNCTIONS  : InitializeApplication() - Sets up Class data structure   *
 *              and registers window class.    *
 *                     *
 *      InitializeInstance ()   - Does a per-instance initial-   *
 *              ization of MultiPad. Creates   *
 *              the "frame" and MDI client.    *
 *                     *
 ***************************************************************************/
#include "multipad.h"

char szFrame[] = "mpframe";   /* Class name for "frame" window */
char szChild[] = "mpchild";   /* Class name for MDI window     */

/****************************************************************************
 *                      *
 *  FUNCTION   : InitializeApplication ()            *
 *                      *
 *  PURPOSE    : Sets up the class data structures and does a one-time      *
 *     initialization of the app by registering the window classes*
 *                      *
 *  RETURNS    : TRUE  - If RegisterClass() was successful for both classes.*
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************

BOOL FAR PASCAL InitializeApplication()
{
    WNDCLASS  wc;

    /* Register the frame class */
    wc.style       = 0;
    wc.lpfnWndProc   = MPFrameWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInst;
    wc.hIcon       = LoadIcon(hInst,IDMULTIPAD);
    wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = COLOR_APPWORKSPACE+1;
    wc.lpszMenuName  = IDMULTIPAD;
    wc.lpszClassName = szFrame;

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

    /* Register the MDI child class */
    wc.lpfnWndProc   = MPMDIChildWndProc;
    wc.hIcon       = LoadIcon(hInst,IDNOTE);
    wc.lpszMenuName  = NULL;
    wc.cbWndExtra    = CBWNDEXTRA;
    wc.lpszClassName = szChild;

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

    return TRUE;

}

/****************************************************************************
 *                      *
 *  FUNCTION   : InitializeInstance ()              *
 *                      *
 *  PURPOSE    : Performs a per-instance initialization of MultiPad. It     *
 *     also creates the frame and an MDI window.        *
 *                      *
 *  RETURNS    : TRUE  - If initialization was successful.        *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL InitializeInstance(LPSTR lpCmdLine, WORD nCmdShow)
{
    extern HWND  hwndMDIClient;
    char   sz[80], *pCmdLine;
    HDC    hdc;
    HMENU   hmenu;

    /* Get the base window title */
    LoadString (hInst, IDS_APPNAME, sz, sizeof(sz));

    /* Create the frame */
    hwndFrame = CreateWindow (szFrame,
            sz,
            WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
            CW_USEDEFAULT,
            0,
            CW_USEDEFAULT,
            0,
            NULL,
            NULL,
            hInst,
            NULL);

    if ((!hwndFrame) || (!hwndMDIClient))
  return FALSE;

    /* Load main menu accelerators */
    if (!(hAccel = LoadAccelerators (hInst, IDMULTIPAD)))
  return FALSE;

    /* Display the frame window */
    ShowWindow (hwndFrame, nCmdShow);
    UpdateWindow (hwndFrame);

    /* If the command line string is empty, nullify the pointer to it
    ** else copy command line into our data segment
    */
    if ( lpCmdLine && !(*lpCmdLine))
       pCmdLine = NULL;
    else {
        pCmdLine = (char *) LocalAlloc(LPTR, lstrlen(lpCmdLine) + 1);
        if (pCmdLine)
           lstrcpy(pCmdLine, lpCmdLine);
    }

    /* Add the first MDI window */
    AddFile (pCmdLine);

    /* if we allocated a buffer then free it */
    if (pCmdLine)
        LocalFree((LOCALHANDLE) pCmdLine);

    /* Default to minimized windows after the first. */
    styleDefault = 0L;

    return TRUE;

}


MPOPEN.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MULTIPAD\MPOPEN.C

/***************************************************************************
 *                     *
 *  MODULE  : MpOpen.c               *
 *                     *
 *  PURPOSE  : Contains the file open dialog function and it's helper   *
 *      functions.               *
 *                     *
 *  FUNCTIONS  : IsWild ()        - Ascertains that the input string   *
 *          contains a DOS wildcard character. *
 *                     *
 *      SelectFile()        - If filename supplied contains a    *
 *          wildcard, this function fills the  *
 *          listboxes in File/Open dialog, else*
 *          the dialog is closed.       *
 *                     *
 *      FileOpenDlgProc()   - Dialog funcion for the File/Open   *
 *          dialog.          *
 *                     *
 *      GetFileName ()      - Gets a file name from the user.    *
 *                     *
 ***************************************************************************/
#include "multipad.h"

char szPropertyName [] = "FILENAME";/* Name of the File name property list it

int FAR PASCAL DialogBoxParam ( HANDLE, LPSTR, HWND, FARPROC, LONG);


/****************************************************************************
 *                      *
 *  FUNCTION   : IsWild ( psz )               *
 *                      *
 *  PURPOSE    : Checks if the string (referenced by a NEAR pointer)      *
 *     contains a DOS wildcard character ("*" or "?").      *
 *                      *
 *  RETURNS    : TRUE  - iff the string contains a wildcard character.      *
 *     FALSE - otherwise.             .      *
 *                      *
 ****************************************************************************
BOOL NEAR PASCAL IsWild( psz )
register PSTR psz;
{
    for(;;)
  switch (*psz++){
      case '*':
      case '?':
    /* Found wildcard */
    return TRUE;

      case 0:
    /* Reached end of string */
    return FALSE;

      default:
    continue;
  }
}

/****************************************************************************
 *                      *
 *  FUNCTION   : FileExists(pch)                                            *
 *                      *
 *  PURPOSE    : Checks to see if a file exists with the path/filename      *
 *               described by the string pointed to by 'pch'.               *
 *                      *
 *  RETURNS    : TRUE  - if the described file does exist.                  *
 *               FALSE - otherwise.                                         *
 *                      *
 ****************************************************************************

BOOL FileExists(PSTR pch)
{
  int fh;

  if ((fh = _lopen((LPSTR) pch, 0)) < 0)
       return(FALSE);

  _lclose(fh);
  return(TRUE);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : SelectFile ( hwnd )              *
 *                      *
 *  PURPOSE    : Reads the string in the edit control of the File/Open      *
 *     dialog. If it contains a wildcard, then it attempts to     *
 *     fill the listboxes in the File/Open dialog. Othewise it    *
 *     ends the dialog. Modifies the FILENAME item in the property*
 *     list of the window.              *
 *                      *
 ****************************************************************************

VOID NEAR PASCAL SelectFile( hwnd )

register HWND hwnd;
{
    register PSTR pch;
    PSTR    pch2;
    int     cch;

    char    szBuf[256];

    /* Get handle (near address) to filename data in window's property list *
    pch = (PSTR)GetProp (hwnd, PROP_FILENAME);

    /* Get the text from the dialog's edit control into this address */
    GetDlgItemText (hwnd, IDD_FILENAME, pch, 64);

    if ( IsWild (pch)){
  /* Select the directory and make a listing of the directories */
  DlgDirList(hwnd, (LPSTR)pch, IDD_DIRS, IDD_PATH, ATTR_DIRS);

  /* Obtain the filename-only part of the path in the edit control */
  for (pch2 = pch; *pch; pch++)
      if (*pch == '\\' || *pch == ':')
    pch2 = pch + 1;

  /* List the files in this directory based on the wildcard. */
  DlgDirList(hwnd, (LPSTR)pch2, IDD_FILES, IDD_PATH, ATTR_FILES);

  /* Set the dialog's edit control to the filename part of path
   * string.
   */
  SetDlgItemText (hwnd, IDD_FILENAME, pch2);
    }
    else
    {
  /* The filename in the property list is not a wildcard */
  if (FileExists (pch)){

      RemoveProp (hwnd, PROP_FILENAME);
      EndDialog (hwnd, 0);
  }
  else{
      MPError ( hwnd, MB_OK | MB_SYSTEMMODAL, IDS_CANTOPEN, (LPSTR) pch);
      SetActiveWindow (hwnd);
  }
    }
}
/****************************************************************************
 *                      *
 *  FUNCTION   : FileOpenDlgProc()              *
 *                      *
 *  PURPOSE    : Dialog function for the File/Open dialog. Takes care of    *
 *     calling the appropriate functions for extracting the      *
 *     filename and wildcard, filling the listboxes and changing  *
 *     the FILENAME item in the property list for the window.     *
 *                      *
 ****************************************************************************

BOOL FAR PASCAL FileOpenDlgProc ( hwnd, message, wParam, lParam)
register HWND hwnd;
WORD        message;
register WORD wParam;
LONG        lParam;
{
    PSTR pch;

    switch (message){

  case WM_INITDIALOG:
      /* Set the default file extension on edit window, and try to
       * get a listing of the files and directories.
       */
      SetDlgItemText ( hwnd, IDD_FILENAME, DEFFILESEARCH);
      SetProp (hwnd, PROP_FILENAME, LOWORD(lParam));
      SendDlgItemMessage (hwnd, IDD_FILENAME, EM_LIMITTEXT, 64, 0L);
      SelectFile (hwnd);
      break;

  case WM_COMMAND:
      switch (wParam){
    case IDOK:
        SelectFile(hwnd);
        break;

    case IDCANCEL:
        /* Set the filename in the prop. list to NULL and quit */
        pch  = (PSTR) GetProp (hwnd, PROP_FILENAME);
        *pch = 0;
        EndDialog (hwnd, 0);
        break;

    case IDD_FILENAME:
        /* Enable the OK button if the edit control has text. */
        EnableWindow ( GetDlgItem (hwnd, IDOK),
           GetWindowTextLength ((HWND)LOWORD (lParam)));
        break;

    case IDD_FILES:

        /* The files listbox. If file selection has changed, fill
         * the new filename into the property list buffer and set
         * text in edit control.
         */
        if (HIWORD(lParam) == LBN_SELCHANGE){
      pch = (PSTR) GetProp (hwnd, PROP_FILENAME);
      DlgDirSelect (hwnd, (LPSTR)pch, IDD_FILES);
      SetDlgItemText (hwnd, IDD_FILENAME, (LPSTR)pch);
        }
        else if (HIWORD(lParam) == LBN_DBLCLK)
      /* if the item was double-clicked, try to open it */
      SelectFile(hwnd);
        break;

    case IDD_DIRS:

        /* The directories listbox. Append current filename in edit
         * control (stripped of the path prefix) to the name from
         * the property list and set the new string in the edit
         * control.
         */
        if (HIWORD(lParam) == LBN_SELCHANGE){

      PSTR pch2, pchT, pchS;

      pch = (PSTR) GetProp (hwnd, PROP_FILENAME);

      /* Get the new drive/dir */
      DlgDirSelect (hwnd, pch, IDD_DIRS);
      pch2 = pch + lstrlen(pch);

      /* Fetch current contents of dialog's edit control and append
       * it to name from property list... */
      GetDlgItemText(hwnd,IDD_FILENAME,(LPSTR)pch2,64);
      if (*pch2 == 0){
          SetDlgItemText(hwnd, IDD_FILENAME, DEFFILESEARCH);
          GetDlgItemText(hwnd,IDD_FILENAME,(LPSTR)pch2,64);
      }
      else {
          pchS = pch;
          for (pchT = pch = pch2; *pch; pch++) {
        if (*pch == '\\' || *pch == ':')
            pchT = pch2;
        else
            *pchT++ = *pch;
          }
          *pchT = 0;
          pch = pchS;
      }

      /* Set the edit control with new string */
      SetDlgItemText (hwnd, IDD_FILENAME, (LPSTR)pch);
        }
        else if (HIWORD(lParam) == LBN_DBLCLK)
      SelectFile (hwnd);
        break;

    default:
        return FALSE;
      }
      break;

  default:
      return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : GetFilename ( pstr )              *
 *                      *
 *  PURPOSE    : Gets a filename from the user by calling the File/Open     *
 *     dialog.                *
 *                      *
 ****************************************************************************
VOID NEAR PASCAL GetFileName(PSTR pstr)
{
    FARPROC lpfn;

    lpfn = MakeProcInstance (FileOpenDlgProc, hInst);
    DialogBoxParam (hInst, IDD_FILEOPEN, hwndFrame, lpfn, MAKELONG(pstr,0));
    FreeProcInstance (lpfn);
}


MPPRINT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MULTIPAD\MPPRINT.C

/***************************************************************************
 *                     *
 *  MODULE  : MpPrint()               *
 *                     *
 *  PURPOSE  : Printing code for MultiPad.           *
 *                     *
 *  FUNCTIONS  : GetPrinterDC ()     -  Creates a printer DC for the *
 *                default device.       *
 *                     *
 *      AbortProc ()       -  Export proc. for GDI to check*
 *                print abort.       *
 *                     *
 *      PrintDlgProc ()     -  Dialog function for the print*
 *                cancel dialog.       *
 *                     *
 *      PrintFile ()       -  Prints the contents of the   *
 *                edit control.       *
 *                     *
 *      GetInitializationData () -  Gets DC initialisation data  *
 *                from a DC supporting     *
 *                ExtDeviceMode().       *
 *                     *
 ***************************************************************************/
#include "multipad.h"

BOOL fAbort;    /* TRUE if the user has aborted the print job   */
HWND hwndPDlg;    /* Handle to the cancel print dialog     */
char szDevice[160];  /* Contains the device, the driver, and the port */
PSTR szDriver;    /* Pointer to the driver name       */
PSTR szPort;    /* Port, ie, LPT1         */
PSTR szTitle;    /* Global pointer to job title       */
int iPrinter = 0;  /* level of available printer support.     */
      /* 0 - no printer available       */
      /* 1 - printer available       */
      /* 2 - driver supports 3.0 device initialization */
HANDLE hInitData=NULL;  /* handle to initialization data     */

char szExtDeviceMode[] = "EXTDEVICEMODE";

/****************************************************************************
 *                      *
 *  FUNCTION   : GetPrinterDC ()              *
 *                      *
 *  PURPOSE    : Creates a printer display context for the default device.  *
 *     As a side effect, it sets the szDevice and szPort variables*
 *     It also sets iPrinter to the supported level of printing.  *
 *                      *
 *  RETURNS    : HDC   - A handle to printer DC.          *
 *                      *
 ****************************************************************************
HDC FAR PASCAL GetPrinterDC(void)
{
    char     szT[32];
    HDC      hdc;
    LPSTR    lpdevmode = NULL;

    iPrinter = 0;

    /* Get the printer information from win.ini into a buffer and
     * null terminate it.
     */
    GetProfileString ( "windows", "device", "" ,szDevice, sizeof(szDevice));
    for (szDriver = szDevice; *szDriver && *szDriver != ','; szDriver++)
  ;
    if (*szDriver)
  *szDriver++ = 0;

    /* From the current position in the buffer, null teminate the
     * list of ports
     */
    for (szPort = szDriver; *szPort && *szPort != ','; szPort++)
  ;
    if (*szPort)
  *szPort++ = 0;

    /* if the device, driver and port buffers all contain meaningful data,
     * proceed.
     */
    if (!*szDevice || !*szDriver || !*szPort){
  *szDevice = 0;
  return NULL;
    }

    /* Create the printer display context */
    if (hInitData){
  /* Get a pointer to the initialization data */
  lpdevmode = (LPSTR) LocalLock (hInitData);

  if (lstrcmp (szDevice, lpdevmode)){
      /* User has changed the device... cancel this setup, as it is
       * invalid (although if we worked harder we could retain some
       * of it).
       */
      lpdevmode = NULL;
      LocalUnlock (hInitData);
      LocalFree (hInitData);
      hInitData = NULL;
  }
    }
    hdc = CreateDC (szDriver, szDevice, szPort, lpdevmode);

    /* Unlock initialization data */
    if (hInitData)
  LocalUnlock (hInitData);

    if (!hdc)
  return NULL;


    iPrinter = 1;

    /* Find out if ExtDeviceMode() is supported and set flag appropriately */
    if (GetProcAddress (GetModuleHandle (szDriver), szExtDeviceMode))
  iPrinter = 2;

    return hdc;

}

/****************************************************************************
 *                      *
 *  FUNCTION   : AbortProc()                *
 *                      *
 *  PURPOSE    : To be called by GDI print code to check for user abort.    *
 *                      *
 ****************************************************************************
int FAR PASCAL AbortProc ( hdc, reserved )
HDC hdc;
WORD reserved;
{
    MSG msg;

    /* Allow other apps to run, or get abort messages */
    while (!fAbort && PeekMessage (&msg, NULL, NULL, NULL, TRUE))
  if (!hwndPDlg || !IsDialogMessage (hwndPDlg, &msg)){
      TranslateMessage (&msg);
      DispatchMessage  (&msg);
  }
    return !fAbort;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : PrintDlgProc ()              *
 *                      *
 *  PURPOSE    : Dialog function for the print cancel dialog box.      *
 *                      *
 *  RETURNS    : TRUE  - OK to abort/ not OK to abort          *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL PrintDlgProc(HWND hwnd, WORD msg, WORD wParam, LONG lParam)
{
    switch (msg){
  case WM_INITDIALOG:
      /* Set up information in dialog box */
      SetDlgItemText (hwnd, IDD_PRINTDEVICE, (LPSTR)szDevice);
      SetDlgItemText (hwnd, IDD_PRINTPORT, (LPSTR)szPort);
      SetDlgItemText (hwnd, IDD_PRINTTITLE, (LPSTR)szTitle);
      break;

  case WM_COMMAND:
      /* abort printing if the only button gets hit */
      fAbort = TRUE;
      break;

  default:
      return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : PrintFile ()                *
 *                      *
 *  PURPOSE    : Prints the contents of the edit control.        *
 *                      *
 ****************************************************************************

VOID FAR PASCAL PrintFile(HWND hwnd)
{
    HDC     hdc;
    int     yExtPage;
    char    sz[32];
    WORD    cch;
    WORD    ich;
    PSTR    pch;
    WORD    iLine;
    WORD    nLinesEc;
    WORD    i;
    HANDLE  hT;
    FARPROC lpfnAbort;
    FARPROC lpfnPDlg;
    HWND    hwndPDlg;
    WORD    dy;
    int     yExtSoFar;
    WORD    fError = TRUE;
    HWND    hwndEdit;

    hwndEdit = (HWND)GetWindowWord(hwnd,GWW_HWNDEDIT);

    /* Create the job title by loading the title string from STRINGTABLE */
    cch = LoadString (hInst, IDS_PRINTJOB, sz, sizeof(sz));
    szTitle = sz + cch;
    cch += GetWindowText (hwnd, sz + cch, 32 - cch);
    sz[31] = 0;

    /* Make instances of the Abort proc. and the Print dialog function */
    lpfnAbort = MakeProcInstance (AbortProc, hInst);
    if (!lpfnAbort)
  goto getout;
    lpfnPDlg = MakeProcInstance (PrintDlgProc, hInst);
    if (!lpfnPDlg)
  goto getout4;

    /* Initialize the printer */
    hdc = GetPrinterDC();
    if (!hdc)
  goto getout5;

    /* Disable the main application window and create the Cancel dialog */
    EnableWindow (hwndFrame, FALSE);
    hwndPDlg = CreateDialog (hInst, IDD_PRINT, hwnd, lpfnPDlg);
    if (!hwndPDlg)
  goto getout3;
    ShowWindow (hwndPDlg, SW_SHOW);
    UpdateWindow (hwndPDlg);

    /* Allow the app. to inform GDI of the escape function to call */
    if (Escape (hdc, SETABORTPROC, 0, (LPSTR)lpfnAbort, NULL) < 0)
  goto getout1;

    /* Initialize the document */
    if (Escape (hdc, STARTDOC, cch, (LPSTR)sz, NULL) < 0)
  goto getout1;

    /* Get the height of one line and the height of a page */
    dy = HIWORD (GetTextExtent (hdc, "CC", 2));
    yExtPage = GetDeviceCaps (hdc, VERTRES);

    /* Get the lines in document and and a handle to the text buffer */
    iLine     = 0;
    yExtSoFar = 0;
    nLinesEc  = (WORD)SendMessage (hwndEdit, EM_GETLINECOUNT, 0, 0L);
    hT        = (HANDLE)SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L);

    /* While more lines print out the text */
    while (iLine < nLinesEc){
  if (yExtSoFar + dy > yExtPage){
      /* Reached the end of a page. Tell the device driver to eject a
       * page
       */
      if (Escape (hdc, NEWFRAME, 0, NULL, NULL) < 0 || fAbort)
    goto getout2;
      yExtSoFar = 0;
  }

  /* Get the length and position of the line in the buffer
   * and lock from that offset into the buffer */
  ich = (WORD)SendMessage (hwndEdit, EM_LINEINDEX, iLine, 0L);
  cch = (WORD)SendMessage (hwndEdit, EM_LINELENGTH, ich, 0L);
  pch = LocalLock(hT) + ich;

  /* Print the line and unlock the text handle */
  TextOut (hdc, 0, yExtSoFar, (LPSTR)pch, cch);
  LocalUnlock (hT);

  /* Test and see if the Abort flag has been set. If yes, exit. */
  if (fAbort)
      goto getout2;

  /* Move down the page */
  yExtSoFar += dy;
  iLine++;
    }

    /* Eject the last page. */
    if (Escape (hdc, NEWFRAME, 0, NULL, NULL) < 0)
  goto getout2;

    /* Complete the document. */
    if (Escape (hdc, ENDDOC, 0, NULL, NULL) < 0){
getout2:
  /* Ran into a problem before NEWFRAME? Abort the document */
  Escape( hdc, ABORTDOC, 0, NULL, NULL);
    }
    else
  fError=FALSE;

getout3:
    /* Close the cancel dialog and re-enable main app. window */
    EnableWindow (hwndFrame, TRUE);
    DestroyWindow (hwndPDlg);

getout1:
    DeleteDC(hdc);

getout5:
    /* Get rid of dialog procedure instances */
    FreeProcInstance (lpfnPDlg);

getout4:
    FreeProcInstance (lpfnAbort);

getout:

    /* Error? make sure the user knows... */
    if (fError)
  MPError (hwnd, MB_OK | MB_ICONEXCLAMATION, IDS_PRINTERROR, (LPSTR)szTitle);

    return;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : GetInitializationData()            *
 *                      *
 *  PURPOSE    : Gets DC initialization data from a printer driver      *
 *     supporting ExtDeviceMode(). Called in response to the      *
 *     File/Printer setup menu selection.          *
 *                      *
 *     This function allows the user to change the printer      *
 *     settings FOR MULTIPAD ONLY.  This allows Multipad to print *
 *     in a variety of settings without messing up any other      *
 *     applications. In a more sophisticated application, this    *
 *     setup could even be saved on a document-by-document basis. *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL GetInitializationData( hwnd )
HWND hwnd ;
{
    LPSTR     lpOld;
    LPSTR     lpNew;
    FARPROC   lpfn;
    HANDLE    hT,hDrv;
    char      sz[32];
    WORD      cb;
    int       flag;

    /* Pop up dialog for user and retain data in app buffer */
    flag = DM_PROMPT | DM_COPY;

    /* Load the device driver and find the ExtDeviceMode() function */
    wsprintf (sz, "%s.drv", (LPSTR)szDriver);
    if ((hDrv = LoadLibrary (sz)) < 32)
  return FALSE;
    if (!(lpfn = GetProcAddress (hDrv, szExtDeviceMode)))
  return FALSE;

    if (hInitData){
  /* We have some old data... we want to modify the previously specified
   * setup rather than starting with the default setup.
   */
  lpOld = (LPSTR)LocalLock(hInitData);
  flag |= DM_MODIFY;
    }
    else
  lpOld = NULL;

    /* Get the number of bytes needed for the init data */
    cb = (*lpfn) (hwnd,
      hDrv,
      NULL,
      (LPSTR)szDevice,
      (LPSTR)szPort,
      (LPDEVMODE)NULL,
      (LPSTR)NULL,
      0);

    /* Grab some memory for the new data and lock it. */
    hT    = LocalAlloc (LHND,cb);
    lpNew = (LPSTR)LocalLock (hT);

    /* Post the device mode dialog. 0 flag iff user hits OK button */
    if ((*lpfn) (hwnd,
     hDrv,
     (LPDEVMODE)lpNew,
     (LPSTR)szDevice,
     (LPSTR)szPort,
     (LPDEVMODE)lpOld,
     (LPSTR)NULL,
     flag)==IDOK)
  flag = 0;

    /* Unlock the input structures */
    LocalUnlock (hT);
    if (hInitData)
  LocalUnlock (hInitData);

    /* If the user hit OK and everything worked, free the original init.
     * data and retain the new one.  Otherwise, toss the new buffer.
     */
    if (flag)
  LocalFree (hT);
    else{
  if (hInitData)
      LocalFree (hInitData);
  hInitData = hT;
    }

    FreeLibrary(hDrv);
    return (!flag);
}


MULTIPAD.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MULTIPAD\MULTIPAD.C

/***************************************************************************
 *                     *
 *  PROGRAM  : MultiPad.c               *
 *                     *
 *  PURPOSE  : To give a multi-Notepad demonstration of the new MDI     *
 *      API in Windows 3.0             *
 *                     *
 *  FUNCTIONS  : WinMain()        - Calls the initialization function  *
 *          and processes message loop     *
 *                     *
 *      MPFrameWndProc()    - Window function for the "frame"    *
 *          window, which controls the menu    *
 *          and contains the MDI document     *
 *          windows as child windows.     *
 *                     *
 *      MPMDIChildWndProc() - Window function for the individual *
 *          document windows       *
 *                     *
 *      InitializeMenu()    - Handles enabling/greying of menu   *
 *          items according to the app's state.*
 *                     *
 *      CloseAllChildren    - Destroys all MDI child windows.    *
 *                     *
 *      CommandHandler()    - Processes the "frame" window's     *
 *          WM_COMMAND messages.       *
 *                     *
 *      AboutDlgProc()      - Dialog function for the ubiquitous *
 *          About.. dialog.        *
 *                     *
 *      SetWrap()        - Alters word wrapping in the edit   *
 *          control.         *
 *                     *
 *      MPError()        - Flashes an error messagebox.     *
 *                     *
 *      QueryCloseChild     - Prompts for saving current MDI     *
 *          child window.         *
 *                     *
 *      QueryCloseAllChildren() - Asks whether it is OK to close *
 *              down app.         *
 *                     *
 ***************************************************************************/

#include "multipad.h"

/* global variables used in this module or among more than one module */
HANDLE hInst;          /* Program instance handle         */
HANDLE hAccel;          /* Main accelerator resource       */
HWND hwndFrame     = NULL;    /* Handle to main window         */
HWND hwndMDIClient   = NULL;    /* Handle to MDI client         */
HWND hwndActive    = NULL;    /* Handle to currently activated child   */
HWND hwndActiveEdit   = NULL;    /* Handle to edit control         */
LONG styleDefault    = WS_MAXIMIZE; /* Default style bits for child windows
            /* The first window is created maximized */
            /* to resemble Notepad.  Later children  */
            /* are normal windows.         */
LPSTR lpMenu       = IDMULTIPAD;  /* Contains the resource id of the       */
            /* current frame menu         */


/* Forward declarations of helper functions in this module */
VOID NEAR PASCAL InitializeMenu (HANDLE);
VOID NEAR PASCAL CommandHandler (HWND,WORD);
VOID NEAR PASCAL SetWrap (HWND,BOOL);
BOOL NEAR PASCAL QueryCloseAllChildren ( VOID );
int  NEAR PASCAL QueryCloseChild (HWND);

/****************************************************************************
 *                      *
 *  FUNCTION   : WinMain(HANDLE, HANDLE, LPSTR, int)          *
 *                      *
 *  PURPOSE    : Creates the "frame" window, does some initialization and   *
 *     enters the message loop.            *
 *                      *
 ****************************************************************************
int NEAR PASCAL WinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow)

HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR  lpszCmdLine;
int    nCmdShow;
{
    MSG msg;

    hInst = hInstance;

    /* If this is the first instance of the app. register window classes */
    if (!hPrevInstance){
  if (!InitializeApplication ())
      return 0;
    }

    /* Create the frame and do other initialization */
    if (!InitializeInstance (lpszCmdLine, nCmdShow))
  return 0;

    /* Enter main message loop */
    while (GetMessage (&msg, NULL, 0, 0)){
  /* If a keyboard message is for the MDI , let the MDI client
   * take care of it.  Otherwise, check to see if it's a normal
   * accelerator key (like F3 = find next).  Otherwise, just handle
   * the message as usual.
   */
  if ( !TranslateMDISysAccel (hwndMDIClient, &msg) &&
       !TranslateAccelerator (hwndFrame, hAccel, &msg)){
      TranslateMessage (&msg);
      DispatchMessage (&msg);
  }
    }
    return 0;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : MPFrameWndProc (hwnd, msg, wParam, lParam )        *
 *                      *
 *  PURPOSE    : The window function for the "frame" window, which controls *
 *     the menu and encompasses all the MDI child windows. Does   *
 *     the major part of the message processing. Specifically, in *
 *     response to:                *
 *                      *
 *         WM_CREATE    : Creates and displays the "frame". *
 *                      *
 *         WM_INITMENU  : Sets up the state of the menu.    *
 *                      *
 *         WM_WININICHANGE &  : If default printer characteristics*
 *         WM_DEVMODECHANGE    have been changed, reinitialises  *
 *            printer DC.          *
 *                      *
 *         WM_COMMAND   : Passes control to a command-      *
 *            handling function.        *
 *                      *
 *         WM_CLOSE    : Quits the app. if all the child   *
 *            windows agree.        *
 *                      *
 *         WM_QUERYENDSESSION : Checks that all child windows     *
 *            agree to quit.        *
 *                      *
 *         WM_DESTROY   : Destroys frame window and quits   *
 *            app.            *
 *                      *
 ****************************************************************************
LONG FAR PASCAL MPFrameWndProc ( hwnd, msg, wParam, lParam )

register HWND   hwnd;
WORD     msg;
register WORD   wParam;
LONG     lParam;

{
    switch (msg){
  case WM_CREATE:{

      CLIENTCREATESTRUCT ccs;
      HDC hdc;

      /* Find window menu where children will be listed */
      ccs.hWindowMenu = GetSubMenu (GetMenu(hwnd),WINDOWMENU);
      ccs.idFirstChild = IDM_WINDOWCHILD;

      /* Create the MDI client filling the client area */
      hwndMDIClient = CreateWindow ("mdiclient",
            NULL,
            WS_CHILD | WS_CLIPCHILDREN |
            WS_VSCROLL | WS_HSCROLL,
            0,
            0,
            0,
            0,
            hwnd,
            0xCAC,
            hInst,
            (LPSTR)&ccs);


      ShowWindow (hwndMDIClient,SW_SHOW);

      /* Check if printer can be initialized */
      if (hdc = GetPrinterDC ()){
    DeleteDC (hdc);
      }

      break;
  }

  case WM_INITMENU:
      /* Set up the menu state */
      InitializeMenu ((HMENU)wParam);
      break;

  case WM_WININICHANGE:
  case WM_DEVMODECHANGE:{

      /*  If control panel changes default printer characteristics,
       *  reinitialize our printer information...
       */
      HDC hdc;

      if (hdc = GetPrinterDC ()){
    DeleteDC (hdc);
      }
      break;
  }


  case WM_COMMAND:
      /* Direct all menu selection or accelerator commands to another
       * function
       */
      CommandHandler (hwnd,wParam);
      break;

  case WM_CLOSE:
      /* don't close if any children cancel the operation */
      if (!QueryCloseAllChildren ())
    break;
      DestroyWindow (hwnd);
      break;

  case WM_QUERYENDSESSION:
      /*  Before session ends, check that all files are saved */
      return QueryCloseAllChildren ();

  case WM_DESTROY:
      PostQuitMessage (0);
      break;

  default:
      /*  use DefFrameProc() instead of DefWindowProc() since there
       *  are things that have to be handled differently because of MDI
       */
      return DefFrameProc (hwnd,hwndMDIClient,msg,wParam,lParam);
    }
    return 0;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : MPMDIWndProc ( hwnd, msg, wParam, lParam )        *
 *                      *
 *  PURPOSE    : The window function for the individual document windows,   *
 *     each of which has a "note". Each of these windows contain  *
 *     one multi-line edit control filling their client area.     *
 *     In response to the following:            *
 *                      *
 *         WM_CREATE    : Creates & diplays an edit control *
 *            and does some initialization.     *
 *                      *
 *         WM_MDIACTIVATE  : Activates/deactivates the child.  *
 *                      *
 *         WM_SETFOCUS  : Sets focus on the edit control.   *
 *                      *
 *         WM_SIZE    : Resizes the edit control.      *
 *                      *
 *         WM_COMMAND   : Processes some of the edit      *
 *            commands, saves files and alters  *
 *            the edit wrap state.        *
 *                      *
 *         WM_CLOSE    : Closes child if it is ok to do so.*
 *                      *
 *         WM_QUERYENDSESSION : Same as above.        *
 *                      *
 ****************************************************************************

LONG FAR PASCAL MPMDIChildWndProc ( hwnd, msg, wParam, lParam )

register HWND  hwnd;
WORD    msg;
register WORD  wParam;
LONG    lParam;

{
    HWND hwndEdit;

    switch (msg){
  case WM_CREATE:
      /* Create an edit control */
      hwndEdit = CreateWindow ("edit",
             NULL,
             WS_CHILD|WS_HSCROLL|WS_MAXIMIZE|WS_VISIBLE|WS_VSCROLL|ES_AUTOHSC
             0,
             0,
             0,
             0,
             hwnd,
             ID_EDIT,
             hInst,
             NULL);

      /* Remember the window handle and initialize some window attributes */
      SetWindowWord (hwnd, GWW_HWNDEDIT, (WORD)hwndEdit);
      SetWindowWord (hwnd, GWW_CHANGED, FALSE);
      SetWindowWord (hwnd, GWW_WORDWRAP, FALSE);
      SetWindowWord (hwnd, GWW_UNTITLED, TRUE);
      SetFocus (hwndEdit);
      break;

  case WM_MDIACTIVATE:
      /* If we're activating this child, remember it */
      if (wParam){
    hwndActive     = hwnd;
    hwndActiveEdit = (HWND)GetWindowWord (hwnd, GWW_HWNDEDIT);
      }
      else{
    hwndActive     = NULL;
    hwndActiveEdit = NULL;
      }
      break;

  case WM_QUERYENDSESSION:
      /* Prompt to save the child */
      return !QueryCloseChild (hwnd);

  case WM_CLOSE:
      /* If its OK to close the child, do so, else ignore */
      if (QueryCloseChild (hwnd))
    goto CallDCP;
      else
    break;

  case WM_SIZE:{
      RECT rc;

      /* On creation or resize, size the edit control. */
      hwndEdit = GetWindowWord (hwnd, GWW_HWNDEDIT);
      GetClientRect (hwnd, &rc);
      MoveWindow (hwndEdit,
      rc.left,
      rc.top,
      rc.right-rc.left,
      rc.bottom-rc.top,
      TRUE);
      goto CallDCP;
  }

  case WM_SETFOCUS:
      SetFocus (GetWindowWord (hwnd, GWW_HWNDEDIT));
      break;

  case WM_COMMAND:
      switch (wParam){
    case ID_EDIT:
        switch (HIWORD(lParam)){
      case EN_CHANGE:

          /* If the contents of the edit control have changed,
             set the changed flag
           */
          SetWindowWord (hwnd, GWW_CHANGED, TRUE);
          break;

      case EN_ERRSPACE:
          /* If the control is out of space, honk */
          MessageBeep (0);
          break;

      default:
          goto CallDCP;
        }
        break;

    case IDM_FILESAVE:
        /* If empty file, ignore save */
        if ((GetWindowWord(hwnd, GWW_UNTITLED)) && (!ChangeFile(hwnd)))
      break;

        /* Save the contents of the edit control and reset the
         * changed flag
         */
        SaveFile (hwnd);
        SetWindowWord (hwnd, GWW_CHANGED, FALSE);
        break;

    case IDM_EDITWRAP: {
        int fWrap = GetWindowWord (hwnd, GWW_WORDWRAP);

        /* Set the wrap state, or report it */
        if (LOWORD (lParam)){
      fWrap = !fWrap;
      SetWrap (hwnd, fWrap);
        }

        /* return wrap state */
        return fWrap;
    }

    default:
        goto CallDCP;
      }
      break;

  default:
CallDCP:
      /* Again, since the MDI default behaviour is a little different,
       * call DefMDIChildProc instead of DefWindowProc()
       */
      return DefMDIChildProc (hwnd, msg, wParam, lParam);
    }
    return FALSE;
}


/****************************************************************************
 *                      *
 *  FUNCTION   : AboutDlgProc ( hwnd, msg, wParam, lParam )        *
 *                      *
 *  PURPOSE    : Dialog function for the About MultiPad... dialog.      *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL AboutDlgProc ( hwnd, msg, wParam, lParam )
HWND        hwnd;
register WORD msg;
register WORD wParam;
LONG        lParam;
{
    switch (msg){
  case WM_INITDIALOG:
      /* nothing to initialize */
      break;

  case WM_COMMAND:
      switch (wParam){
    case IDOK:
    case IDCANCEL:
        EndDialog(hwnd, 0);
        break;

    default:
        return FALSE;
      }
      break;

  default:
      return FALSE;
    }

    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : Initializemenu ( hMenu )            *
 *                      *
 *  PURPOSE    : Sets up greying, enabling and checking of main menu items  *
 *     based on the app's state.                                  *
 *                      *
 ****************************************************************************
VOID NEAR PASCAL InitializeMenu ( hmenu )
register HANDLE hmenu;
{
    register WORD status;
    int     i;
    int     j;
    long    l;

    /* Is there any active child to talk to? */
    if (hwndActiveEdit){
  /* If edit control can respond to an undo request, enable the
   * undo selection.
   */
  if (SendMessage (hwndActiveEdit, EM_CANUNDO, 0, 0L))
      status = MF_ENABLED;
  else
      status = MF_GRAYED;
  EnableMenuItem (hmenu, IDM_EDITUNDO, status);

  /* If edit control is non-empty, allow cut/copy/clear */
  l      = SendMessage (hwndActiveEdit, EM_GETSEL, 0, 0L);
  status = (HIWORD(l) == LOWORD(l)) ? MF_GRAYED : MF_ENABLED;
  EnableMenuItem (hmenu, IDM_EDITCUT, status);
  EnableMenuItem (hmenu, IDM_EDITCOPY, status);
  EnableMenuItem (hmenu, IDM_EDITCLEAR, status);

  status=MF_GRAYED;
  /* If the clipboard contains some CF_TEXT data, allow paste */
  if (OpenClipboard (hwndFrame)){
      int wFmt = 0;

      while (wFmt = EnumClipboardFormats (wFmt))
    if (wFmt == CF_TEXT){
        status = MF_ENABLED;
        break;
    }

      CloseClipboard ();
  }
  EnableMenuItem (hmenu, IDM_EDITPASTE, status);

  /* Set the word wrap state for the window */
  if ((WORD) SendMessage (hwndActive, WM_COMMAND, IDM_EDITWRAP, 0L))
      status = MF_CHECKED;
  else
      status = MF_UNCHECKED;
  CheckMenuItem (hmenu, IDM_EDITWRAP, status);

  /* Enable search menu items only if there is a search string */
  if (*szSearch)
      status = MF_ENABLED;
  else
      status = MF_GRAYED;
  EnableMenuItem (hmenu, IDM_SEARCHNEXT, status);
  EnableMenuItem (hmenu, IDM_SEARCHPREV, status);

  /* Enable File/Print only if a printer is available */
  status = iPrinter ? MF_ENABLED : MF_GRAYED;
  EnableMenuItem (hmenu, IDM_FILEPRINT, status);

  /* select all and wrap toggle always enabled */
  status = MF_ENABLED;
  EnableMenuItem(hmenu, IDM_EDITSELECT, status);
  EnableMenuItem(hmenu, IDM_EDITWRAP, status);
  EnableMenuItem(hmenu, IDM_SEARCHFIND, status);
    }
    else {
  /* There are no active child windows */
  status = MF_GRAYED;

  /* No active window, so disable everything */
  for (i = IDM_EDITFIRST; i <= IDM_EDITLAST; i++)
      EnableMenuItem (hmenu, i, status);

  CheckMenuItem (hmenu, IDM_EDITWRAP, MF_UNCHECKED);

  for (i = IDM_SEARCHFIRST; i <= IDM_SEARCHLAST; i++)
      EnableMenuItem (hmenu, i, status);

  EnableMenuItem (hmenu, IDM_FILEPRINT, status);

    }

    /* The following menu items are enabled if there is an active window */
    EnableMenuItem (hmenu, IDM_FILESAVE, status);
    EnableMenuItem (hmenu, IDM_FILESAVEAS, status);
    EnableMenuItem (hmenu, IDM_WINDOWTILE, status);
    EnableMenuItem (hmenu, IDM_WINDOWCASCADE, status);
    EnableMenuItem (hmenu, IDM_WINDOWICONS, status);
    EnableMenuItem (hmenu, IDM_WINDOWCLOSEALL, status);

    /* Allow printer setup only if printer driver supports device initializat
    if (iPrinter < 2)
  status = MF_GRAYED;
    EnableMenuItem ( hmenu, IDM_FILESETUP, status);

}

/****************************************************************************
 *                      *
 *  FUNCTION   : CloseAllChildren ()              *
 *                      *
 *  PURPOSE    : Destroys all MDI child windows.          *
 *                      *
 ****************************************************************************
VOID NEAR PASCAL CloseAllChildren ()
{
    register HWND hwndT;

    /* hide the MDI client window to avoid multiple repaints */
    ShowWindow(hwndMDIClient,SW_HIDE);

    /* As long as the MDI client has a child, destroy it */
    while ( hwndT = GetWindow (hwndMDIClient, GW_CHILD)){

  /* Skip the icon title windows */
  while (hwndT && GetWindow (hwndT, GW_OWNER))
      hwndT = GetWindow (hwndT, GW_HWNDNEXT);

  if (!hwndT)
      break;

  SendMessage (hwndMDIClient, WM_MDIDESTROY, (WORD)hwndT, 0L);
    }
}

/****************************************************************************
 *                      *
 *  FUNCTION   : CommandHandler ()              *
 *                      *
 *  PURPOSE    : Processes all "frame" WM_COMMAND messages.        *
 *                      *
 ****************************************************************************
VOID NEAR PASCAL CommandHandler ( hwnd, wParam )
register HWND hwnd;
register WORD wParam;

{
    switch (wParam){
  case IDM_FILENEW:
      /* Add a new, empty MDI child */
      AddFile (NULL);
      break;

  case IDM_FILEOPEN:
      ReadFile (hwnd);
      break;

  case IDM_FILESAVE:
      /* Save the active child MDI */
      SendMessage (hwndActive, WM_COMMAND, IDM_FILESAVE, 0L);
      break;

  case IDM_FILESAVEAS:
      /* Save active child MDI under another name */
      if (ChangeFile (hwndActive))
    SendMessage (hwndActive, WM_COMMAND, IDM_FILESAVE, 0L);
      break;

  case IDM_FILEPRINT:
      /* Print the active child MDI */
      PrintFile (hwndActive);
      break;

  case IDM_FILESETUP:
      /* Set up the printer environment for this app */
      GetInitializationData (hwnd);
      break;

  case IDM_FILEMENU:{

        /* lengthen / shorten the size of the MDI menu */
        HMENU hMenu;
        HMENU hWindowMenu;
        int i;

        if (lpMenu == IDMULTIPAD){
      lpMenu = IDMULTIPAD2;
      i   = SHORTMENU;
        }
        else{
      lpMenu = IDMULTIPAD;
      i   = WINDOWMENU;
        }

        hMenu = LoadMenu (hInst, lpMenu);
        hWindowMenu = GetSubMenu (hMenu, i);

        /* Set the new menu */
        hMenu = (HMENU)SendMessage (hwndMDIClient,
            WM_MDISETMENU,
            0,
            MAKELONG(hMenu,hWindowMenu));

        DestroyMenu (hMenu);
        DrawMenuBar (hwndFrame);
        break;
  }

  case IDM_FILEEXIT:
      /* Close Multipad */
      SendMessage (hwnd, WM_CLOSE, 0, 0L);
      break;

  case IDM_HELPABOUT:{
      /* Bring up the ubiquitous Ego box */
      FARPROC lpfn;

      lpfn = MakeProcInstance(AboutDlgProc, hInst);
      DialogBox (hInst, IDD_ABOUT, hwnd, lpfn);
      FreeProcInstance (lpfn);
      break;
  }

  /* The following are edit commands. Pass these off to the active
   * child's edit control window.
   */
  case IDM_EDITCOPY:
      SendMessage (hwndActiveEdit, WM_COPY, 0, 0L);
      break;

  case IDM_EDITPASTE:
      SendMessage (hwndActiveEdit, WM_PASTE, 0, 0L);
      break;

  case IDM_EDITCUT:
      SendMessage (hwndActiveEdit, WM_CUT, 0, 0L);
      break;

  case IDM_EDITCLEAR:
      SendMessage (hwndActiveEdit, EM_REPLACESEL, 0,( LONG)(LPSTR)"");
      break;

  case IDM_EDITSELECT:
      SendMessage (hwndActiveEdit, EM_SETSEL, 0, MAKELONG(0, 0xe000));
      break;

  case IDM_EDITUNDO:
      SendMessage (hwndActiveEdit, EM_UNDO, 0, 0L);
      break;

  case IDM_EDITWRAP:
      SendMessage (hwndActive, WM_COMMAND, IDM_EDITWRAP, 1L);
      break;

  case IDM_SEARCHFIND:
      /* Put up the find dialog box */
      Find ();
      break;

  case IDM_SEARCHNEXT:
      /* Find next occurence */
      FindNext ();
      break;

  case IDM_SEARCHPREV:
      /* Find previous occurence */
      FindPrev ();
      break;

  /* The following are window commands - these are handled by the
   * MDI Client.
   */
  case IDM_WINDOWTILE:
      /* Tile MDI windows */
      SendMessage (hwndMDIClient, WM_MDITILE, 0, 0L);
      break;

  case IDM_WINDOWCASCADE:
      /* Cascade MDI windows */
      SendMessage (hwndMDIClient, WM_MDICASCADE, 0, 0L);
      break;

  case IDM_WINDOWICONS:
      /* Auto - arrange MDI icons */
      SendMessage (hwndMDIClient, WM_MDIICONARRANGE, 0, 0L);
      break;

  case IDM_WINDOWCLOSEALL:
      /* Abort operation if something is not saved */
      if (!QueryCloseAllChildren())
    break;

      CloseAllChildren();

      /* Show the window since CloseAllChilren() hides the window
       * for fewer repaints.
       */
      ShowWindow( hwndMDIClient, SW_SHOW);

      break;

  default:
     /*
      * This is essential, since there are frame WM_COMMANDS generated
      * by the MDI system for activating child windows via the
      * window menu.
      */
      DefFrameProc(hwnd, hwndMDIClient, WM_COMMAND, wParam, 0L);
    }
}
/****************************************************************************
 *                      *
 *  FUNCTION   : SetWrap ()                *
 *                      *
 *  PURPOSE    : Changes the word wrapping in an edit control. Since this   *
 *     cannot be done by direct means, the function creates a new *
 *     edit control, moves data from the old control to the new   *
 *     control and destroys the original control. Note that the   *
 *     function assumes that the child being modified is currently*
 *     active.                *      *
 *                      *
 ****************************************************************************

VOID NEAR PASCAL SetWrap(hwnd, fWrap)
HWND hwnd;
BOOL fWrap;

{
    LONG    dws;
    HANDLE  hT;
    HANDLE  hTT;
    HWND    hwndOld;
    HWND    hwndNew;
    RECT    rc;

    /* Change word wrap mode */
    SetWindowWord (hwnd, GWW_WORDWRAP, fWrap);

    /* Create the appropriate window style, adding a horizontal scroll
     * facility if wrapping is not present.
     */
    dws = WS_CHILD | WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE;
    if (!fWrap)
  dws |= WS_HSCROLL | ES_AUTOHSCROLL;

    /* Create a new child window */
    hwndNew = CreateWindow ( "edit",
           NULL,
           dws,
           0,
           SW_SHOW,
           0,
           0,
           hwnd,
           ID_EDIT,
           hInst,
           NULL);

    /* Get handle to current edit control */
    hwndOld = GetWindowWord (hwnd, GWW_HWNDEDIT);

    /* Get the data handle of the old control */
    hT = (HANDLE)SendMessage (hwndOld, EM_GETHANDLE, 0, 0L);

    /* Create a dummy data handle and make it the handle to
     * the old edit control( hT still references the text of
     * old control).
     */
    hTT = LocalAlloc (LHND, 0);
    SendMessage (hwndOld, EM_SETHANDLE, hTT, 0L);

    /* Make the new window the window of interest and destroy the
     * old control.
     */
    SetWindowWord (hwnd, GWW_HWNDEDIT, hwndNew);
    hwndActiveEdit = hwndNew;
    DestroyWindow (hwndOld);

    /* Cause the window to be properly sized */
    SendMessage (hwnd, WM_SIZE, 0, 0L);

    /* Free the new window's old data handle and set it to
     * hT (text of old edit control)
     */
    LocalFree ((HANDLE)SendMessage (hwndNew, EM_GETHANDLE, 0, 0L));
    SendMessage (hwndNew, EM_SETHANDLE, hT, 0L);

    ShowWindow (hwndNew, SW_SHOW);

    /* Set focus to the new edit control */
    SetFocus (hwndNew);

}


/****************************************************************************
 *                      *
 *  FUNCTION   : MPError ( hwnd, flags, id, ...)          *
 *                      *
 *  PURPOSE    : Flashes a Message Box to the user. The format string is    *
 *     taken from the STRINGTABLE.            *
 *                      *
 *  RETURNS    : Returns value returned by MessageBox() to the caller.      *
 *                      *
 ****************************************************************************
short FAR CDECL MPError(hwnd, bFlags, id, ...)
HWND hwnd;
WORD bFlags;
WORD id;
{
    char sz[160];
    char szFmt[128];

    LoadString (hInst, id, szFmt, sizeof (szFmt));
    wvsprintf (sz, szFmt, (LPSTR)(&id + 1));
    LoadString (hInst, IDS_APPNAME, szFmt, sizeof (szFmt));
    return MessageBox (hwndFrame, sz, szFmt, bFlags);
}


/****************************************************************************
 *                      *
 *  FUNCTION   : QueryCloseAllChildren()            *
 *                      *
 *  PURPOSE    : Asks the child windows if it is ok to close up app. Nothing*
 *     is destroyed at this point. The z-order is not changed.    *
 *                      *
 *  RETURNS    : TRUE - If all children agree to the query.        *
 *     FALSE- If any one of them disagrees.          *
 *                      *
 ****************************************************************************

BOOL NEAR PASCAL QueryCloseAllChildren()
{
    register HWND hwndT;

    for ( hwndT = GetWindow (hwndMDIClient, GW_CHILD);
    hwndT;
    hwndT = GetWindow (hwndT, GW_HWNDNEXT)       ){

  /* Skip if an icon title window */
  if (GetWindow (hwndT, GW_OWNER))
      continue;

  if (SendMessage (hwndT, WM_QUERYENDSESSION, 0, 0L))
      return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : QueryCloseChild (hwnd)             *
 *                      *
 *  PURPOSE    : If the child MDI is unsaved, allow the user to save, not   *
 *               save, or cancel the close operation.                       *
 *                      *
 *  RETURNS    : TRUE  - if user chooses save or not save, or if the file   *
 *                       has not changed.                                   *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************

BOOL NEAR PASCAL QueryCloseChild(hwnd)
register HWND hwnd;
{
    char   sz [64];
    register int i;

    /* Return OK if edit control has not changed. */
    if (!GetWindowWord (hwnd, GWW_CHANGED))
  return TRUE;

    GetWindowText (hwnd, sz, sizeof(sz));

    /* Ask user whether to save / not save / cancel */
    i = MPError (hwnd,
     MB_YESNOCANCEL|MB_ICONQUESTION,IDS_CLOSESAVE,
     (LPSTR)sz);

    switch (i){
  case IDYES:
      /* User wants file saved */
      SaveFile(hwnd);
      break;

  case IDNO:
      /* User doesn't want file saved */
      break;

  default:
      /* We couldn't do the messagebox, or not ok to close */
      return FALSE;
    }
    return TRUE;
}


MYPAL.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\MYPAL\MYPAL.C

/***************************************************************************
 *                     *
 *  PROGRAM  : MyPal.c               *
 *                     *
 *  PURPOSE  : Sets up a bar representation of the current physical     *
 *      palette and displays useful information regarding     *
 *      pixel colors and palette indices.         *
 *                     *
 *  FUNCTIONS  : WinMain() - calls initialization function,       *
 *            processes message loop         *
 *                     *
 *      WndProc() - Window function for app. Processes     *
 *            window messages.           *
 *                     *
 *    ShowColor() - Displays a little box on each side of the    *
 *            caption bar displaying the pixel color at the*
 *            mouse position.           *
 ***************************************************************************/

#include <windows.h>
#include "mypal.h"

HANDLE    hPal;         /* Handle to the application's logical palette */
static WORD  nSizeX;        /* Width of the application window        */
static WORD  nSizeY;        /* Height of the application window        */
NPLOGPALETTE  pLogPal;       /* Pointer to program's logical palette        *
WORD    nXBorder;      /* Width of window border          */
WORD    nXTitle;       /* Width of title bar            */
WORD    nYTitle;       /* Height of title bar            */
BOOL    bCaptureOn;    /* Indicates if mouse capture is on        */
int    iIndex;        /* Last index selected in palette        */
char    szTitlebuf[90];/* Buffer for pixel and palette info. text     */
HDC    hDCGlobal;     /* The Screen DC             */
int    iNumColors;    /* Number of colors supported by device        */
int    iRasterCaps;   /* Raster capabilities            */
int    iPalSize;      /* Size of Physical palette          */
RECT    rClientRect;   /* Client rectangle coordinates          */
DWORD   dwPal[PALETTESIZE];   /* Stores palette entries for later lookup
int    iGlobalXOffset;
int    iGlobalYOffset;
int    iYMiddle;

long FAR PASCAL WndProc (HWND, unsigned, WORD, LONG) ;

/****************************************************************************
 *                      *
 *  FUNCTION   : void ShowColor(HWND hWnd, HDC hDC)          *
 *                      *
 *  PURPOSE    : Displays a little box on each side of the caption bar      *
 *     displaying the pixel color at the mouse position.      *
 *                      *
 ****************************************************************************
void ShowColor (hWnd, hDC)
HWND  hWnd;
HDC   hDC;
{
     HBRUSH  hBrush, hOldBrush;

     hBrush    = CreateSolidBrush ( PALETTEINDEX(iIndex) );
     hOldBrush = SelectObject (hDC,hBrush) ;

     GetWindowRect (hWnd, (LPRECT)&rClientRect);

     PatBlt ( hDC,
        rClientRect.left + nXTitle + nXBorder + 1,
        rClientRect.top + nXBorder,
        nXTitle,
        nYTitle,
        PATCOPY);

     PatBlt(hDC,
      rClientRect.right - ( 3 * nXTitle + nXBorder + 2),
      rClientRect.top + nXBorder,
      nXTitle,
      nYTitle,
      PATCOPY);
     SelectObject (hDC, hOldBrush);
     DeleteObject (hBrush) ;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : WinMain(HANDLE, HANDLE, LPSTR, int)          *
 *                      *
 *  PURPOSE    : Creates the app. window and processes the message loop.    *
 *                      *
 ****************************************************************************
int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)

HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR  lpszCmdLine;
int    nCmdShow;

{
     static char szAppName [] = "MyPal";
     HWND   hWnd;
     WNDCLASS   wndclass;
     MSG   msg ;
     short int   xScreen;
     short int   yScreen;

     if (!hPrevInstance){
   wndclass.style   = CS_HREDRAW | CS_VREDRAW;
   wndclass.lpfnWndProc  = WndProc;
   wndclass.cbClsExtra  = 0;
   wndclass.cbWndExtra  = 0;
   wndclass.hInstance  = hInstance;
   wndclass.hIcon   = LoadIcon(hInstance, szAppName);
   wndclass.hCursor  = LoadCursor (NULL, IDC_ARROW);
   wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
   wndclass.lpszMenuName  = szAppName;
   wndclass.lpszClassName = szAppName;

   if (!RegisterClass (&wndclass))
       return FALSE ;
     }

     /* Do some global initializations */
     xScreen   = GetSystemMetrics (SM_CXSCREEN);
     yScreen   = GetSystemMetrics (SM_CYSCREEN);
     nXBorder   = GetSystemMetrics (SM_CXFRAME);
     nXTitle   = GetSystemMetrics (SM_CXSIZE);
     nYTitle   = GetSystemMetrics (SM_CYSIZE);
     iIndex   = 0;
     bCaptureOn  = FALSE;

     hDCGlobal   = GetDC (NULL);
     iPalSize   = GetDeviceCaps (hDCGlobal, SIZEPALETTE);
     iRasterCaps = GetDeviceCaps (hDCGlobal, RASTERCAPS);
     iRasterCaps = (iRasterCaps & RC_PALETTE) ? TRUE : FALSE;
     ReleaseDC (NULL,hDCGlobal);

     if (iRasterCaps)
   iNumColors = GetDeviceCaps(hDCGlobal, SIZEPALETTE);
     else
   iNumColors = GetDeviceCaps( hDCGlobal, NUMCOLORS);

     nSizeX = ((xScreen - 2*nXBorder) / PALETTESIZE) * PALETTESIZE;

     /* create the app. window */
     hWnd = CreateWindow (szAppName,
        "My Physical Palette ",
        WS_OVERLAPPEDWINDOW,
        (xScreen-nSizeX) / 2 - nXBorder,
        yScreen - ( 4 * GetSystemMetrics (SM_CYCAPTION)),
        nSizeX + 2 * nXBorder,
        4 * GetSystemMetrics (SM_CYCAPTION),
        NULL,
        NULL,
        hInstance,
        NULL);
     ShowWindow (hWnd, nCmdShow);
     UpdateWindow (hWnd);

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

     return msg.wParam ;
}

/****************************************************************************
 *                        *
 *  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)            *
 *                        *
 *  PURPOSE:  Processes window messages and sets up a 256 bar representation
 *        of the current physical palette. Specifically, in response to:  *
 *                        *
 *     WM_CREATE  -Allocates for and sets up a LOGPALETTE        *
 *           structure, creates a logical palette the same    *
 *           size as the physical palette and obtains a       *
 *           handle to the logical palette.          *
 *                        *
 *     WM_DESTROY -Destroys the logical palette and shuts down app. *
 *                        *
 *     WM_PAINT   -Resizes client area to hold as many vertical     *
 *           color bars as there are physical palette entries.*
 *           Also realises the current logical palette and    *
 *           draws one color bar corresponding to each        *
 *           palette entry              *
 *                        *
 *       WM_RBUTTONDOWN -Captures the mouse and initiates the below       *
 *           process:                *
 *                        *
 *       WM_MOUSEMOVE   -Following a WM_RBUTTONDOWN, if the right mouse   *
 *           key is depressed, displays info about the        *
 *           pixel RGB value and palette index of the mouse   *
 *           coordinates.              *
 *                        *
 *       WM_RBUTTONUP   -Release mouse capture and terminates the above   *
 *           process                *
 *                        *
 *       WM_LBUTTONDOWN -Determines and displays the palette index and    *
 *           RGB value of the bar under the mouse.        *
 *                        *
 *       WM_KEYDOWN     -Allows use of the arrow keys in stepping thro'   *
 *           palette entries.              *
 *                        *
 ****************************************************************************
long FAR PASCAL WndProc (hWnd, iMessage, wParam, lParam)

HWND   hWnd;
unsigned iMessage;
WORD   wParam;
LONG   lParam;

{
    HDC     hDC;
    PAINTSTRUCT   ps;
    WORD    iLoop;
    WORD    nStart;
    HMENU    hMenu;
    HBRUSH    hBrush;
    HBRUSH    hOldBrush;

    POINT    pt;
    static WORD   nIncr;
    static DWORD  dwColor;
    static DWORD  dwLastColor;
    static int    i, x;

    switch (iMessage){
   case WM_DESTROY:
        /* delete the handle to the logical palette if it has any
         * color entries and quit.
         */
        if (pLogPal->palNumEntries)
      DeleteObject (hPal);
        PostQuitMessage (0) ;
        break ;

   case WM_CREATE:
        /* Allocate enough memory for a logical palette with
         * PALETTESIZE entries and set the size and version fields
         * of the logical palette structure.
         */
        pLogPal = (NPLOGPALETTE) LocalAlloc (LMEM_FIXED,
              (sizeof (LOGPALETTE) +
              (sizeof (PALETTEENTRY) * (PALETTESIZE))));

        pLogPal->palVersion    = 0x300;
        pLogPal->palNumEntries = PALETTESIZE;

        /* fill in intensities for all palette entry colors */
        for (iLoop = 0; iLoop < PALETTESIZE; iLoop++) {
      *((WORD *) (&pLogPal->palPalEntry[iLoop].peRed)) = iLoop;
      pLogPal->palPalEntry[iLoop].peBlue  = 0;
      pLogPal->palPalEntry[iLoop].peFlags = PC_EXPLICIT;
        }

        /*  create a logical color palette according the information
         *  in the LOGPALETTE structure.
         */
        hPal = CreatePalette ((LPLOGPALETTE) pLogPal) ;
        break;

   case WM_GETMINMAXINFO:

        ((LPRGPT)lParam)->iInfo[6] = nXBorder * 2 + PALETTESIZE;
        ((LPRGPT)lParam)->iInfo[7] = nXBorder * 2 + nYTitle*3;

        return DefWindowProc (hWnd, iMessage, wParam, lParam) ;
        break;

   case WM_PAINT:

        /* Divide client width into equal-sized parts, one per palette
         * entry, and re-calculate client width so that it will display
         * exactly as many vertical bars as there are palette entries.
         */
         GetClientRect(hWnd,(LPRECT) &rClientRect);
         nSizeX = (rClientRect.right - rClientRect.left);
         nSizeX = (nSizeX/iNumColors) * iNumColors;

         nSizeY = rClientRect.bottom - rClientRect.top;
         GetWindowRect(hWnd,(LPRECT) &rClientRect);

        /* Adjust window width so that it can display exactly
         * as many vertical bars( of equal width) as there are palette
         * colors.
         */

        SetWindowPos( hWnd,
          (HWND)NULL,
          0,
          0,
          nSizeX + 2*nXBorder,
          rClientRect.bottom - rClientRect.top,
          SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

        hDC = BeginPaint(hWnd, &ps);

        /* Select the palette into the window device context and
         * make the Palette Manager map the logical palette to the
         * system palette (realize it).
         */
        SelectPalette (hDC, hPal, 1);
        RealizePalette (hDC);

        /* Calculate width of each color bar to be displayed */
        nIncr = nSizeX / iNumColors;

        /* Paint the individual bars separately on the app. window */
        for (nStart = iLoop = 0; iLoop < iNumColors; iLoop++){

      /* Since this app. uses a logical palette, use the
       * PALETTEINDEX macro to specify the palette entry
       * index instead of using an explicit RGB value.
       */
      hBrush       = CreateSolidBrush (PALETTEINDEX (iLoop));
      dwPal[iLoop] = GetNearestColor (hDC, PALETTEINDEX (iLoop) );
      hOldBrush    = SelectObject (hDC,hBrush) ;
      PatBlt (hDC, nStart, 0, nIncr, nSizeY, PATCOPY);
      nStart       += nIncr;
      SelectObject (hDC, hOldBrush);
      DeleteObject (hBrush) ;
        }
        wsprintf (szTitlebuf, "MyPal Colors= %d", iNumColors);
        SetWindowText (hWnd, (LPSTR)szTitlebuf);

        EndPaint(hWnd,&ps);

        break ;

   case WM_MOUSEMOVE:

        if (wParam & MK_RBUTTON) {

      /* Convert mouse position to screen coordinates */
      pt.x = LOWORD(lParam);
      pt.y = HIWORD(lParam);
      ClientToScreen(hWnd,&pt);

      /* Get RGB value (color) of pixel under mouse coordinate */
      dwColor = GetPixel(hDCGlobal, pt.x, pt.y);

      /* If color value already exists in palette lookup table,
       * obtain it's index.
       */
      for (i=0 ; i < iNumColors ; i++)
          if ( dwColor == dwPal[i] )
        break;
      iIndex = i;

      /* If previous color value was not identical to current one,
       * display color boxes on either side of title bar,
       * the R, G, B values and palette index of current color.
       */
      if (dwColor != dwLastColor) {
          wsprintf ( szTitlebuf,
         "MyPal Colors=%d  Index=%d  R=%3u G=%3u B=%3u",
         iNumColors,
         iIndex,
         (WORD)(BYTE) GetRValue (dwColor),
         (WORD)(BYTE) GetGValue (dwColor),
         (WORD)(BYTE) GetBValue (dwColor));
          SetWindowText (hWnd, (LPSTR)szTitlebuf);
          ShowColor (hWnd, hDCGlobal);
          dwLastColor = dwColor;
      }
        }
        break;

   case WM_RBUTTONDOWN:

        /* Determine number of color bar under mouse, thus the index
         * of color in palette.
         */
        x = LOWORD(lParam);
        iIndex = (x / nIncr );

        wsprintf ( szTitlebuf,
       "MyPal Colors=%d  Index=%d  PalSize=%d RasterCaps:%d",
       iNumColors,
       iIndex,
       iPalSize,
       iRasterCaps );

        SetWindowText (hWnd, (LPSTR)szTitlebuf);

        /* Set mouse capture so that subsequent WM_MOUSEMOVEs
         * (with right mouse button depressed) will allow MyPal
         * to display RGB info anywhere on the screen without losing
         * the focus.
         */
        SetCapture (hWnd);
        bCaptureOn = TRUE;
        hDCGlobal = GetDC(NULL);
        if (hPal) {
      SelectPalette (hDCGlobal, hPal, FALSE);
      RealizePalette (hDCGlobal);
        }
        break;

   case WM_RBUTTONUP:
        /* Stops displaying RGB and palette info and releases mouse
         * capture
         */
        ReleaseDC (NULL, hDCGlobal);
        bCaptureOn = FALSE;
        ReleaseCapture ();
        break;

   case WM_MOVE:
        /* If you have a wide column, this adds 1/2 so X is centered */
        iGlobalXOffset  = LOWORD (lParam);
        iGlobalYOffset  = HIWORD (lParam) + nXBorder;
        break;

   case WM_SIZE:
        iYMiddle = (HIWORD (lParam)/2);
        break;

   case WM_LBUTTONDOWN:
   case WM_KEYDOWN:

       if (iMessage == WM_LBUTTONDOWN){
     /* determine which column was hit by the mouse */
     x = LOWORD(lParam);
     iIndex = (x / nIncr );
       }
       else{
     /* Use arrow keys to step thro' the palette entries */
     switch (wParam) {
         case VK_RIGHT:
         case VK_UP:
          /* go to next (higher) palette entry */
          iIndex++;
          break;
         case VK_LEFT:
         case VK_DOWN:
          /* go to previous (lower) palette entry */
          iIndex--;
          break;
         case VK_NEXT:
          iIndex += 10;
          break;
         case VK_PRIOR:
          iIndex -= 10;
          break;
         case VK_HOME:
          /* go to first palette entry */
          iIndex = 0;
          break;
         case VK_END:
          /* go to last palette entry */
          iIndex = iNumColors-1;
          break;
         default:
          return 0L;
          break;
     }
     /* Make sure the palette index is within range else
      * set it to the limiting values and give a warning beep.
      */
     if (iIndex < 0) {
         iIndex = 0;
         MessageBeep(1);
     }
     else{
         if (iIndex > iNumColors-1) {
       iIndex = iNumColors-1;
       MessageBeep(1);
          }
     }

     pt.x = (iIndex * nIncr) +
      iGlobalXOffset   +
      ((nIncr > 1) ? (nIncr / 2) : 1);
     pt.y = iYMiddle + iGlobalYOffset;

     SetCursorPos (pt.x, pt.y);
       }

       if (TRUE == bCaptureOn) {
     MessageBeep(1);
     break;
       }

       /* Select & realize the palette or the colors > 0x7
        * will not match up.
        */
       hDC = GetDC(NULL);
       SelectPalette  (hDC, hPal, 1);
       RealizePalette (hDC) ;

       dwColor = GetNearestColor (hDC, PALETTEINDEX (iIndex));

       wsprintf ( szTitlebuf,
      "MyPal Colors=%d  Index=%d  R=%3u G=%3u B=%3u",
      iNumColors,
      iIndex,
      (WORD)(BYTE)GetRValue (dwColor),
      (WORD)(BYTE)GetGValue (dwColor),
      (WORD)(BYTE)GetBValue (dwColor)
         );

       SetWindowText (hWnd, (LPSTR)szTitlebuf);
       ShowColor (hWnd,hDC);
       ReleaseDC(NULL, hDC);
       break;

   default:
        return DefWindowProc (hWnd, iMessage, wParam, lParam) ;

    }
    return 0L ;
}


OUTPUT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\OUTPUT\OUTPUT.C

/****************************************************************************

    PROGRAM: Output.c

    PURPOSE: Output template for Windows applications

    FUNCTIONS:

  WinMain() - calls initialization function, processes message loop
  InitApplication() - initializes window data and registers window
  InitInstance() - saves instance handle and creates main window
  MainWndProc() - processes messages
  About() - processes messages for "About" dialog box

****************************************************************************/

#include "windows.h"
#include "string.h"
#include "output.h"

HANDLE hInst;

HPEN hDashPen;                                         /* "---" pen handle
HPEN hDotPen;                                          /* "..." pen handle
HBRUSH hOldBrush;                                      /* old brush handle
HBRUSH hRedBrush;                                      /* red brush handle
HBRUSH hGreenBrush;                                    /* green brush handle
HBRUSH hBlueBrush;                                     /* blue brush handle


/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
  if (!InitApplication(hInstance))
      return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "OutputMenu";
    wc.lpszClassName = "OutputWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    HWND            hWnd;

    hInst = hInstance;

    hWnd = CreateWindow(
        "OutputWClass",
        "Output Sample Application",
        WS_OVERLAPPEDWINDOW,
  0,
  0,
  GetSystemMetrics(SM_CXSCREEN),
  GetSystemMetrics(SM_CYSCREEN),
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hWnd)
        return (FALSE);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

  WM_COMMAND    - application menu (About dialog box)
  WM_CREATE     - create window and objects
  WM_PAINT      - update window, draw objects
  WM_DESTROY    - destroy window

    COMMENTS:

  Handles to the objects you will use are obtained when the WM_CREATE
  message is received, and deleted when the WM_DESTROY message is
  received.  The actual drawing is done whenever a WM_PAINT message is
  received.

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout;

    HDC hDC;                          /* display-context variable  */
    PAINTSTRUCT ps;                   /* paint structure           */
    RECT rcTextBox;                   /* rectangle around the text */
    HPEN hOldPen;                     /* old pen handle            */



    switch (message) {
  case WM_COMMAND:
      if (wParam == IDM_ABOUT) {
    lpProcAbout = MakeProcInstance(About, hInst);

    DialogBox(hInst,
        "AboutBox",
        hWnd,
        lpProcAbout);

    FreeProcInstance(lpProcAbout);
    break;
      }
      else
    return (DefWindowProc(hWnd, message, wParam, lParam));

        case WM_CREATE:

            /* Create the brush objects */

            hRedBrush =   CreateSolidBrush(RGB(255,   0,   0));
            hGreenBrush = CreateSolidBrush(RGB(  0, 255,   0));
            hBlueBrush =  CreateSolidBrush(RGB(  0,   0, 255));

            /* Create the "---" pen */

            hDashPen = CreatePen(PS_DASH,                /* style */
                1,                                       /* width */
                RGB(0, 0, 0));                           /* color */

            /* Create the "..." pen */

            hDotPen = CreatePen(2,                       /* style */
                1,                                       /* width */
                RGB(0, 0, 0));                           /* color */
            break;

        case WM_PAINT:
            {
    TEXTMETRIC textmetric;
    int nDrawX;
    int nDrawY;
    char szText[300];

    /* Set up a display context to begin painting */

                hDC = BeginPaint (hWnd, &ps);

                /* Get the size characteristics of the current font.  */
                /* This information will be used for determining the  */
                /* vertical spacing of text on the screen.            */

                GetTextMetrics (hDC, &textmetric);

                /* Initialize drawing position to 1/4 inch from the top  */
                /* and from the left of the top, left corner of the      */
                /* client area of the main windows.                      */

                nDrawX = GetDeviceCaps (hDC, LOGPIXELSX) / 4;   /* 1/4 inch *
                nDrawY = GetDeviceCaps (hDC, LOGPIXELSY) / 4;   /* 1/4 inch *

                /* Send characters to the screen.  After displaying each   */
                /* line of text, advance the vertical position for the     */
                /* next line of text.  The pixel distance between the top  */
                /* of each line of text is equal to the standard height of */
                /* the font characters (tmHeight), plus the standard       */
                /* amount of spacing (tmExternalLeading) between adjacent  */
                /* lines.                                                  */

                strcpy (szText, "These characters are being painted using ");
                TextOut (hDC, nDrawX, nDrawY, szText, strlen (szText));
                nDrawY += textmetric.tmExternalLeading + textmetric.tmHeight;

                strcpy (szText, "the TextOut() function, which is fast and ")
                TextOut (hDC, nDrawX, nDrawY, szText, strlen (szText));
                nDrawY += textmetric.tmExternalLeading + textmetric.tmHeight;

                strcpy (szText, "allows programmer control of placement and "
                TextOut (hDC, nDrawX, nDrawY, szText, strlen (szText));
                nDrawY += textmetric.tmExternalLeading + textmetric.tmHeight;

                strcpy (szText, "formatting details.  However, TextOut() ");
                TextOut (hDC, nDrawX, nDrawY, szText, strlen (szText));
                nDrawY += textmetric.tmExternalLeading + textmetric.tmHeight;

                strcpy (szText, "does not provide any automatic formatting.")
                TextOut (hDC, nDrawX, nDrawY, szText, strlen (szText));
                nDrawY += textmetric.tmExternalLeading + textmetric.tmHeight;

                /* Put text in a 5-inch by 1-inch rectangle and display it. *
                /* First define the size of the rectangle around the text   *

                nDrawY += GetDeviceCaps (hDC, LOGPIXELSY) / 4;  /* 1/4 inch *
                SetRect (
                      &rcTextBox
                    , nDrawX
                    , nDrawY
                    , nDrawX + (5 * GetDeviceCaps (hDC, LOGPIXELSX)) /* 5" */
                    , nDrawY + (1 * GetDeviceCaps (hDC, LOGPIXELSY)) /* 1" */
                );

                /* Draw the text within the bounds of the above rectangle */

                strcpy (szText, "This text is being displayed with a single "
                            "call to DrawText().  DrawText() isn't as fast "
                            "as TextOut(), and it is somewhat more "
                            "constrained, but it provides numerous optional "
                            "formatting features, such as the centering and "
                            "line breaking used in this example.");
                DrawText (
                      hDC
                    , szText
                    , strlen (szText)
                    , &rcTextBox
                    , DT_CENTER | DT_EXTERNALLEADING | DT_NOCLIP
                                                | DT_NOPREFIX | DT_WORDBREAK
                );

                /*  Paint the next object immediately below the bottom of   *
                /*  the above rectangle in which the text was drawn.        *

                nDrawY = rcTextBox.bottom;

                /* The (x,y) pixel coordinates of the objects about to be   *
                /* drawn are below, and to the right of, the current        *
                /* coordinate (nDrawX,nDrawY).                              *

                /* Draw a red rectangle.. */

                hOldBrush = SelectObject(hDC, hRedBrush);
                Rectangle (
                      hDC
                    , nDrawX
                    , nDrawY
                    , nDrawX + 50
                    , nDrawY + 30
                );

                /* Draw a green ellipse */

                SelectObject(hDC, hGreenBrush);
                Ellipse (
                      hDC
                    , nDrawX + 150
                    , nDrawY
                    , nDrawX + 150 + 50
                    , nDrawY + 30
                );

                /* Draw a blue pie shape */

                SelectObject(hDC, hBlueBrush);
                Pie (
                      hDC
                    , nDrawX + 300
                    , nDrawY
                    , nDrawX + 300 + 50
                    , nDrawY + 50
                    , nDrawX + 300 + 50
                    , nDrawY
                    , nDrawX + 300 + 50
                    , nDrawY + 50
                );

                nDrawY += 50;

                /* Restore the old brush */

                SelectObject(hDC, hOldBrush);

                /* Select a "---" pen, save the old value */

                nDrawY += GetDeviceCaps (hDC, LOGPIXELSY) / 4;  /* 1/4 inch *
                hOldPen = SelectObject(hDC, hDashPen);

                /* Move to a specified point */

                MoveTo(hDC, nDrawX, nDrawY);

                /* Draw a line */

                LineTo(hDC, nDrawX + 350, nDrawY);

                /* Select a "..." pen */

                SelectObject(hDC, hDotPen);

                /* Draw an arc connecting the line */

                Arc (
                      hDC
                    , nDrawX
                    , nDrawY - 20
                    , nDrawX + 350
                    , nDrawY + 20
                    , nDrawX
                    , nDrawY
                    , nDrawX + 350
                    , nDrawY
                );

                /* Restore the old pen */

                SelectObject(hDC, hOldPen);

                /* Tell Windows you are done painting */

                EndPaint (hWnd,  &ps);
            }
            break;

  case WM_DESTROY:
            DeleteObject(hRedBrush);
            DeleteObject(hGreenBrush);
            DeleteObject(hBlueBrush);
            DeleteObject(hDashPen);
            DeleteObject(hDotPen);
      PostQuitMessage(0);
      break;

  default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

  WM_INITDIALOG - initialize dialog box
  WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


OWNCOMBO.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\OWNCOMBO\OWNCOMBO.C

/***************************************************************************
 *                     *
 *  PROGRAM  : OwnCombo.c               *
 *                     *
 *  PURPOSE  : Illustrates the use of functions and messages for     *
 *      combo boxes and owner-draw control styles.       *
 *                     *
 *  FUNCTIONS  : WinMain                - Creates the app. window and     *
 *             enters the message loop.     *
 *                     *
 *      OwnComboInit           - Registers the main window class *
 *                     *
 *      About                  - Dialog function for the About   *
 *             dialog.         *
 *                     *
 *      OwnComboWndProc        - Window function for app. It     *
 *             handles the menu selections     *
 *             and processes the other window  *
 *             messages.         *
 *                     *
 *                DrawEntireItem         - Handles the drawing of a list   *
 *                                         list box or combo box item.     *
 *                     *
 *                HandleSelectionState   - Handles the selecting/deselect- *
 *                                         ing of a list box or combo box  *
 *                                         item.                           *
 *                     *
 *                HandleFocusState       - Handles the getting/losing of   *
 *                                         the input focus by a list box   *
 *                     *
 *      ListBoxExample         - Dialog function for the     *
 *             owner-draw list box example.    *
 *                     *
 *      ComboBoxExample        - Dialog function for the text    *
 *             combo dialog.       *
 *                     *
 *      OwnerComboBoxExample   - Dialog fubction for the drop-   *
 *             down-list combobox with     *
 *             ownerdraw.         *
 *                     *
 ***************************************************************************/
#include "windows.h"
#include "owncombo.h"

HANDLE  hInst;

/****************************************************************************
 *                      *
 *  FUNCTION   : WinMain(HANDLE, HANDLE, LPSTR, int)          *
 *                      *
 *  PURPOSE    : Creates the app. window and enters the message loop.      *
 *                      *
 ****************************************************************************
int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)

HANDLE hInstance, hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    HWND  hWnd;
    MSG   msg;

    if (!hPrevInstance)
  if (!OwnComboInit (hInstance))
      return (NULL);

    hInst = hInstance;

    /* Create the app. window */
    hWnd = CreateWindow ("owncombo",
       "Owner-draw & Combo Box Example",
       WS_OVERLAPPEDWINDOW,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       (HWND) NULL,
       NULL,
       hInstance,
       (LPSTR) NULL);

    if (!hWnd)
  return (NULL);

    ShowWindow (hWnd, nCmdShow);

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

    return(msg.wParam);
}


/****************************************************************************
 *                      *
 *  FUNCTION   : OwnComboInit (hInstance)            *
 *                      *
 *  PURPOSE    : Registers the main window class.          *
 *                      *
 *  RETURNS    : TRUE  - if RegisterClass () succeeds.          *
 *     FALSE - if RegisterClass () fails.          *
 *                      *
 ****************************************************************************
BOOL NEAR PASCAL OwnComboInit (hInstance)

HANDLE hInstance;
{
    HANDLE     hMemory;
    PWNDCLASS  pWndClass;
    BOOL       bSuccess;

    /* Allocate for and fill class structure. */
    hMemory = LocalAlloc (LPTR, sizeof (WNDCLASS));
    pWndClass = (PWNDCLASS) LocalLock (hMemory);

    pWndClass->style       = NULL;
    pWndClass->lpfnWndProc   = OwnComboWndProc;
    pWndClass->hInstance     = hInstance;
    pWndClass->hIcon       = LoadIcon (hInstance, "owncombo");
    pWndClass->hCursor       = LoadCursor (NULL, IDC_ARROW);
    pWndClass->hbrBackground = GetStockObject (WHITE_BRUSH);
    pWndClass->lpszMenuName  = (LPSTR) "OwnComboMenu",
    pWndClass->lpszClassName = (LPSTR) "owncombo";

    bSuccess = RegisterClass (pWndClass);
    LocalUnlock (hMemory);
    LocalFree (hMemory);

    return (bSuccess);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : About (hDlg,message, wParam, lParam)          *
 *                      *
 *  PURPOSE    : Dialog function for the About... dialog.        *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL About (hDlg, message, wParam, lParam)

HWND   hDlg;
unsigned message;
WORD   wParam;
LONG   lParam;

{
    switch (message){
  case WM_INITDIALOG:
      return(TRUE);

  case WM_COMMAND:
      if (wParam == IDOK){
    EndDialog (hDlg,NULL);
    return(FALSE);
      }
      break;

  default:
      break;
    }
  return(FALSE);
}


/****************************************************************************
 *                      *
 *  FUNCTION   : OwnComboWndProc(hWnd, message, wParam, lParam)       *
 *                      *
 *  PURPOSE    : Window function for the app. It handles menu selections    *
 *     and processes window WM_ messages.          *
 *                      *
 ****************************************************************************
long FAR PASCAL OwnComboWndProc (hWnd, message, wParam, lParam)

HWND   hWnd;
unsigned message;
WORD   wParam;
LONG   lParam;

{
    FARPROC       lpProc;
    HMENU       hMenu;
    LPDRAWITEMSTRUCT dis;
    RECT       rc;

    switch (message){
  case WM_COMMAND:
      switch (wParam){
    case IDM_EXIT:
        DestroyWindow (hWnd);
        break;

    case IDM_ABOUT:
        /* Bring up the about box */
        lpProc = MakeProcInstance (About, hInst);
        DialogBox (hInst,
             "AboutBox",
             hWnd,
             lpProc);

        FreeProcInstance (lpProc);
        break;

    case IDM_LISTBOX:
        /* Bring up the list box example */
        lpProc = MakeProcInstance (ListBoxExample, hInst);
        DialogBox (hInst,
             "ListBoxDialog",
             hWnd,
             lpProc);
        FreeProcInstance (lpProc);
        break;

    case IDM_MULTILISTBOX:
        /* Bring up the multiple selection list box example */
        lpProc = MakeProcInstance (ListBoxExample, hInst);
        DialogBox (hInst,
             "MultiListBoxDialog",
             hWnd,
             lpProc);
        FreeProcInstance (lpProc);
        break;

    case IDM_COMBOBOX:
        /* Bring up the combo box example */
        lpProc = MakeProcInstance (ComboBoxExample, hInst);
        DialogBox (hInst,
             "ComboBoxDialog",
             hWnd,
             lpProc);
        FreeProcInstance (lpProc);
        break;

    case IDM_OWNERCOMBOBOX:
        /* Bring up the owner-draw dropdown list box example */
        lpProc = MakeProcInstance (OwnerComboBoxExample, hInst);
        DialogBox (hInst,
             "OwnerComboBoxDialog",
             hWnd,
             lpProc);
        FreeProcInstance (lpProc);
        break;
      }
      break;

  case WM_DESTROY:
      PostQuitMessage (0);
      break;

  default:
      return(DefWindowProc(hWnd, message, wParam, lParam));
    }
    return(NULL);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : HandleSelectionState(LPDRAWITEMSTRUCT, int)                *
 *                                                                          *
 *  PURPOSE    : Handles a change in an item selection state. If an item is *
 *               selected, a black rectangular frame is drawn around that   *
 *               item; if an item is de-selected, the frame is removed.     *
 *                                                                          *
 *  COMMENT    : The black selection frame is slightly larger than the gray *
 *               focus frame so they won't paint over each other.           *
 *                                                                          *
 ****************************************************************************
void FAR PASCAL HandleSelectionState(lpdis, inflate)
  LPDRAWITEMSTRUCT  lpdis;
  int      inflate;
{
  RECT  rc;
  HBRUSH  hbr;

  /* Resize rectangle to place selection frame outside of the focus
   * frame and the item.
   */
  CopyRect ((LPRECT)&rc, (LPRECT)&lpdis->rcItem);
  InflateRect ((LPRECT)&rc, inflate, inflate);

  if (lpdis->itemState & ODS_SELECTED)
  {
    /* selecting item -- paint a black frame */
    hbr = GetStockObject(BLACK_BRUSH);
  }
  else
  {
    /* de-selecting item -- remove frame */
    hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  }
  FrameRect(lpdis->hDC, (LPRECT)&rc, hbr);
  DeleteObject (hbr);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : HandleFocusState(LPDRAWITEMSTRUCT, int)                    *
 *                                                                          *
 *  PURPOSE    : Handle a change in item focus state. If an item gains the  *
 *               input focus, a gray rectangular frame is drawn around that *
 *               item; if an item loses the input focus, the gray frame is  *
 *               removed.                                                   *
 *                                                                          *
 *  COMMENT    : The gray focus frame is slightly smaller than the black    *
 *               selection frame so they won't paint over each other.       *
 *                                                                          *
 ****************************************************************************
void FAR PASCAL HandleFocusState(lpdis, inflate)
  LPDRAWITEMSTRUCT  lpdis;
  int      inflate;
{
  RECT  rc;
  HBRUSH  hbr;

  /* Resize rectangle to place focus frame between the selection
   * frame and the item.
   */
  CopyRect ((LPRECT)&rc, (LPRECT)&lpdis->rcItem);
  InflateRect ((LPRECT)&rc, inflate, inflate);

  if (lpdis->itemState & ODS_FOCUS)
  {
    /* gaining input focus -- paint a gray frame */
    hbr = GetStockObject(GRAY_BRUSH);
  }
  else
  {
    /* losing input focus -- remove (paint over) frame */
    hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  }
  FrameRect(lpdis->hDC, (LPRECT)&rc, hbr);
  DeleteObject (hbr);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DrawEntireItem(LPDRAWITEMSTRUCT, int)                      *
 *                                                                          *
 *  PURPOSE    : Draws an item and frames it with a selection frame and/or  *
 *               a focus frame when appropriate.                            *
 *                                                                          *
 ****************************************************************************
void FAR PASCAL DrawEntireItem(lpdis, inflate)
  LPDRAWITEMSTRUCT  lpdis;
  int      inflate;
{
  RECT  rc;
  HBRUSH  hbr;

  /* Resize rectangle to leave space for frames */
  CopyRect ((LPRECT)&rc, (LPRECT)&lpdis->rcItem);
  InflateRect ((LPRECT)&rc, inflate, inflate);

  /* Create a brush using the value in the item data field (this value
   * was initialized when we added the item to the list/combo box using
   * LB_ADDSTRING/CB_ADDSTRING) and draw the color in the list/combo box.
   */
  hbr = CreateSolidBrush (lpdis->itemData);
  FillRect (lpdis->hDC, (LPRECT)&rc, hbr);
  DeleteObject (hbr);

  /* Draw or erase appropriate frames */
  HandleSelectionState(lpdis, inflate + 4);
  HandleFocusState(lpdis, inflate + 2);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : ListBoxExample (hDlg, message, wParam, lParam)       *
 *                      *
 *  PURPOSE    : Dialog function for the owner-draw list box example.      *
 *     It sets up the example dialog with the owner-draw list box,*
 *     adds the colors to the list box, and handles setting the   *
 *     selection and focus for the items.                         *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL ListBoxExample (hDlg, message, wParam, lParam)

HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;

{
    LPDRAWITEMSTRUCT  lpdis;
    LPMEASUREITEMSTRUCT lpmis;

    /* Vars for WM_DRAWITEM */
    RECT    rc;
    HBRUSH    hbr;

    switch (message){
  case WM_COMMAND:
      switch (wParam){
    case IDOK:
       EndDialog (hDlg, NULL);
       return (TRUE);
       break;

    /* Clicking any of these buttons adds the corresponding color
     * to the list box. The application-supplied data is the RGB
     * value for the color to be drawn in the listbox.
     */
    case ID_BLACK:
        SendMessage (GetDlgItem (hDlg, ID_LISTBOX),
         LB_ADDSTRING,
         0,
         RGB (0,0,0));
        return(TRUE);
        break;
    case ID_RED:
        SendMessage (GetDlgItem (hDlg, ID_LISTBOX),
         LB_ADDSTRING,
         0,
         RGB (255,0,0));
        return(TRUE);
        break;

    case ID_BLUE:
        SendMessage (GetDlgItem (hDlg, ID_LISTBOX),
         LB_ADDSTRING,
         0,
         RGB (0,0,255));
        return(TRUE);
        break;

    case ID_GREEN:
        SendMessage (GetDlgItem (hDlg, ID_LISTBOX),
         LB_ADDSTRING,
         0,
         RGB (0,255,0));
        return(TRUE);
        break;

    default:
        return(FALSE);
        break;
      }

  case WM_DRAWITEM:
      /* Get pointer to the DRAWITEMSTRUCT */
      lpdis = (LPDRAWITEMSTRUCT)lParam;

      if (lpdis->itemID == -1)
      {
    /* We have a request to draw an item in the list box, yet there
     * are no list box items. This is sent when the user TABS into
     * an empty list box or an empty list box gets the focus. We
     * have to indicate (somehow) that this owner-draw list box has
     * the focus. We do it in response to this message. Note that
     * lpdis->itemData field would be invalid in this instance so
     * we can't allow it to fall into our standard routines.
     */
    HandleFocusState(lpdis, -5);
      }
      else
      {
    switch (lpdis->itemAction)
    {
      case ODA_DRAWENTIRE:
        DrawEntireItem(lpdis, -7);
        break;

      case ODA_SELECT:
        HandleSelectionState(lpdis, -3);
        break;

      case ODA_FOCUS:
        HandleFocusState(lpdis, -5);
        break;
    }
      }

      /* Return TRUE meaning that we processed this message. */
      return(TRUE);
      break;

  case WM_MEASUREITEM:
      lpmis = (LPMEASUREITEMSTRUCT)lParam;

      /* All the items are the same height since the list box style is
       * LBS_OWNERDRAWFIXED
       */
      lpmis->itemHeight = 30;
      break;

  case WM_CLOSE:
      EndDialog(hDlg, NULL);
      return(TRUE);
      break;

  default:
      return(FALSE);
    }

    return(TRUE);
}

/****************************************************************************
 *                      *
 *  FUNCTION   : ComboBoxExample(hWnd, message, wParam, lParam)       *
 *                      *
 *  PURPOSE    : Dialog function for the text combo dialog. The push buttons*
 *     send various messages to the combo box and the edit control*
 *     when selected. They allow the user to vary data sent with  *
 *     each message.                *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL ComboBoxExample(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{

    HWND hWndCombo;         /* Handle to the combo box control */
             /* in the dialog box window        */
    HWND hWndCheckBox;         /* Handle to the Auto Check Box    */
    char strSingleEditLine[255];     /* Single line edit control input  */
    int  wIndex, wCount;

    /* Get handles to the Combo box and the Check box */
    hWndCombo    = GetDlgItem(hDlg, ID_COMBOBOX);
    hWndCheckBox  = GetDlgItem(hDlg, ID_STEPSBOX);

    switch (message){
  case WM_COMMAND:
      switch (wParam){
    case IDOK:
        EndDialog (hDlg,NULL);
        return(TRUE);

    case ID_UNSLBUTTON:
        /* Selecting this button unselects any selection in the
         * combo box.
         */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        wIndex = (WORD) SendMessage( hWndCombo, CB_GETCURSEL, NULL, 0L);
        if (wIndex == CB_ERR)
      MessageBox (hDlg, (LPSTR)"No Selection", NULL, MB_OK);
        else
      SendMessage (hWndCombo, CB_SETCURSEL, -1, 0L);
        SetFocus (GetDlgItem (hDlg, ID_SINGLEEDIT));
        break;

    case ID_NUMSELBUTTON:
        /* An integer value is taken from the edit control and an
         * attempt is made to select a combo box entry having this
         * index.
         */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        wCount = (WORD) SendMessage (hWndCombo, CB_GETCOUNT, 0, 0L);
        wIndex = (int) GetDlgItemInt (hDlg, ID_SINGLEEDIT, NULL, TRUE);
        if (wIndex >= wCount)
      MessageBox (hDlg, (LPSTR)"Bad Selection", NULL, MB_OK);
        else
      SendMessage(hWndCombo, CB_SETCURSEL, wIndex, 0L);
        SetFocus (GetDlgItem (hDlg, ID_SINGLEEDIT));
        break;

    case ID_TXTSELBUTTON:
        /* A text string is taken from the edit control and an
         * attempt is made to select a combo box entry having the
         * string as a prefix.
         */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        GetDlgItemText (hDlg, ID_SINGLEEDIT,
         (LPSTR)strSingleEditLine, 255);
        wIndex = (WORD) SendMessage (hWndCombo,
            CB_SELECTSTRING,
            -1,
            (LONG)(LPSTR)strSingleEditLine);
        if (wIndex == CB_ERR)
          MessageBox (hDlg, (LPSTR)"Bad Selection", NULL, MB_OK);
        SetFocus (GetDlgItem (hDlg, ID_SINGLEEDIT));
        break;

    case ID_FNDSELBUTTON:
        /* Searches for the text specified in the list of combo
         * entries and returns the index (in combo box) of the
         * first match. The index is displayed in the "Text1"
         * field of the dialog.
         */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        GetDlgItemText (hDlg,
            ID_SINGLEEDIT,
            (LPSTR)strSingleEditLine,
            255);
        wIndex = (WORD)SendMessage (hWndCombo,
                 CB_FINDSTRING,-1,
                 (LONG)(LPSTR)strSingleEditLine);
        if (wIndex == CB_ERR)
      MessageBox (hDlg, (LPSTR)"Bad Selection", NULL, MB_OK);
        else
      SetDlgItemInt (hDlg, ID_TEXT1, wIndex, FALSE);
        SetFocus (GetDlgItem (hDlg, ID_SINGLEEDIT));
        break;

    case ID_CLRBUTTON:
        /* Clears the combo box of all it's entries */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        wCount = (WORD) SendMessage (hWndCombo, CB_GETCOUNT, 0, 0L);
        if (!wCount)
      MessageBox (hDlg, (LPSTR)"Already clear", NULL, MB_OK);
        else{
      SetDlgItemInt (hDlg, ID_TEXT1, wCount, TRUE);
      SetDlgItemText (hDlg, ID_TEXT2, "Items cleared");
      SendMessage (hWndCombo,CB_RESETCONTENT, 0, 0L);
        }
        SetFocus (GetDlgItem (hDlg,ID_SINGLEEDIT));
        break;

    case ID_ADDBUTTON:
        /* Takes the string specified in the edit control and
         * adds it to the combo box.
         */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        GetDlgItemText (hDlg, ID_SINGLEEDIT, strSingleEditLine, 255);
        SendMessage (hWndCombo,
         CB_ADDSTRING,
         0,
         (LONG)(LPSTR) strSingleEditLine);
        SetFocus (GetDlgItem (hDlg, ID_SINGLEEDIT));
        break;

    case ID_DELETEBUTTON:
        /* Delete the currently selected item from the combo box. */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        wIndex = (WORD) SendMessage (hWndCombo, CB_GETCURSEL, 0, 0L);
        if (SendMessage (hWndCombo, CB_DELETESTRING, wIndex, 0L) == CB_ERR)
      MessageBox (hDlg, (LPSTR)"No Selection", NULL, MB_OK);
        else{
      SetDlgItemText (hDlg, ID_TEXT1, "deleted index #");
      SetDlgItemInt  (hDlg, ID_TEXT2, wIndex, TRUE);
        }
        SetFocus (GetDlgItem (hDlg, ID_SINGLEEDIT));
        break;

    case ID_CBDIRBUTTON:
        /* Appends a directory listing of the current directory
         * to the combo box entries.
         */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        wIndex = (WORD)SendMessage (hWndCombo,
                 CB_DIR,
                 0x10|0x4000,
                 (LONG)(LPSTR)"*.*");
        SetFocus (GetDlgItem (hDlg, ID_SINGLEEDIT));
        break;


    case ID_CPYBUTTON:
        /* Copies the currently selected item in the combo box to
         * the edit control.
         */
        SetDlgItemText (hDlg, ID_TEXT1, "");
        SetDlgItemText (hDlg, ID_TEXT2, "");
        wIndex = (WORD) SendMessage (hWndCombo, CB_GETCURSEL, 0, 0L);
        if (wIndex == CB_ERR)
      MessageBox(hDlg, (LPSTR)"No Selection", NULL, MB_OK);
        else{
      wCount = SendMessage (hWndCombo, CB_GETLBTEXTLEN, wIndex, 0L);
      SendMessage (hWndCombo,
             CB_GETLBTEXT,
             wIndex,
             (LONG)(LPSTR)strSingleEditLine);
      SetDlgItemText(hDlg, ID_SINGLEEDIT,
               (LPSTR)strSingleEditLine);
      SetDlgItemText(hDlg, ID_TEXT1, "copied index #");
      SetDlgItemInt(hDlg, ID_TEXT2, wIndex, TRUE);
        }
        SetFocus (GetDlgItem (hDlg, ID_SINGLEEDIT));
        break;

    /* When the combo notification box is checked, a message box
     * is flashed showing what notification codes the combo box is
     * returning to the app. in response to the messages sent by
     * the buttons.
     */
    case ID_COMBOBOX:
        if (SendMessage (hWndCheckBox, BM_GETCHECK, 0, 0L)){
      switch (HIWORD(lParam)){
          case (WORD)CBN_ERRSPACE:
            MessageBox (hDlg, (LPSTR)"CB Out of Space",
           "CB MSG", MB_OK);
            break;

          case CBN_SELCHANGE:
            MessageBox (hDlg, (LPSTR)"CB Sel Change",
           "CB MSG", MB_OK);
            break;

          case CBN_DBLCLK:
            MessageBox(hDlg, (LPSTR)"CB Double Click",
           "CB MSG", MB_OK);
            break;

          case CBN_SETFOCUS:
            SetDlgItemText(hDlg, ID_TEXT1, "CB SetFocus");
            break;

          case CBN_KILLFOCUS:
            SetDlgItemText(hDlg, ID_TEXT1, "CB KillFocus");
            break;
      }
        }
        break;

    default:
        return(FALSE);
      }
      break;

  case WM_CLOSE:
      EndDialog(hDlg, NULL);
      return(TRUE);
      break;

  default:
      return(FALSE);
    }
    return(TRUE);
}


/****************************************************************************
 *                      *
 *  FUNCTION   : OwnerComboBoxExample(hWnd, message, wParam, lParam)      *
 *                      *
 *  PURPOSE    : Dialog function for the dropdown list combo box with      *
 *     owner-draw.                *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL OwnerComboBoxExample (hDlg, message, wParam, lParam)

HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    LPDRAWITEMSTRUCT  lpdis;
    LPMEASUREITEMSTRUCT lpmis;

    /* Variables for WM_DRAWITEM */
    RECT    rc;
    HBRUSH    hbr;

    switch (message){
  case WM_COMMAND:
      switch (wParam){
    case IDOK:
       EndDialog (hDlg, NULL);
       return(TRUE);
       break;

    /* Clicking any of these buttons adds the corresponding color
     * to the combo box. The application-supplied data is the RGB
     * value for the color to be drawn in the listbox.
     */
    case ID_BLACK:
       SendMessage (GetDlgItem(hDlg, ID_LISTBOX),
        CB_ADDSTRING,
        0,
        RGB (0,0,0));
       return(TRUE);
       break;

    case ID_RED:
       SendMessage (GetDlgItem (hDlg, ID_LISTBOX),
        CB_ADDSTRING,
        0,
        RGB (255,0,0));
       return(TRUE);
       break;

    case ID_BLUE:
       SendMessage (GetDlgItem(hDlg, ID_LISTBOX),
        CB_ADDSTRING,
        0,
        RGB (0,0,255));
       return(TRUE);
       break;

    case ID_GREEN:
       SendMessage (GetDlgItem(hDlg, ID_LISTBOX),
        CB_ADDSTRING,
        0,
        RGB (0,255,0));
       return(TRUE);
       break;

    default:
       return(TRUE);
       break;
      }

  case WM_DRAWITEM:
      /* Get pointer to the DRAWITEMSTRUCT */
      lpdis = (LPDRAWITEMSTRUCT)lParam;

      if (lpdis->itemID == -1){
    /* We have a request to draw an item in the combo box, yet there
     * are no combo box items. This is sent when the user TABS into
     * an empty combo box or an empty combo box gets the focus. We
     * have to indicate (somehow) that this owner-draw combo box has
     * the focus. We do it in response to this message. Note that
     * lpdis->itemData field would be invalid in this instance so
     * we can't allow it to fall into our standard routines.
     */
    HandleFocusState(lpdis, -2);
      }
      else
      {
    switch (lpdis->itemAction)
    {
      case ODA_DRAWENTIRE:
        DrawEntireItem(lpdis, -4);
        break;

      case ODA_SELECT:
        HandleSelectionState(lpdis, 0);
        break;

      case ODA_FOCUS:
        HandleFocusState(lpdis, -2);
        break;
    }
      }

      /* Return TRUE meaning that we processed this message. */
      return(TRUE);
      break;

  case WM_MEASUREITEM:
      lpmis = (LPMEASUREITEMSTRUCT)lParam;

      /* All the items are the same height since the combo box is
       * CBS_OWNERDRAWFIXED
       */
      if (lpmis->itemID == -1){
    /* If -1 for item, then we are setting the height of the
     * always visible static item part of the dropdown combo box.
     */
    lpmis->itemHeight = 25;
    return(TRUE);
      }
      lpmis->itemHeight = 30;
      break;

  case WM_CLOSE:
      EndDialog(hDlg, NULL);
      return(TRUE);
      break;

  default:
      return(FALSE);
    }
    return(TRUE);
}


PRINT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SHOWDIB\PRINT.C

/****************************************************************************
 *                         *
 *  MODULE  : Print.c                   *
 *                         *
 *  DESCRIPTION : Routines used for printing.               *
 *                         *
 *  FUNCTIONS  : GetPrinterDC()   - Gets default printer from WIN.INI and
 *             creates a DC for it.           *
 *                         *
 *      InitPrinting()   - Initializes print job.           *
 *                         *
 *      TermPrinting()   - Terminates print job.           *
 *                         *
 *      PrintDlgProc()   - Dialog function for the "Cancel Printing" *
 *             dialog.               *
 *                         *
 *      AbortProc()     - Peeks at message queue for messages from  *
 *             the print dialog.             *
 *                         *
 ****************************************************************************

#include <windows.h>
#include <string.h>
#include "showdib.h"

FARPROC  lpfnAbortProc    = NULL;
FARPROC  lpfnPrintDlgProc = NULL;
HWND   hWndParent    = NULL;
HWND   hDlgPrint    = NULL;
BOOL   bError;
BOOL   bUserAbort;


BOOL FAR PASCAL AbortProc (HDC, short);
BOOL FAR PASCAL PrintDlgProc (HWND, unsigned, WORD, DWORD);

#pragma alloc_text(_PRINT, AbortProc, PrintDlgProc)

/****************************************************************************
 *                      *
 *  FUNCTION   : GetPrinterDC()               *
 *                      *
 *  PURPOSE    : Read WIN.INI for default printer and create a DC for it.   *
 *                      *
 *  RETURNS    : A handle to the DC if successful or NULL otherwise.      *
 *                      *
 ****************************************************************************
HDC PASCAL GetPrinterDC()
{
    static char szPrinter [80];
    char    *szDevice, *szDriver, *szOutput;

    GetProfileString ("windows", "device", "", szPrinter, sizeof(szPrinter));

    if ((szDevice = strtok (szPrinter, "," )) &&
  (szDriver = strtok (NULL,      ", ")) &&
  (szOutput = strtok (NULL,      ", ")))

  return CreateDC (szDriver, szDevice, szOutput, NULL) ;

    return NULL;
}
/****************************************************************************
 *                      *
 *  FUNCTION   : InitPrinting(HDC hDC, HWND hWnd, HANDLE hInst, LPSTR msg)  *
 *                      *
 *  PURPOSE    : Makes preliminary driver calls to set up print job.      *
 *                      *
 *  RETURNS    : TRUE  - if successful.             *
 *     FALSE - otherwise.              *
 *                      *
 ****************************************************************************
BOOL PASCAL InitPrinting(HDC hDC, HWND hWnd, HANDLE hInst, LPSTR msg)
{

    bError     = FALSE;     /* no errors yet */
    bUserAbort = FALSE;     /* user hasn't aborted */

    hWndParent = hWnd;      /* save for Enable at Term time */

    lpfnPrintDlgProc = MakeProcInstance (PrintDlgProc, hInst);
    lpfnAbortProc    = MakeProcInstance (AbortProc, hInst);

    hDlgPrint = CreateDialog (hInst, "PRTDLG", hWndParent, lpfnPrintDlgProc);

    if (!hDlgPrint)
  return FALSE;

    SetWindowText (hDlgPrint, msg);
    EnableWindow (hWndParent, FALSE);       /* disable parent */

    if ((Escape (hDC, SETABORTPROC, 0, (LPSTR)lpfnAbortProc, NULL) > 0) &&
  (Escape (hDC, STARTDOC, lstrlen(msg), msg, NULL) > 0))
  bError = FALSE;
    else
  bError = TRUE;

    /* might want to call the abort proc here to allow the user to
     * abort just before printing begins */
    return TRUE;
}
/****************************************************************************
 *                      *
 *  FUNCTION   :  TermPrinting(HDC hDC)             *
 *                      *
 *  PURPOSE    :  Terminates print job.             *
 *                      *
 ****************************************************************************
void PASCAL TermPrinting(HDC hDC)
{
    if (!bError)
  Escape(hDC, ENDDOC, 0, NULL, NULL);

    if (bUserAbort)
  Escape (hDC, ABORTDOC, 0, NULL, NULL) ;
    else {
  EnableWindow(hWndParent, TRUE);
  DestroyWindow(hDlgPrint);
    }

    FreeProcInstance(lpfnAbortProc);
    FreeProcInstance(lpfnPrintDlgProc);
}
/****************************************************************************
 *                      *
 *  FUNCTION   :PrintDlgProc (HWND, unsigned , WORD , DWORD )        *
 *                      *
 *  PURPOSE    :Dialog function for the "Cancel Printing" dialog. It sets   *
 *    the abort flag if the user presses <Cancel>.        *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL PrintDlgProc (HWND hDlg, unsigned iMessage, WORD wParam, DWOR
{
    switch (iMessage) {
    case WM_INITDIALOG:

      EnableMenuItem (GetSystemMenu (hDlg, FALSE), SC_CLOSE, MF_GRAYED);
      break;

    case WM_COMMAND:
      bUserAbort = TRUE;
      EnableWindow (hWndParent, TRUE);
      DestroyWindow (hDlg);
      hDlgPrint = 0;
      break;

    default:
      return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   :AbortProc (HDC hPrnDC, short nCode)          *
 *                      *
 *  PURPOSE    :Checks message queue for messages from the "Cancel Printing"*
 *    dialog. If it sees a message, (this will be from a print    *
 *    cancel command), it terminates.           *
 *                      *
 *  RETURNS    :Inverse of Abort flag              *
 *                      *
 ****************************************************************************
BOOL FAR PASCAL AbortProc (HDC hPrnDC, short nCode)
{
    MSG   msg;

    while (!bUserAbort && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) {
  if (!hDlgPrint || !IsDialogMessage(hDlgPrint, &msg)) {
      TranslateMessage (&msg);
      DispatchMessage (&msg);
  }
    }
    return !bUserAbort;
}


PRNTFILE.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\PRNTFILE\PRNTFILE.C

/****************************************************************************

    PROGRAM: PrntFile.c

    PURPOSE: Loads, saves, and edits text files

    FUNCTIONS:

        WinMain() - calls initialization function, processes message loop
        InitApplication() - initializes window data and registers window
        InitInstance() - saves instance handle and creates main window
        MainWndProc() - processes messages
        About() - processes messages for "About" dialog box
        SaveAsDlg() - save file under different name
        OpenDlg() - let user select a file, and open it.
        UpdateListBox() - Update the list box of OpenDlg
        ChangeDefExt() - Change the default extension
        SeparateFile() - Separate filename and pathname
        AddExt() - Add default extension
        CheckFileName() - Check for wildcards, add extension if needed
        SaveFile() - Save current file
        QuerySaveFile() - Called when some action might lose current contents
        SetNewBuffer() - Set new buffer for edit window

****************************************************************************/

#include "windows.h"
#include "prntfile.h"

HANDLE hInst;

HANDLE hAccTable;                                /* handle to accelerator tab
HWND hEditWnd;                                      /* handle to edit window
HWND hwnd;                                       /* handle to main window */

/* Additional includes needed for the fstat() function */

#include <sys\types.h>
#include <sys\stat.h>

char FileName[128];
char PathName[128];
char OpenName[128];
char DefPath[128];
char DefSpec[13] = "*.*";
char DefExt[] = ".txt";
char str[255];

HANDLE hEditBuffer;                       /* handle to editing buffer      */
HANDLE hOldBuffer;                        /* old buffer handle        */
HANDLE hHourGlass;                        /* handle to hourglass cursor
HANDLE hSaveCursor;                       /* current cursor handle      */
int hFile;                                /* file handle              */
int count;                                /* number of chars read or written
PSTR pBuffer;                             /* address of read/write buffer
OFSTRUCT OfStruct;                        /* information from OpenFile()
struct stat FileStatus;                   /* information from fstat()      */
BOOL bChanges = FALSE;                    /* TRUE if the file is changed
BOOL bSaveEnabled = FALSE;                /* TRUE if text in the edit buffer
PSTR pEditBuffer;                         /* address of the edit buffer
RECT Rect;                                /* dimension of the client window

char Untitled[] =                         /* default window title      */
     "Edit File - (untitled)";

/* Printer variables  */

HDC hPr;                            /* handle for printer device context
int LineSpace;                      /* spacing between lines          */
int LinesPerPage;                   /* lines per page                 */
int CurrentLine;                    /* current line                   */
int LineLength;                     /* line length                    */
DWORD dwLines;                      /* number of lines to print       */
DWORD dwIndex;                      /* index into lines to print      */
char pLine[128];                    /* buffer to store lines before printing
TEXTMETRIC TextMetric;              /* information about character size
BOOL bAbort;                        /* FALSE if user cancels printing      */
HWND hAbortDlgWnd;
FARPROC lpAbortDlg, lpAbortProc;

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

    if (!hPrevInstance)
        if (!InitApplication(hInstance))
            return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {

    /* Only translate message if it is not an accelerator message */

        if (!TranslateAccelerator(hwnd, hAccTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "PrntFileMenu";
    wc.lpszClassName = "PrntFileWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    RECT            Rect;

    hInst = hInstance;

    hAccTable = LoadAccelerators(hInst, "PrntFileAcc");

    hwnd = CreateWindow(
        "PrntFileWClass",
        "PrntFile Sample Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

    GetClientRect(hwnd, (LPRECT) &Rect);

    /* Create a child window */

    hEditWnd = CreateWindow("Edit",
        NULL,
        WS_CHILD | WS_VISIBLE |
        ES_MULTILINE |
        WS_VSCROLL | WS_HSCROLL |
        ES_AUTOHSCROLL | ES_AUTOVSCROLL,
        0,
        0,
        (Rect.right-Rect.left),
        (Rect.bottom-Rect.top),
        hwnd,
        IDC_EDIT,                          /* Child control i.d. */
        hInst,
        NULL);

    if (!hEditWnd) {
        DestroyWindow(hwnd);
        return (NULL);
    }

    /* Get an hourglass cursor to use during file transfers */

    hHourGlass = LoadCursor(NULL, IDC_WAIT);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

        WM_COMMAND    - application menu (About dialog box)
        WM_DESTROY    - destroy window
        WM_SIZE       - window size has changed
        WM_QUERYENDSESSION - willing to end session?
        WM_ENDSESSION - end Windows session
        WM_CLOSE      - close the window
        WM_SIZE       - window resized

    COMMENTS:

        Adds printing capability to the EDITFILE program.  Printing request
        is sent as an IDM_PRINT message.

        Before the printing operation begins, a modeless dialog box is
        created to allow the user to abort the printing operation.  This
        dialog box remains active until the print job is completed, or the
        user cancels the print operation.

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout, lpOpenDlg, lpSaveAsDlg;

    int Success;                            /* return value from SaveAsDlg()
    int IOStatus;          /* result of file i/o      */
    int nPageSize;          /* vert. resolution of printer device */


    switch (message) {
        case WM_COMMAND:
            switch (wParam) {
                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance(About, hInst);
                    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
                    FreeProcInstance(lpProcAbout);
                    break;

                case IDM_NEW:

                    /* If current file has been modified, query user about
                     * saving it.
                     */

                    if (!QuerySaveFile(hWnd))
                        return (NULL);

                    /* bChanges is set to FALSE to indicate there have been
                     * no changes since the last file save.
                     */

                    bChanges = FALSE;
                    FileName[0] = 0;

                    /* Update the edit buffer */

                    SetNewBuffer(hWnd, NULL, Untitled);
                    break;

                case IDM_OPEN:
                    if (!QuerySaveFile(hWnd))
                        return (NULL);

                    lpOpenDlg = MakeProcInstance((FARPROC) OpenDlg, hInst);

                    /* Open the file and get its handle */

                    hFile = DialogBox(hInst, "Open", hWnd, lpOpenDlg);
                    FreeProcInstance(lpOpenDlg);
                    if (!hFile)
                        return (NULL);

                    /* Allocate edit buffer to the size of the file + 1 */

                    hEditBuffer =
                        LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,
          (WORD)FileStatus.st_size+1);

                    if (!hEditBuffer) {
                        MessageBox(hWnd, "Not enough memory.",
                            NULL, MB_OK | MB_ICONHAND);
                        return (NULL);
                    }
                    hSaveCursor = SetCursor(hHourGlass);
                    pEditBuffer = LocalLock(hEditBuffer);

                    IOStatus = read(hFile, pEditBuffer, FileStatus.st_size);
                    close(hFile);

                    /* # bytes read must equal file size */

                    if (IOStatus != (int)FileStatus.st_size) {

                        sprintf(str, "Error reading %s.", FileName);
                        SetCursor(hSaveCursor);      /* Remove the hourglass
                        MessageBox(hWnd, str, NULL, MB_OK | MB_ICONEXCLAMATIO
                    }

                    LocalUnlock(hEditBuffer);

                    /* Set up a new buffer and window title */

                    sprintf(str, "PrntFile - %s", FileName);
                    SetNewBuffer(hWnd, hEditBuffer, str);
                    SetCursor(hSaveCursor);            /* restore the cursor
                    break;

                case IDM_SAVE:

                    /* If there is no filename, use the saveas command to get
                     * one.  Otherwise, save the file using the current
                     * filename.
                     */

                    if (!FileName[0])
                        goto saveas;
                    if (bChanges)
                        SaveFile(hWnd);
                    break;

                case IDM_SAVEAS:
saveas:
                    lpSaveAsDlg = MakeProcInstance(SaveAsDlg, hInst);

                    /* Call the SaveAsDlg() function to get the new filename

                    Success = DialogBox(hInst, "SaveAs", hWnd, lpSaveAsDlg);
                    FreeProcInstance(lpSaveAsDlg);

                    /* If successful, update the window title, save the file

                    if (Success == IDOK) {
                        sprintf(str, "PrntFile - %s", FileName);
                        SetWindowText(hWnd, str);
                        SaveFile(hWnd);
                    }
                    break;                                  /* User canceled
                case IDM_PRINT:
                    hSaveCursor = SetCursor(hHourGlass);
                    hPr = GetPrinterDC();
                    if (!hPr) {
                        sprintf(str, "Cannot print %s", FileName);
                        MessageBox(hWnd, str, NULL, MB_OK | MB_ICONHAND);
                        return (NULL);
                    }

                    lpAbortDlg =  MakeProcInstance(AbortDlg, hInst);
                    lpAbortProc = MakeProcInstance(AbortProc, hInst);

                    /* Define the abort function */

                    Escape(hPr, SETABORTPROC, NULL,
                        (LPSTR) (long) lpAbortProc,
                        (LPSTR) NULL);

                    if (Escape(hPr, STARTDOC, 4, "PrntFile text",
                            (LPSTR) NULL) < 0) {
                        MessageBox(hWnd, "Unable to start print job",
                            NULL, MB_OK | MB_ICONHAND);
                        FreeProcInstance(lpAbortDlg);
                        FreeProcInstance(lpAbortProc);
                        DeleteDC(hPr);
                    }

                    bAbort = FALSE; /* Clears the abort flag  */

                    /* Create the Abort dialog box (modeless) */

                    hAbortDlgWnd = CreateDialog(hInst, "AbortDlg",
                        hWnd, lpAbortDlg);

                    if (!hAbortDlgWnd) {
                        SetCursor(hSaveCursor);      /* Remove the hourglass
                        MessageBox(hWnd, "NULL Abort window handle",
                            NULL, MB_OK | MB_ICONHAND);
                        return (FALSE);
                    }

                    /* Now show Abort dialog */

                    ShowWindow (hAbortDlgWnd, SW_NORMAL);

                    /* Disable the main window to avoid reentrancy problems *

                    EnableWindow(hWnd, FALSE);
                    SetCursor(hSaveCursor);      /* Remove the hourglass */

                    /* Since you may have more than one line, you need to
                     * compute the spacing between lines.  You can do that by
                     * retrieving the height of the characters you are printi
                     * and advancing their height plus the recommended extern
                     * leading height.
                     */

                    GetTextMetrics(hPr, &TextMetric);
                    LineSpace = TextMetric.tmHeight +
                        TextMetric.tmExternalLeading;

                    /* Since you may have more lines than can fit on one
                     * page, you need to compute the number of lines you can
                     * print per page.  You can do that by retrieving the
         * dimensions of the page and dividing the height
                     * by the line spacing.
                     */

        nPageSize = GetDeviceCaps (hPr, VERTRES);
        LinesPerPage = nPageSize / LineSpace - 1;


                    /* You can output only one line at a time, so you need a
                     * count of the number of lines to print.  You can retrie
                     * the count sending the EM_GETLINECOUNT message to the e
                     * control.
                     */

                    dwLines = SendMessage(hEditWnd, EM_GETLINECOUNT, 0, 0L);

                    /* Keep track of the current line on the current page */

                    CurrentLine = 1;

                    /* One way to output one line at a time is to retrieve
                     * one line at a time from the edit control and write it
                     * using the TextOut function.  For each line you need to
                     * advance one line space.  Also, you need to check for t
                     * end of the page and start a new page if necessary.
                     */

                    for (dwIndex = IOStatus = 0; dwIndex < dwLines; dwIndex++
                        pLine[0] = 128;               /* Maximum buffer size
                        pLine[1] = 0;
                        LineLength = SendMessage(hEditWnd, EM_GETLINE,
                            (WORD)dwIndex, (LONG)((LPSTR)pLine));
                        TextOut(hPr, 0, CurrentLine*LineSpace,
                            (LPSTR)pLine, LineLength);
                        if (++CurrentLine > LinesPerPage ) {
                            CurrentLine = 1;
                            IOStatus = Escape(hPr, NEWFRAME, 0, 0L, 0L);
                            if (IOStatus<0 || bAbort)
                                break;
                        }
                    }

                    if (IOStatus >= 0 && !bAbort) {
                        Escape(hPr, NEWFRAME, 0, 0L, 0L);
                        Escape(hPr, ENDDOC, 0, 0L, 0L);
                    }
                    EnableWindow(hWnd, TRUE);

                    /* Destroy the Abort dialog box */

                    DestroyWindow(hAbortDlgWnd);
                    FreeProcInstance(lpAbortDlg);
                    FreeProcInstance(lpAbortProc);
                    DeleteDC(hPr);
                    break;


                /* edit menu commands */

                case IDM_UNDO:
                case IDM_CUT:
                case IDM_COPY:
                case IDM_PASTE:
                case IDM_CLEAR:
                    MessageBox (
                          GetFocus(),
                          "Command not implemented",
                          "PrntFile Sample Application",
                          MB_ICONASTERISK | MB_OK);
                    break;

                case IDM_EXIT:
                    QuerySaveFile(hWnd);
                    DestroyWindow(hWnd);
                    break;

                case IDC_EDIT:
                    if (HIWORD (lParam) == EN_ERRSPACE) {
                        MessageBox (
                              GetFocus ()
                            , "Out of memory."
                            , "PrntFile Sample Application"
                            , MB_ICONHAND | MB_OK
                        );
                    }
                    break;

            }
            break;

        case WM_SETFOCUS:
            SetFocus (hEditWnd);
            break;

        case WM_SIZE:
            MoveWindow(hEditWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;

        case WM_QUERYENDSESSION:             /* message: to end the session?
            return (QuerySaveFile(hWnd));

        case WM_CLOSE:                       /* message: close the window
            if (QuerySaveFile(hWnd))
                DestroyWindow(hWnd);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}

/****************************************************************************

    FUNCTION: SaveAsDlg(HWND, unsigned, WORD, LONG)

    PURPOSE: Allows user to change name to save file to

    COMMENTS:

        This will initialize the window class if it is the first time this
        application is run.  It then creates the window, and processes the
        message loop until a PostQuitMessage is received.  It exits the
        application by returning the value passed by the PostQuitMessage.

****************************************************************************/

int FAR PASCAL SaveAsDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    char TempName[128];

    switch (message) {
        case WM_INITDIALOG:

            /* If no filename is entered, don't allow the user to save to it

            if (!FileName[0])
                bSaveEnabled = FALSE;
            else {
                bSaveEnabled = TRUE;

                /* Process the path to fit within the IDC_PATH field */

                DlgDirList(hDlg, DefPath, NULL, IDC_PATH, 0x4010);

                /* Send the current filename to the edit control */

                SetDlgItemText(hDlg, IDC_EDIT, FileName);

                /* Accept all characters in the edit control */

                SendDlgItemMessage(hDlg, IDC_EDIT, EM_SETSEL, 0,
                    MAKELONG(0, 0x7fff));
            }

            /* Enable or disable the save control depending on whether the
             * filename exists.
             */

            EnableWindow(GetDlgItem(hDlg, IDOK), bSaveEnabled);

            /* Set the focus to the edit control within the dialog box */

            SetFocus(GetDlgItem(hDlg, IDC_EDIT));
            return (FALSE);                 /* FALSE since Focus was changed

        case WM_COMMAND:
            switch (wParam) {
                case IDC_EDIT:

                    /* If there was previously no filename in the edit
                     * control, then the save control must be enabled as soon
                     * a character is entered.
                     */

                    if (HIWORD(lParam) == EN_CHANGE && !bSaveEnabled)
                    EnableWindow(GetDlgItem(hDlg, IDOK), bSaveEnabled = TRUE)
                    return (TRUE);

                case IDOK:

                   /* Get the filename from the edit control */

                    GetDlgItemText(hDlg, IDC_EDIT, TempName, 128);

                    /* If there are no wildcards, then separate the name into
                     * path and name.  If a path was specified, replace the
                     * default path with the new path.
                     */

                    if (CheckFileName(hDlg, FileName, TempName)) {
                        SeparateFile(hDlg, (LPSTR) str, (LPSTR) DefSpec,
                            (LPSTR) FileName);
                        if (str[0])
                            strcpy(DefPath, str);

                        /* Tell the caller a filename was selected */

                        EndDialog(hDlg, IDOK);
                    }
                    return (TRUE);

                case IDCANCEL:

                    /* Tell the caller the user canceled the SaveAs function

                    EndDialog(hDlg, IDCANCEL);
                    return (TRUE);
            }
            break;

    }
    return (FALSE);
}

/****************************************************************************

    FUNCTION: OpenDlg(HWND, unsigned, WORD, LONG)

    PURPOSE: Let user select a file, and open it.

****************************************************************************/

HANDLE FAR PASCAL OpenDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    WORD index;
    PSTR pTptr;
    HANDLE hFile;

    switch (message) {
        case WM_COMMAND:
            switch (wParam) {

                case IDC_LISTBOX:
                    switch (HIWORD(lParam)) {

                        case LBN_SELCHANGE:
                            /* If item is a directory name, append "*.*" */
          if (DlgDirSelect(hDlg, str, IDC_LISTBOX)) {
                                strcat(str, DefSpec);

          }
                            SetDlgItemText(hDlg, IDC_EDIT, str);
                            SendDlgItemMessage(hDlg,
                                IDC_EDIT,
                                EM_SETSEL,
                                NULL,
                                MAKELONG(0, 0x7fff));


                            break;

                        case LBN_DBLCLK:
                            goto openfile;
                    }
                    return (TRUE);

                case IDOK:
openfile:
                    GetDlgItemText(hDlg, IDC_EDIT, OpenName, 128);
                    if (strchr(OpenName, '*') || strchr(OpenName, '?')) {
                        SeparateFile(hDlg, (LPSTR) str, (LPSTR) DefSpec,
                            (LPSTR) OpenName);
                        if (str[0])
                            strcpy(DefPath, str);
                        ChangeDefExt(DefExt, DefSpec);
                        UpdateListBox(hDlg);
                        return (TRUE);
                    }

                    if (!OpenName[0]) {
                        MessageBox(hDlg, "No filename specified.",
                            NULL, MB_OK | MB_ICONHAND);
                        return (TRUE);
                    }

                    AddExt(OpenName, DefExt);

                    /* Open the file */

                    if ((hFile = OpenFile(OpenName, (LPOFSTRUCT) &OfStruct,
                            OF_READ)) == -1) {
                        sprintf(str, "Error %d opening %s.",
                            OfStruct.nErrCode, OpenName);
                        MessageBox(hDlg, str, NULL,
                            MB_OK | MB_ICONHAND);
                    }
                    else {

                        /* Make sure there's enough room for the file */

                        fstat(hFile, &FileStatus);

                        if (FileStatus.st_size > MAXFILESIZE) {
                            sprintf(str,
                    "Not enough memory to load %s.\n%s exceeds %ld bytes.",
                                OpenName, OpenName, MAXFILESIZE);
                            MessageBox(hDlg, str, NULL,
                                MB_OK | MB_ICONHAND);
                            return (TRUE);
                        }

                        /* File is opened and there is enough room so return
                         * the handle to the caller.
                         */

                        strcpy(FileName, OpenName);
                        EndDialog(hDlg, hFile);
                        return (TRUE);
                    }
                    return (TRUE);

                case IDCANCEL:
       /* strcpy(DefPath, str);
        ChangeDefExt(DefExt, DefSpec);*/
                    EndDialog(hDlg, NULL);
                    return (TRUE);
            }
            break;

        case WM_INITDIALOG:                        /* message: initialize
            UpdateListBox(hDlg);
            SetDlgItemText(hDlg, IDC_EDIT, DefSpec);
            SendDlgItemMessage(hDlg,               /* dialog handle      */
                IDC_EDIT,                          /* where to send message
                EM_SETSEL,                         /* select characters
                NULL,                              /* additional information
                MAKELONG(0, 0x7fff));              /* entire contents      */
            SetFocus(GetDlgItem(hDlg, IDC_EDIT));
            return (FALSE); /* Indicates the focus is set to a control */
    }
    return FALSE;
}

/****************************************************************************

    FUNCTION: UpdateListBox(HWND);

    PURPOSE: Update the list box of OpenDlg

****************************************************************************/

void UpdateListBox(hDlg)
HWND hDlg;
{
    strcpy(str, DefPath);
    strcat(str, DefSpec);
    DlgDirList(hDlg, str, IDC_LISTBOX, IDC_PATH, 0x4010);

    /* To ensure that the listing is made for a subdir. of
     * current drive dir...
     */
    if (!strchr (DefPath, ':'))
  DlgDirList(hDlg, DefSpec, IDC_LISTBOX, IDC_PATH, 0x4010);

    /* Remove the '..' character from path if it exists, since this
     * will make DlgDirList move us up an additional level in the tree
     * when UpdateListBox() is called again.
     */
    if (strstr (DefPath, ".."))
  DefPath[0] = '\0';
    SetDlgItemText(hDlg, IDC_EDIT, DefSpec);
}

/****************************************************************************

    FUNCTION: ChangeDefExt(PSTR, PSTR);

    PURPOSE: Change the default extension

****************************************************************************/

void ChangeDefExt(Ext, Name)
PSTR Ext, Name;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr)
        if (!strchr(pTptr, '*') && !strchr(pTptr, '?'))
            strcpy(Ext, pTptr);
}

/****************************************************************************

    FUNCTION: SeparateFile(HWND, LPSTR, LPSTR, LPSTR)

    PURPOSE: Separate filename and pathname

****************************************************************************/

void SeparateFile(hDlg, lpDestPath, lpDestFileName, lpSrcFileName)
HWND hDlg;
LPSTR lpDestPath, lpDestFileName, lpSrcFileName;
{
    LPSTR lpTmp;
    char  cTmp;

    lpTmp = lpSrcFileName + (long) lstrlen(lpSrcFileName);
    while (*lpTmp != ':' && *lpTmp != '\\' && lpTmp > lpSrcFileName)
        lpTmp = AnsiPrev(lpSrcFileName, lpTmp);
    if (*lpTmp != ':' && *lpTmp != '\\') {
        lstrcpy(lpDestFileName, lpSrcFileName);
        lpDestPath[0] = 0;
        return;
    }
    lstrcpy(lpDestFileName, lpTmp + 1);
    cTmp = *(lpTmp + 1);
    lstrcpy(lpDestPath, lpSrcFileName);
     *(lpTmp + 1) = cTmp;
    lpDestPath[(lpTmp - lpSrcFileName) + 1] = 0;
}

/****************************************************************************

    FUNCTION: AddExt(PSTR, PSTR);

    PURPOSE: Add default extension

/***************************************************************************/

void AddExt(Name, Ext)
PSTR Name, Ext;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr != '.')
        strcat(Name, Ext);
}

/****************************************************************************

    FUNCTION: CheckFileName(HWND, PSTR, PSTR)

    PURPOSE: Check for wildcards, add extension if needed

    COMMENTS:

        Make sure you have a filename and that it does not contain any
        wildcards.  If needed, add the default extension.  This function is
        called whenever your application wants to save a file.

****************************************************************************/

BOOL CheckFileName(hWnd, pDest, pSrc)
HWND hWnd;
PSTR pDest, pSrc;
{
    PSTR pTmp;

    if (!pSrc[0])
        return (FALSE);               /* Indicates no filename was specified

    pTmp = pSrc;
    while (*pTmp) {                     /* Searches the string for wildcards
        switch (*pTmp++) {
            case '*':
            case '?':
                MessageBox(hWnd, "Wildcards not allowed.",
                    NULL, MB_OK | MB_ICONEXCLAMATION);
                return (FALSE);
        }
    }

    AddExt(pSrc, DefExt);            /* Adds the default extension if needed

    if (OpenFile(pSrc, (LPOFSTRUCT) &OfStruct, OF_EXIST) >= 0) {
        sprintf(str, "Replace existing %s?", pSrc);
        if (MessageBox(hWnd, str, "PrntFile",
                MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
            return (FALSE);
    }
    strcpy(pDest, pSrc);
    return (TRUE);
}

/****************************************************************************

    FUNCTION: SaveFile(HWND)

    PURPOSE: Save current file

    COMMENTS:

        This saves the current contents of the Edit buffer, and changes
        bChanges to indicate that the buffer has not been changed since the
        last save.

        Before the edit buffer is sent, you must get its handle and lock it
        to get its address.  Once the file is written, you must unlock the
        buffer.  This allows Windows to move the buffer when not in immediate
        use.

****************************************************************************/

BOOL SaveFile(hWnd)
HWND hWnd;
{
    BOOL bSuccess;
    int IOStatus;                                  /* result of a file write

    if ((hFile = OpenFile(FileName, &OfStruct,
        OF_PROMPT | OF_CANCEL | OF_CREATE)) < 0) {

        /* If the file can't be saved */

        sprintf(str, "Cannot write to %s.", FileName);
        MessageBox(hWnd, str, NULL, MB_OK | MB_ICONHAND);
        return (FALSE);
    }


    hEditBuffer = SendMessage(hEditWnd, EM_GETHANDLE, 0, 0L);
    pEditBuffer = LocalLock(hEditBuffer);

    /* Set the cursor to an hourglass during the file transfer */

    hSaveCursor = SetCursor(hHourGlass);
    IOStatus = write(hFile, pEditBuffer, strlen(pEditBuffer));
    close(hFile);
    SetCursor(hSaveCursor);
    if (IOStatus != strlen(pEditBuffer)) {
        sprintf(str, "Error writing to %s.", FileName);
        MessageBox(hWnd, str,
            NULL, MB_OK | MB_ICONHAND);
        bSuccess = FALSE;
    }
    else {
        bSuccess = TRUE;                /* Indicates the file was saved
        bChanges = FALSE;               /* Indicates changes have been saved
    }

    LocalUnlock(hEditBuffer);
    return (bSuccess);
}

/****************************************************************************

    FUNCTION: QuerySaveFile(HWND);

    PURPOSE: Called when some action might lose current contents

    COMMENTS:

        This function is called whenever we are about to take an action that
        would lose the current contents of the edit buffer.

****************************************************************************/

BOOL QuerySaveFile(hWnd)
HWND hWnd;
{
    int Response;
    FARPROC lpSaveAsDlg;

    if (bChanges) {
        sprintf(str, "Save current changes: %s", FileName);
        Response = MessageBox(hWnd, str,
            "PrntFile",  MB_YESNOCANCEL | MB_ICONHAND);
        if (Response == IDYES) {
check_name:

            /* Make sure there is a filename to save to */

            if (!FileName[0]) {
                lpSaveAsDlg = MakeProcInstance(SaveAsDlg, hInst);
                Response = DialogBox(hInst, "SaveAs",
                    hWnd, lpSaveAsDlg);
                FreeProcInstance(lpSaveAsDlg);
                if (Response == IDOK)
                    goto check_name;
                else
                    return (FALSE);
            }
            SaveFile(hWnd);
        }
        else if (Response == IDCANCEL)
            return (FALSE);
    }
    else
        return (TRUE);
}

/****************************************************************************

    FUNCTION: SetNewBuffer(HWND, HANDLE, PSTR)

    PURPOSE: Set new buffer for edit window

    COMMENTS:

        Point the edit window to the new buffer, update the window title, and
        redraw the edit window.  If hNewBuffer is NULL, then create an empty
        1K buffer, and return its handle.

****************************************************************************/

void SetNewBuffer(hWnd, hNewBuffer, Title)
HWND hWnd;
HANDLE hNewBuffer;
PSTR Title;
{
    HANDLE hOldBuffer;

    hOldBuffer = SendMessage(hEditWnd, EM_GETHANDLE, 0, 0L);
    LocalFree(hOldBuffer);
    if (!hNewBuffer)                    /* Allocates a buffer if none exists
        hNewBuffer = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, 1);

    SendMessage(hEditWnd, EM_SETHANDLE, hNewBuffer, 0L); /* Updates the buffe
                  and displays new buffer */
    SetWindowText(hWnd, Title);
    SetFocus(hEditWnd);
    bChanges = FALSE;
}


/****************************************************************************

    FUNCTION: GetPrinterDC()

    PURPOSE:  Get hDc for current device on current output port according to
              info in WIN.INI.

    COMMENTS:

        Searches WIN.INI for information about what printer is connected, and
        if found, creates a DC for the printer.

        returns
            hDC > 0 if success
            hDC = 0 if failure

****************************************************************************/

HANDLE GetPrinterDC()
{
    char pPrintInfo[80];
    LPSTR lpTemp;
    LPSTR lpPrintType;
    LPSTR lpPrintDriver;
    LPSTR lpPrintPort;

    if (!GetProfileString("windows", "Device", (LPSTR)"", pPrintInfo, 80))
        return (NULL);
    lpTemp = lpPrintType = pPrintInfo;
    lpPrintDriver = lpPrintPort = 0;
    while (*lpTemp) {
        if (*lpTemp == ',') {
            *lpTemp++ = 0;
            while (*lpTemp == ' ')
                lpTemp = AnsiNext(lpTemp);
            if (!lpPrintDriver)
                lpPrintDriver = lpTemp;
            else {
                lpPrintPort = lpTemp;
                break;
            }
        }
        else
            lpTemp = AnsiNext(lpTemp);
    }

    return (CreateDC(lpPrintDriver, lpPrintType, lpPrintPort, (LPSTR) NULL));
}

/****************************************************************************

    FUNCTION: AbortProc()

    PURPOSE:  Processes messages for the Abort Dialog box

****************************************************************************/

int FAR PASCAL AbortProc(hPr, Code)
HDC hPr;                            /* for multiple printer display contexts
int Code;                           /* printing status                */
{
    MSG msg;

    /* Process messages intended for the abort dialog box */

    while (!bAbort && PeekMessage(&msg, NULL, NULL, NULL, TRUE))
        if (!IsDialogMessage(hAbortDlgWnd, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

    /* bAbort is TRUE (return is FALSE) if the user has aborted */

    return (!bAbort);
}

/****************************************************************************

    FUNCTION: AbortDlg(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for printer abort dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

    COMMENTS

        This dialog box is created while the program is printing, and allows
        the user to cancel the printing process.

****************************************************************************/

int FAR PASCAL AbortDlg(hDlg, msg, wParam, lParam)
HWND hDlg;
unsigned msg;
WORD wParam;
LONG lParam;
{
    switch(msg) {

        /* Watch for Cancel button, RETURN key, ESCAPE key, or SPACE BAR */

        case WM_COMMAND:
            return (bAbort = TRUE);

        case WM_INITDIALOG:

            /* Set the focus to the Cancel box of the dialog */

            SetFocus(GetDlgItem(hDlg, IDCANCEL));
            SetDlgItemText(hDlg, IDC_FILENAME, FileName);
            return (TRUE);
        }
    return (FALSE);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
        case WM_INITDIALOG:
            return (TRUE);

        case WM_COMMAND:
      if (wParam == IDOK
                || wParam == IDCANCEL) {
                EndDialog(hDlg, TRUE);
                return (TRUE);
            }
            return (TRUE);
    }
    return (FALSE);
}


RAINBOW.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\RAINBOW\RAINBOW.C

/*
 * RAINBOW -- Example Dialog Editor custom control
 *
 */

#include <windows.h>
#include <custcntl.h>
#include "rainbow.h"

/* global static variables */
HANDLE    hLibData;
HANDLE    hLibInstance;
LPFNSTRTOID    lpfnVerId;
LPFNIDTOSTR    lpfnIdStr;

/* string for property lists */
#define  IDFNLO          "lpfnIdFnLo"
#define  IDFNHI          "lpfnIdFnHi"

#define  RAINBOWCLASS      "Rainbow"

/* general rainbow definitions */
#define  ID              GetWindowWord( hWnd, GWW_ID )
#define  PARENT          GetWindowWord( hWnd, GWW_HWNDPARENT )
#define  INSTANCE          GetWindowWord( hWnd, GWW_HINSTANCE )

/* rainbow specific definitions */
#define  RAINBOW_EXTRA      12

#define  RANGE            GetWindowWord( hWnd, 0 )
#define  TABLE            GetWindowWord( hWnd, 2 )
#define  WIDTH            GetWindowWord( hWnd, 4 )
#define  HEIGHT          GetWindowWord( hWnd, 6 )
#define  CHOICE          GetWindowWord( hWnd, 8 )
#define  CAPTURE          GetWindowWord( hWnd, 10 )

#define  SET_RANGE(x)      SetWindowWord( hWnd, 0, x )
#define  SET_TABLE(x)      SetWindowWord( hWnd, 2, x )
#define  SET_WIDTH(x)      SetWindowWord( hWnd, 4, x )
#define  SET_HEIGHT(x)      SetWindowWord( hWnd, 6, x )
#define  SET_CHOICE(x)      SetWindowWord( hWnd, 8, x )
#define  SET_CAPTURE(x)      SetWindowWord( hWnd, 10, x )

/* caret related definitions */
#define  CARET_XPOS        ((CHOICE*WIDTH)+3)
#define  CARET_YPOS        (3)
#define  CARET_WIDTH        (WIDTH-6)
#define  CARET_HEIGHT      (HEIGHT-6)

/* selector related definitions */
#define  SELECTOR_XPOS      ((CHOICE*WIDTH)+1)
#define  SELECTOR_YPOS      (1)
#define  SELECTOR_WIDTH      (WIDTH-2)
#define  SELECTOR_HEIGHT    (HEIGHT-2)

/* undocumented internal function definitions */
int FAR PASCAL    lstrlen( LPSTR );
int FAR PASCAL    lstrcmp( LPSTR, LPSTR );
LPSTR FAR PASCAL  lstrcpy( LPSTR, LPSTR );
LPSTR FAR PASCAL  lstrcat( LPSTR, LPSTR );

/* internal rainbow function prototypes */
BOOL FAR PASCAL   RainbowDlgFn( HWND, WORD, WORD, LONG );
LONG FAR PASCAL   RainbowWndFn( HWND, WORD, WORD, LONG );
void static      DrawSelector( HWND, HDC );

/*♀*/

/*
 * LibMain( hInstance, wDataSegment, wHeapSize, lpszCmdLine ) : WORD
 *
 *    hInstance      library instance handle
 *    wDataSegment   library data segment
 *    wHeapSize      default heap size
 *    lpszCmdLine    command line arguments
 *
 * LibMain is called by LibEntry, which is called by Windows when
 * the DLL is loaded.  The LibEntry routine is provided
 * in the LIBENTRY.OBJ in the SDK Link Libraries disk.  (The source
 * LIBENTRY.ASM is also provided.)
 *
 * LibEntry initializes the DLL's heap, if a HEAPSIZE value is
 * specified in the DLL's DEF file.  Then LibEntry calls
 * LibMain.  The LibMain function below satisfies that call.
 *
 * LibMain performs all the initialization necessary to use the
 * rainbow user control.  Included in this initialization is the
 * registration of the Rainbow window class.
 *
*/

int FAR PASCAL LibMain(
   HANDLE      hInstance,
   WORD        wDataSegment,
   WORD        wHeapSize,
   LPSTR       lpszCmdLine )
{
  HANDLE      hClassStruct;
  LPWNDCLASS    lpClassStruct;

  /* register rainbow window if necessary */
  if ( hLibInstance == NULL ) {

    /* allocate memory for class structure */
    hClassStruct = GlobalAlloc( GHND, (DWORD)sizeof(WNDCLASS) );
    if ( hClassStruct ) {

      /* lock it down */
      lpClassStruct = (LPWNDCLASS)GlobalLock( hClassStruct );
      if ( lpClassStruct ) {

        /* define class attributes */
        lpClassStruct->lpszClassName =   (LPSTR)RAINBOWCLASS;
        lpClassStruct->hCursor =      LoadCursor( NULL, IDC_ARROW );
        lpClassStruct->lpszMenuName =    (LPSTR)NULL;
        lpClassStruct->style =        CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS|CS_GLO
        lpClassStruct->lpfnWndProc =    RainbowWndFn;
        lpClassStruct->hInstance =      hInstance;
        lpClassStruct->hIcon =        NULL;
        lpClassStruct->cbWndExtra =    RAINBOW_EXTRA;
        lpClassStruct->hbrBackground =  (HBRUSH)(COLOR_WINDOW + 1 );

        /* register rainbow window class */
        hLibInstance = ( RegisterClass(lpClassStruct) ) ? hInstance : NULL;

        /* unlock structure */
        GlobalUnlock( hClassStruct );

      }

      /* release class structure */
      GlobalFree( hClassStruct );

    }

  }

  /* return result 1 = success; 0 = fail */
  return( hLibInstance? 1:0 );
}

/*♀*/


/****************************************************************************
    FUNCTION:  WEP(int)

    PURPOSE:  Performs cleanup tasks when the DLL is unloaded.  WEP() is
              called automatically by Windows when the DLL is unloaded
              (no remaining tasks still have the DLL loaded).  It is
              strongly recommended that a DLL have a WEP() function,
              even if it does nothing but return, as in this example.

*****************************************************************************
VOID FAR PASCAL WEP (bSystemExit)
int  bSystemExit;
{
    return;
}


/*
 * RainbowInfo() : HANDLE
 *
 * This function returns a handle to a global block of memory that
 * contains various information about the kinds of controls the library
 * is capable of supporting.  This data block can, for example, be used
 * by the dialog editor when determining the capabilities of a particular
 * control library.
 *
 * Note that this handle becomes the property of the caller once this
 * function returns.  This implies that the caller must call GlobalFree
 * once it is finished with the data.
 *
 */

HANDLE FAR PASCAL RainbowInfo()
{
  HANDLE    hCtlInfo;
  LPCTLINFO  lpCtlInfo;

  /* allocate space for information structure */
  hCtlInfo = GlobalAlloc( GHND, (DWORD)sizeof(CTLINFO) );
  if ( hCtlInfo ) {

    /* attempt to lock it down */
    lpCtlInfo = (LPCTLINFO)GlobalLock( hCtlInfo );
    if ( lpCtlInfo ) {

      /* define the fixed portion of the structure */
      lpCtlInfo->wVersion = 100;
      lpCtlInfo->wCtlTypes = 1;
      lstrcpy( lpCtlInfo->szClass, RAINBOWCLASS );
      lstrcpy( lpCtlInfo->szTitle, "Sample User Control" );

      /* define the variable portion of the structure */
      lpCtlInfo->Type[0].wWidth = 33;
      lpCtlInfo->Type[0].wHeight = 20;
      lpCtlInfo->Type[0].dwStyle = WS_CHILD;
      lstrcpy( lpCtlInfo->Type[0].szDescr, "Rainbow" );

      /* unlock it */
      GlobalUnlock( hCtlInfo );

    } else {
      GlobalFree( hCtlInfo );
      hCtlInfo = NULL;
    }

  }

  /* return result */
  return( hCtlInfo );

}

/*♀*/

/*
 * RainbowStyle( hWnd, hCtlStyle, lpfnVeriyId, lpfnGetIdStr ) : BOOL;
 *
 *    hWnd           handle to parent window
 *    hCtlStyle      handle to control style
 *    lpfnVerifyId   pointer to the VerifyId function from Dialog editor
 *    lpfnGetIdStr   pointer to the GetIdStr functionn from Dialog editor
 *
 * This function enables the user to edit the style of a particular
 * control provided.  The current control style information is passed
 * in using a handle to a control style data structure.
 *
 * This function returns this same handle (referencing updated
 * information) if the dialog box is normally closed.  A value of
 * NULL is returned if the user cancelled the operation.
 *
 */

BOOL FAR PASCAL RainbowStyle(
  HWND        hWnd,
  HANDLE      hCtlStyle,
  LPFNSTRTOID       lpfnVerifyId,
  LPFNIDTOSTR  lpfnGetIdStr )
{
  FARPROC    lpDlgFn;
  HANDLE    hNewCtlStyle;

  /* initialization */
  hLibData = hCtlStyle;
  lpfnVerId = lpfnVerifyId;
  lpfnIdStr = lpfnGetIdStr;

  /* display dialog box */
  lpDlgFn = MakeProcInstance( (FARPROC)RainbowDlgFn, hLibInstance );
  hNewCtlStyle = ( DialogBox(hLibInstance,"RainbowStyle",hWnd,lpDlgFn) ) ? hL
  FreeProcInstance( lpDlgFn );

  /* return updated data block */
  return( hNewCtlStyle );

}

/*♀*/

/*
 * RainbowFlags( wFlags, lpszString, wMaxString ) : WORD;
 *
 *    wFlags         class style flags
 *    lpszString     class style string
 *    wMaxString     maximum size of class style string
 *
 * This function translates the class style flags provided into a
 * corresponding text string for output to an RC file.  The general
 * windows flags (contained in the low byte) are not interpreted,
 * only those in the high byte.
 *
 * The value returned by this function is the library instance
 * handle when sucessful, and NULL otherwise.
 *
 */

WORD FAR PASCAL RainbowFlags(
  WORD        wFlags,
  LPSTR       lpszString,
  WORD        wMaxString )
{
  lpszString[0] = NULL;
  return( 0 );
}

/*♀*/

/*
 * RainbowWndFn( hWnd, wMsg, wParam, lParam ) : LONG
 *
 *    hWnd          handle to rainbow window
 *    wMsg          message number
 *    wParam        single word parameter
 *    lParam        double word parameter
 *
 * This function is responsible for processing all the messages
 * which relate to the rainbow control window.  Note how the
 * code is written to avoid potential problems when re-entrancy
 * ocurrs - this involves the use of extra bytes associated with
 * the window data structure.
 *
 * The LONG value returned by this function is either a value
 * returned by the internal handling of the message or by the
 * default window procedure.
 *
 */

LONG FAR PASCAL RainbowWndFn( hWnd, wMsg, wParam, lParam )
  HWND      hWnd;
  WORD      wMsg;
  WORD      wParam;
  LONG      lParam;
{
  /* local variables */
  LONG      lResult;          /* temporary result variable */

  /* initialization */
  lResult = TRUE;

  /* process message */
  switch( wMsg )
    {
  case WM_GETDLGCODE : /* capture all key strokes */
    lParam = DLGC_WANTARROWS;
    break;
  case WM_CREATE : /* create pallette window */

    {
      /* temporary variables */
      HANDLE    hrgbList;        /* handle to rgb list */
      LONG FAR *  lprgbEntry;        /* pointer to rgb list */

      /* allocate space for rgb color list */
      hrgbList = GlobalAlloc( GMEM_MOVEABLE, sizeof(LONG)*16L );
      if ( hrgbList ) {

         /*
         * Define initial rgb color & value list - note that
         * eight default colors are selected with the values
         * matching each of the colors.
         */

        lprgbEntry = (LONG FAR *)GlobalLock( hrgbList );
        lprgbEntry[0] = RGB( 0x00, 0x00, 0x00 );
        lprgbEntry[1] = RGB( 0x00, 0x00, 0xFF );
        lprgbEntry[2] = RGB( 0x00, 0xFF, 0x00 );
        lprgbEntry[3] = RGB( 0xFF, 0x00, 0x00 );
        lprgbEntry[4] = RGB( 0x00, 0xFF, 0xFF );
        lprgbEntry[5] = RGB( 0xFF, 0xFF, 0x00 );
        lprgbEntry[6] = RGB( 0xFF, 0x00, 0xFF );
        lprgbEntry[7] = RGB( 0xFF, 0xFF, 0xFF );
        lprgbEntry[8] = RGB( 0x00, 0x00, 0x00 );
        lprgbEntry[9] = RGB( 0x00, 0x00, 0xFF );
        lprgbEntry[10] = RGB( 0x00, 0xFF, 0x00 );
        lprgbEntry[11] = RGB( 0xFF, 0x00, 0x00 );
        lprgbEntry[12] = RGB( 0x00, 0xFF, 0xFF );
        lprgbEntry[13] = RGB( 0xFF, 0xFF, 0x00 );
        lprgbEntry[14] = RGB( 0xFF, 0x00, 0xFF );
        lprgbEntry[15] = RGB( 0xFF, 0xFF, 0xFF );
        GlobalUnlock( hrgbList );

        /* define instance variables */
        SET_RANGE( 8 );
        SET_TABLE( hrgbList );
        SET_WIDTH( ((LPCREATESTRUCT)lParam)->cx / 8 );
        SET_HEIGHT( ((LPCREATESTRUCT)lParam)->cy );
        SET_CHOICE( 0 );
        SET_CAPTURE( FALSE );

      } else
        DestroyWindow( hWnd );

    }

    break;
  case WM_SIZE : /* window being resized */

    /* redefine width & height instance variables */
    SET_WIDTH( LOWORD(lParam) / 8 );
    SET_HEIGHT( HIWORD(lParam) );

    break;
  case WM_PAINT : /* paint control window */

    {
      PAINTSTRUCT    Ps;          /* paint structure */
      WORD        wEntry;        /* current color entry */
      HANDLE      hBrush;        /* handle to new brush */
      HANDLE      hOldBrush;      /* handle to old brush */
      LONG FAR *    lprgbEntry;      /* pointer to rgb list */

      /* start paint operation */
      BeginPaint( hWnd, (LPPAINTSTRUCT)&Ps );

      /* iteratively paint each color patch */
      lprgbEntry = (LONG FAR *)GlobalLock( TABLE );
      for ( wEntry=0; wEntry<RANGE; wEntry++ ) {

        /* create solid brush for patch & select */
        hBrush = CreateSolidBrush( lprgbEntry[wEntry] );
        hOldBrush = SelectObject( Ps.hdc, hBrush );

        /* draw rectangle with brush fill */
        Rectangle(
          Ps.hdc,
          wEntry*WIDTH,
          0,
          (wEntry*WIDTH)+WIDTH,
          HEIGHT
        );

        /* unselect brush and delete */
        SelectObject( Ps.hdc, hOldBrush );
        DeleteObject( hBrush );

      }
      GlobalUnlock( TABLE );

      /* End paint operation */
      EndPaint( hWnd, (LPPAINTSTRUCT)&Ps );
    }

    break;
  case WM_KEYDOWN : /* key being pressed */

    {
      /* local variables */
      HDC        hDC;          /* display context handle */
      LONG FAR *    lprgbEntry;      /* pointer to rgb list */

      /* retrieve display context & unmark current selection */
      hDC = GetDC( hWnd );
      DrawSelector( hWnd, hDC );

      /* process virtual key codes */
      switch( wParam )
        {
      case VK_HOME : /* home key */
        SET_CHOICE( 0 );
        break;
      case VK_UP : /* up cursor key */
      case VK_LEFT : /* left cursor key */
        SET_CHOICE( (CHOICE > 0) ? CHOICE-1 : RANGE-1 );
        break;
      case VK_DOWN : /* down cursor key */
      case VK_RIGHT : /* right cursor key */
      case VK_SPACE : /* space bar - move right */
        SET_CHOICE( (CHOICE < RANGE-1) ? CHOICE+1 : 0 );
        break;
      case VK_END : /* end key */
        SET_CHOICE( RANGE-1 );
        break;
      default : /* some other key */
        lResult = FALSE;
        break;
      }

      /* mark new selection & release display context */
      DrawSelector( hWnd, hDC );
      ReleaseDC( hWnd, hDC );

      /* move caret to new position */
      SetCaretPos( CARET_XPOS, CARET_YPOS );

      /* notify parent of new selection */
      lprgbEntry = (LONG FAR *)GlobalLock( TABLE );
      SendMessage(PARENT,WM_COMMAND,ID,lprgbEntry[RANGE+CHOICE]);
      GlobalUnlock( TABLE );

    }

    break;
  case WM_SETFOCUS : /* get focus - display caret */
    /* create caret & display */
    CreateCaret( hWnd, NULL, CARET_WIDTH, CARET_HEIGHT );
    SetCaretPos( CARET_XPOS, CARET_YPOS );
    ShowCaret( hWnd );
    break;

  case WM_LBUTTONDOWN : /* left button depressed - fall through */

    {
      /* local variables */
      HDC        hDC;          /* display context handle */
      LONG FAR *    lprgbEntry;      /* pointer to rgb list */

      /* retrieve display context */
      hDC = GetDC ( hWnd );

      /* unmark old selection & mark new one */
      DrawSelector( hWnd, hDC );
      SET_CHOICE( LOWORD(lParam)/WIDTH );
      DrawSelector( hWnd, hDC );

      /* release display context & move caret */
      ReleaseDC( hWnd, hDC );

      /* capture focus & move caret */
      SetFocus(hWnd);
      SetCaretPos( CARET_XPOS, CARET_YPOS );

      /* notify parent of new selection */
      lprgbEntry = (LONG FAR *)GlobalLock( TABLE );
      SendMessage(PARENT,WM_COMMAND,ID,lprgbEntry[RANGE+CHOICE]);
      GlobalUnlock( TABLE );

      /* activate capture */
      SetCapture( hWnd );
      SET_CAPTURE( TRUE );

    }

    break;
  case WM_MOUSEMOVE : /* mouse being moved */

    /* track mouse only if capture on */
    if ( CAPTURE ) {

      /* local variables */
      HDC        hDC;          /* display context handle */
      WORD        wNewChoice;      /* new mouse selection */
      LONG FAR *    lprgbEntry;      /* pointer to rgb list */

      /* calculate new selection */
      wNewChoice = ( LOWORD(lParam) <= 0 ) ?
        0 :
        ( LOWORD(lParam)/WIDTH >= RANGE ) ?
          RANGE - 1 :
          LOWORD(lParam) / WIDTH;

      /* update display if different */
      if ( wNewChoice != CHOICE ) {

        /* retrieve display context */
        hDC = GetDC ( hWnd );

        /* unmark old selection & mark new one */
        DrawSelector( hWnd, hDC );
        SET_CHOICE( wNewChoice );
        DrawSelector( hWnd, hDC );

        /* release display context & move caret */
        ReleaseDC( hWnd, hDC );
        SetCaretPos( CARET_XPOS, CARET_YPOS );

        /* notify parent of new selection */
        lprgbEntry = (LONG FAR *)GlobalLock( TABLE );
        SendMessage(PARENT,WM_COMMAND,ID,lprgbEntry[RANGE+CHOICE]);
        GlobalUnlock( TABLE );

      }

    }

    break;
  case WM_LBUTTONUP : /* left button released */

    /* release capture if active */
    if ( CAPTURE ) {
      SET_CAPTURE( FALSE );
      ReleaseCapture();
    }

    break;
  case WM_KILLFOCUS : /* kill focus - hide caret */
    DestroyCaret();
    break;
  case RM_SETSEL : /* select a color entry */
    {
      LONG FAR *    lprgbEntry;

      /* update selection & redraw rainbow */
      SET_CHOICE( (wParam >= RANGE) ? 0 : wParam );
      InvalidateRect( hWnd, NULL, TRUE );

      /* notify parent of change in color */
      lprgbEntry = (LONG FAR *)GlobalLock( TABLE );
      SendMessage( PARENT, WM_COMMAND, ID, lprgbEntry[RANGE+CHOICE] );
      GlobalUnlock( TABLE );

      /* return new choice */
      lResult = CHOICE;

    }
    break;
  case RM_GETSEL : /* retrieve selected color */
    {
      LONG FAR *    lprgbEntry;

      /* return selected entry */
      lResult = CHOICE;

      /* define selected color */
      lprgbEntry = (LONG FAR *)GlobalLock( TABLE );
      *(LONG FAR *)lParam = lprgbEntry[RANGE+CHOICE];
      GlobalUnlock( TABLE );

    }
    break;
  case RM_SETCOLORS : /* define rainbow color table */
    {
      WORD      wEntry;
      HANDLE    hrgbList;
      RECT      rectClient;
      LONG FAR *  lprgbEntry;

      /* release previous table from memory */
      GlobalFree( TABLE );

      hrgbList = GlobalAlloc(  GMEM_MOVEABLE,  sizeof(LONG)*wParam*2L);
      if ( hrgbList ) {

        /* define initial rgb colors & values */
        lprgbEntry = (LONG FAR *)GlobalLock( hrgbList );
        for ( wEntry=0; wEntry < wParam; wEntry++ ) {
          lprgbEntry[wEntry] = ((LONG FAR*)lParam)[wEntry];
          lprgbEntry[wParam+wEntry] = ((LONG FAR*)lParam)[wParam+wEntry];
        }
        GlobalUnlock( hrgbList );

        /* retrieve current window dimensions */
        GetClientRect( hWnd, &rectClient );

        /* re-define instance variables */
        SET_RANGE( wParam );
        SET_TABLE( hrgbList );
        SET_WIDTH( (rectClient.right-rectClient.left)/wParam );
        SET_HEIGHT( rectClient.bottom-rectClient.top );
        SET_CHOICE( 0 );

        /* update window & notify parent of new selection */
        InvalidateRect( hWnd, NULL, TRUE );
        SendMessage( PARENT, WM_COMMAND, ID, lprgbEntry[RANGE+CHOICE] );

        /* normal return */
        lResult = wParam;

      } else
        lResult = -1L;

    }
    break;
  case RM_GETCOLORS : /* retrieve rainbow color table */
    {
      WORD      wEntry;
      LONG FAR *  lprgbEntry;

      /* retrieve number of colors */
      lResult = RANGE;

      /* retrieve rgb color list */
      lprgbEntry = (LONG FAR *)GlobalLock( TABLE );
      for ( wEntry=0; (wEntry < RANGE)&&(wEntry < wParam); wEntry++ ) {
        ((LONG FAR *)lParam)[wEntry] = lprgbEntry[wEntry];
        ((LONG FAR *)lParam)[RANGE+wEntry] = lprgbEntry[RANGE+wEntry];
      }
      GlobalUnlock( TABLE );

    }
    break;
  case WM_DESTROY : /* window being destroyed */
    GlobalFree( TABLE );
    break;
  default : /* default window message processing */
    lResult = DefWindowProc( hWnd, wMsg, wParam, lParam );
    break;
  }

  /* return final result */
  return( lResult );

}

/*♀*/

/*
 * RainbowDlgFn( hDlg, wMessage, wParam, lParam ) : BOOL;
 *
 *    hDlg           dialog box handle
 *    wMessage       current window message
 *    wParam         word parameter
 *    lParam         long parameter
 *
 * This function is responsible for processing all the messages that
 * relate to the style dialog box.  This function transfers data
 * between itself and the RainbowStyle using a global data handle.
 *
 * If the user presses the OK button, this data handle is used to pass
 * back a new style data block.  It is left to the calling routine to
 * delete this new block.
 *
 */

BOOL FAR PASCAL RainbowDlgFn(
  HWND        hDlg,
  WORD        wMessage,
  WORD        wParam,
  LONG        lParam )
{
  BOOL      bResult;

  /* initialization */
  bResult = TRUE;

  /* process message */
  switch( wMessage )
    {
  case WM_INITDIALOG :
    {
      HANDLE    hCtlStyle;
      LPCTLSTYLE  lpCtlStyle;
      char      szId[  20 ];  /* temp. string to hold id */

      /* disable Ok button & save dialog data handle */
      hCtlStyle = hLibData;
      EnableWindow( GetDlgItem(hDlg,IDOK), FALSE );

      /* retrieve & display style parameters */
      if ( hCtlStyle ) {

        /* add handle to property list */
        SetProp( hDlg, MAKEINTRESOURCE(1), hCtlStyle );

        /* update dialog box fields */
        lpCtlStyle = (LPCTLSTYLE)GlobalLock( hCtlStyle );
        SetDlgItemText( hDlg, IDTEXT, lpCtlStyle->szTitle );

        /* Kanhom Ng 2/7/89
         * Set the id value string correctly.
         * Save the pointer to the verify id function from dialog editor
         */
        if ( lpfnIdStr )
        {
          (*lpfnIdStr)(lpCtlStyle->wId, (LPSTR)szId, sizeof( szId ) );
          SetDlgItemText( hDlg, IDVALUE, szId );
        }
        else
        {
          EnableWindow( GetDlgItem( hDlg, IDVALUE ), FALSE );
        }
        lstrcpy( lpCtlStyle->szClass, RAINBOWCLASS );
        SetProp( hDlg, IDFNHI, HIWORD( (DWORD)lpfnVerId ) );
        SetProp( hDlg, IDFNLO, LOWORD( (DWORD)lpfnVerId ) );

        GlobalUnlock( hCtlStyle );

      } else
        EndDialog( hDlg, FALSE );

    }
    break;
  case WM_COMMAND :

    /* process sub-message */
    switch( wParam )
      {
    case IDCANCEL : /* cancel dialog box */
      RemoveProp( hDlg, MAKEINTRESOURCE(1) );
      RemoveProp( hDlg, IDFNLO );
      RemoveProp( hDlg, IDFNHI );
      EndDialog( hDlg, FALSE );
      break;
    case IDOK : /* save dialog changes */
      {
        HANDLE    hCtlStyle;
        LPCTLSTYLE  lpCtlStyle;
        LPFNSTRTOID    lpfnId;      /* pointer to the verify id function from
        char      szId[ 20 ];    /* temp. string to hold the id */

        hCtlStyle = GetProp( hDlg, MAKEINTRESOURCE(1) );

        /* update structure contents */
        lpCtlStyle = (LPCTLSTYLE)GlobalLock( hCtlStyle );

        szId[ 0 ] = NULL;
        GetDlgItemText( hDlg, IDVALUE, szId, sizeof(szId) );
        lpfnId = (LPFNSTRTOID)MAKELONG( GetProp( hDlg, IDFNLO ), GetProp( hDl
        if ( lpfnId )
        {
          DWORD    dwResult; /* result ofthe verifyId function */

          dwResult = (*lpfnId)( (LPSTR)szId );
          if ( !(BOOL)dwResult )
          {
            /* Wrong id */
            GlobalUnlock( hCtlStyle );
            break;
          }

          lpCtlStyle->wId = HIWORD( dwResult );
        }

        GetDlgItemText( hDlg, IDTEXT, lpCtlStyle->szTitle, sizeof(lpCtlStyle-
        GlobalUnlock( hCtlStyle );

        RemoveProp( hDlg, MAKEINTRESOURCE(1) );
        RemoveProp( hDlg, IDFNLO );
        RemoveProp( hDlg, IDFNHI );

        /* end dialog box */
        hLibData = hCtlStyle;
        EndDialog( hDlg, TRUE );

      }
      break;
    case IDTEXT : /* control text */
    case IDVALUE : /* control id */

      /* enable or disable Ok button */
      if ( HIWORD(lParam) == EN_CHANGE )
        EnableWindow(
          GetDlgItem(hDlg,IDOK),
          (SendMessage(GetDlgItem(hDlg,IDTEXT),WM_GETTEXTLENGTH,0,0L) &&
           SendMessage(GetDlgItem(hDlg,IDVALUE),WM_GETTEXTLENGTH,0,0L)) ? TRU
        );

      break;
    default : /* something else */
      bResult = FALSE;
      break;
    }

    break;
  default :
    bResult = FALSE;
    break;
  }

  /* return final result */
  return( bResult );

}

/*♀*/

/*
 * DrawSelector( hWnd, hDC ) : void;
 *
 *    hWnd      window handle
 *    hDC      handle to display context
 *
 *  This function is responsible for drawing the selector
 * which surrounds the active color patch.  This drawing
 * process involves the use of the R2_NOT operator to
 * simplify the drawing & re-drawing process.
 *
 */

void static DrawSelector( hWnd, hDC )
  HWND      hWnd;
  HDC      hDC;
{
  /* local variables */
  HANDLE    hOldPen;          /* old pen */
  WORD      wOldROP2;        /* old raster op code */
  WORD      wOldBkMode;        /* old background mode */

  /* setup display context */
  wOldROP2 = SetROP2( hDC, R2_NOT );
  wOldBkMode = SetBkMode( hDC, TRANSPARENT );

  hOldPen = SelectObject( hDC, CreatePen(0,1,RGB(0,0,0)) );

  /* draw selector rectangle */
  Rectangle(
    hDC,
    SELECTOR_XPOS,
    SELECTOR_YPOS,
    SELECTOR_XPOS+SELECTOR_WIDTH,
    SELECTOR_YPOS+SELECTOR_HEIGHT
  );

  DeleteObject( SelectObject(hDC,hOldPen) );

  /* restore display context */
  SetBkMode( hDC, wOldBkMode );
  SetROP2( hDC, wOldROP2 );

}


SELECT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SELECT\SELECT.C

/****************************************************************************

    PROGRAM: Select.c

    PURPOSE: Contains library routines for selecting a region

    FUNCTIONS:

  StartSelection(HWND, POINT, LPRECT, int) - begin selection area
  UpdateSelection(HWND, POINT, LPRECT, int) - update selection area
  EndSelection(POINT, LPRECT) - end selection area
  ClearSelection(HWND, LPRECT, int) - clear selection area

*****************************************************************************

#include "windows.h"
#include "select.h"

/****************************************************************************
   FUNCTION: LibMain(HANDLE, WORD, WORD, LPSTR)

   PURPOSE:  Is called by LibEntry.  LibEntry is called by Windows when
             the DLL is loaded.  The LibEntry routine is provided in
             the LIBENTRY.OBJ in the SDK Link Libraries disk.  (The
             source LIBENTRY.ASM is also provided.)

             LibEntry initializes the DLL's heap, if a HEAPSIZE value is
             specified in the DLL's DEF file.  Then LibEntry calls
             LibMain.  The LibMain function below satisfies that call.

             The LibMain function should perform additional initialization
             tasks required by the DLL.  In this example, no initialization
             tasks are required.  LibMain should return a value of 1 if
             the initialization is successful.

*****************************************************************************
int FAR PASCAL LibMain(hModule, wDataSeg, cbHeapSize, lpszCmdLine)
HANDLE  hModule;
WORD    wDataSeg;
WORD    cbHeapSize;
LPSTR   lpszCmdLine;
{
    return 1;
}


/****************************************************************************
    FUNCTION:  WEP(int)

    PURPOSE:  Performs cleanup tasks when the DLL is unloaded.  WEP() is
              called automatically by Windows when the DLL is unloaded (no
              remaining tasks still have the DLL loaded).  It is strongly
              recommended that a DLL have a WEP() function, even if it does
              nothing but returns success (1), as in this example.

*****************************************************************************
int FAR PASCAL WEP (bSystemExit)
int  bSystemExit;
{
    return(1);
}


/****************************************************************************

    FUNCTION: StartSelection(HWND, POINT, LPRECT, int)

    PURPOSE: Begin selection of region

****************************************************************************/

int FAR PASCAL StartSelection(hWnd, ptCurrent, lpSelectRect, fFlags)
HWND hWnd;
POINT ptCurrent;
LPRECT lpSelectRect;
int fFlags;
{
    if (lpSelectRect->left != lpSelectRect->right ||
      lpSelectRect->top != lpSelectRect->bottom)
  ClearSelection(hWnd, lpSelectRect, fFlags);

    lpSelectRect->right = ptCurrent.x;
    lpSelectRect->bottom = ptCurrent.y;

    /* If you are extending the box, then invert the current rectangle */

    if ((fFlags & SL_SPECIAL) == SL_EXTEND)
  ClearSelection(hWnd, lpSelectRect, fFlags);

    /* Otherwise, set origin to current location */

    else {
  lpSelectRect->left = ptCurrent.x;
  lpSelectRect->top = ptCurrent.y;
    }
    SetCapture(hWnd);
}

/****************************************************************************

    FUNCTION: UpdateSelection(HWND, POINT, LPRECT, int) - update selection ar

    PURPOSE: Update selection

****************************************************************************/

int FAR PASCAL UpdateSelection(hWnd, ptCurrent, lpSelectRect, fFlags)
HWND hWnd;
POINT ptCurrent;
LPRECT lpSelectRect;
int fFlags;
{
    HDC hDC;
    short OldROP;

    hDC = GetDC(hWnd);

    switch (fFlags & SL_TYPE) {

  case SL_BOX:
      OldROP = SetROP2(hDC, R2_NOTXORPEN);
      MoveTo(hDC, lpSelectRect->left, lpSelectRect->top);
      LineTo(hDC, lpSelectRect->right, lpSelectRect->top);
      LineTo(hDC, lpSelectRect->right, lpSelectRect->bottom);
      LineTo(hDC, lpSelectRect->left, lpSelectRect->bottom);
      LineTo(hDC, lpSelectRect->left, lpSelectRect->top);
      LineTo(hDC, ptCurrent.x, lpSelectRect->top);
      LineTo(hDC, ptCurrent.x, ptCurrent.y);
      LineTo(hDC, lpSelectRect->left, ptCurrent.y);
      LineTo(hDC, lpSelectRect->left, lpSelectRect->top);
      SetROP2(hDC, OldROP);
      break;

  case SL_BLOCK:
      PatBlt(hDC,
    lpSelectRect->left,
    lpSelectRect->bottom,
    lpSelectRect->right - lpSelectRect->left,
    ptCurrent.y - lpSelectRect->bottom,
    DSTINVERT);
      PatBlt(hDC,
    lpSelectRect->right,
    lpSelectRect->top,
    ptCurrent.x - lpSelectRect->right,
    ptCurrent.y - lpSelectRect->top,
    DSTINVERT);
      break;
    }
    lpSelectRect->right = ptCurrent.x;
    lpSelectRect->bottom = ptCurrent.y;
    ReleaseDC(hWnd, hDC);
}

/****************************************************************************

    FUNCTION: EndSelection(POINT, LPRECT)

    PURPOSE: End selection of region, release capture of mouse movement

****************************************************************************/

int FAR PASCAL EndSelection(ptCurrent, lpSelectRect)
POINT ptCurrent;
LPRECT lpSelectRect;
{
    lpSelectRect->right = ptCurrent.x;
    lpSelectRect->bottom = ptCurrent.y;
    ReleaseCapture();
}

/****************************************************************************

    FUNCTION: ClearSelection(HWND, LPRECT, int) - clear selection area

    PURPOSE: Clear the current selection

****************************************************************************/

int FAR PASCAL ClearSelection(hWnd, lpSelectRect, fFlags)
HWND hWnd;
LPRECT lpSelectRect;
int fFlags;
{
    HDC hDC;
    short OldROP;

    hDC = GetDC(hWnd);
    switch (fFlags & SL_TYPE) {

  case SL_BOX:
      OldROP = SetROP2(hDC, R2_NOTXORPEN);
      MoveTo(hDC, lpSelectRect->left, lpSelectRect->top);
      LineTo(hDC, lpSelectRect->right, lpSelectRect->top);
      LineTo(hDC, lpSelectRect->right, lpSelectRect->bottom);
      LineTo(hDC, lpSelectRect->left, lpSelectRect->bottom);
      LineTo(hDC, lpSelectRect->left, lpSelectRect->top);
      SetROP2(hDC, OldROP);
      break;

  case SL_BLOCK:
      PatBlt(hDC,
    lpSelectRect->left,
    lpSelectRect->top,
    lpSelectRect->right - lpSelectRect->left,
    lpSelectRect->bottom - lpSelectRect->top,
    DSTINVERT);
      break;
    }
    ReleaseDC(hWnd, hDC);
}


SERVDATA.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\DDE\SERVDATA.C

/****************************************************************************

    MODULE: SERVDATA.C

    PURPOSE: Maintains conversation information.


****************************************************************************/

#include "windows.h"

#include "dde.h"
#include "server.h"
#include <string.h>

typedef struct CONV
{
    HWND hwndServerDDE;
    HWND hwndClientDDE;
    BOOL bInClientRequestedTerminate;
};

typedef struct ADVISE
{
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
    int         nItem;
    ATOM        atomItem;
    BOOL        bAckRequest;
    BOOL        bDeferUpdate;
    BOOL        bAwaitingAck;
    HANDLE      hData;
};

struct CONV Conv[CONV_MAX_COUNT];
static int nConvCount = 0;
static struct ADVISE Advise[ADVISE_MAX_COUNT];
static int nAdviseCount = 0;

struct ADVISE * NEAR FindAdvise(HWND, int);
struct CONV *  NEAR FindConv(HWND);


/****************************************************************************

    FUNCTION: AddAdvise

    PURPOSE:  Register a new hot or warm link to a specified client, for
        a specified server application item (1, 2, or 3).

****************************************************************************/
BOOL AddAdvise(hwndServerDDE, hDDEAdviseOptions, atomItem, nItem)
    HWND   hwndServerDDE;
    HANDLE hDDEAdviseOptions;
    ATOM   atomItem;
    int    nItem;
{
    struct ADVISE     * pAdvise;
    DDEADVISE FAR     * lpDDEAdviseOptions;
    int     nAdviseIndex;

    if (nAdviseCount >= ADVISE_MAX_COUNT)
    {
  MessageBox(hwndMain,
            "Maximum advisories exceeded",
            "Server",
            MB_ICONEXCLAMATION | MB_OK);
        return (FALSE);
    }
    if ((lpDDEAdviseOptions
          = (DDEADVISE FAR *)GlobalLock(hDDEAdviseOptions)) == NULL)
        return (FALSE);

    for (pAdvise = Advise, nAdviseIndex = 0;
   nAdviseIndex < nAdviseCount;
   pAdvise++, nAdviseIndex++)
    {
  if (pAdvise->hwndServerDDE == hwndServerDDE
      && pAdvise->nItem == nItem)
  {
      MessageBox(hwndMain,
    "Advisory (paste link) already established",
    "Server",
    MB_ICONEXCLAMATION | MB_OK);
      GlobalUnlock(hDDEAdviseOptions);
      return (FALSE);
  }
    }

    pAdvise = Advise + nAdviseCount++;

    pAdvise->hwndServerDDE = hwndServerDDE;
    pAdvise->hwndClientDDE = GetHwndClientDDE(hwndServerDDE);
    pAdvise->nItem = nItem;
    pAdvise->atomItem = atomItem;
    pAdvise->bAckRequest = lpDDEAdviseOptions->fAckReq;
    pAdvise->bDeferUpdate = lpDDEAdviseOptions->fDeferUpd;
    pAdvise->bAwaitingAck = FALSE;

    GlobalUnlock(hDDEAdviseOptions);
    return (TRUE);
}



/****************************************************************************

    FUNCTION: AddConv

    PURPOSE:  Register a new conversation with a client window

****************************************************************************/
BOOL AddConv(hwndServerDDE, hwndClientDDE)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
{
    struct CONV * pConv;

    if (nConvCount >= CONV_MAX_COUNT)
    {
        return (FALSE);
    }

    if (FindConv(hwndServerDDE) != NULL)
    {
  return (FALSE); /* conversation already added */
    }

    pConv = Conv + nConvCount++;
    pConv->hwndServerDDE = hwndServerDDE;
    pConv->hwndClientDDE = hwndClientDDE;
    pConv->bInClientRequestedTerminate = FALSE;
    return (TRUE);
}



/***********************************************************************

    FUNCTION:  AtLeastOneConvActive

    PURPOSE:   Used during termination of application, to
         determine whether any conversations are still active
         while the conversations are being terminated.

***********************************************************************/
BOOL AtLeastOneConvActive()
{
    return (nConvCount? TRUE: FALSE);
}



/****************************************************************************

    FUNCTION: CheckOutSentData

    PURPOSE:  Set Awaiting Ack state to true for specified item.

****************************************************************************/
void CheckOutSentData(hwndServerDDE, nItem, atomItem, hData)
    HWND    hwndServerDDE;
    int     nItem;
    ATOM    atomItem;
    HANDLE  hData;
{
    struct ADVISE * pAdvise;

    if (!(pAdvise = FindAdvise(hwndServerDDE, nItem)))
        return;
    pAdvise->bAwaitingAck = TRUE;
    pAdvise->atomItem = atomItem;
    pAdvise->hData = hData;
    return;
}



/****************************************************************************

    FUNCTION: DoEditCopy

    PURPOSE:  Copy selected data to clipboard in two formats:
        CF_TEXT and (registered) "Link" format.

****************************************************************************/
void DoEditCopy(nItem)
    int   nItem;
{

    /* Format string and set clipboard data here */
    HANDLE  hTextData;
    HANDLE  hConvData;
    char  szItemValue[ITEM_VALUE_MAX_SIZE+1];
    LPSTR  lpTextData, lpConvData;

    if (!GetDlgItemText(hwndMain, nItem, szItemValue, ITEM_VALUE_MAX_SIZE))
  strcpy(szItemValue, " ");

    /* Copy item value in CF_TEXT format to global memory object */

    if (!(hTextData
    = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD)lstrlen(szItemValue)+1))
  return;
    if (!(lpTextData = GlobalLock(hTextData)))
    {
  GlobalFree(hTextData);
  return;
    }
    lstrcpy(lpTextData, szItemValue);
    GlobalUnlock(hTextData);

    /* Copy item value in "Link" format to global memory object */

    if (!(hConvData
    = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
      (DWORD)APP_MAX_SIZE+TOPIC_MAX_SIZE+ITEM_NAME_MAX_SIZE+4)))
       return;
    if (!(lpConvData = GlobalLock(hConvData)))
    {
       GlobalFree(hConvData);
       return;
    }

    /* Compose paste link string, each sub-string is null terminated */
    /* Format is <app> <null> <topic> <null> <item> <null> <null>    */
    lstrcpy(lpConvData, "Server");
    lpConvData = lpConvData + lstrlen(lpConvData) + 1;  /* '+1' adds null */
    lstrcpy(lpConvData, szDocName);   /* topic */
    lpConvData += (lstrlen(lpConvData) + 1);
    lstrcpy(lpConvData, "item#");
    lpConvData[4] = '1' + nItem - 1;
    *(lpConvData + lstrlen(lpConvData) + 1) = 0;  /* final null */

    GlobalUnlock(hConvData);

    if (OpenClipboard(hwndMain))
    {
       EmptyClipboard();

       SetClipboardData(CF_TEXT, hTextData);
       SetClipboardData(cfLink, hConvData);

       CloseClipboard();
    }
    else
    {
       GlobalFree(hTextData);
       GlobalFree(hConvData);
    }

    return;
}



/****************************************************************************

    FUNCTION: FindAdvise

    PURPOSE:  Find advisory data for a specific conversation item.

****************************************************************************/
struct ADVISE * NEAR FindAdvise(hwndServerDDE, nItem)
    HWND hwndServerDDE;
    int  nItem;
{
    struct ADVISE * pAdvise;
    int             nAdviseIndex;

    for (nAdviseIndex = 0, pAdvise = Advise;
         nAdviseIndex < nAdviseCount;
         nAdviseIndex++, pAdvise++)
    {
  if (pAdvise->hwndServerDDE == hwndServerDDE
            && pAdvise->nItem == nItem)
        {
            return (pAdvise);
        }
    }
    return (NULL);
}





/****************************************************************************

    FUNCTION: FindConv

    PURPOSE:  Find the conversation for a specified server DDE window.

****************************************************************************/
struct CONV * NEAR FindConv(hwndServerDDE)
    HWND  hwndServerDDE;
{
    struct CONV * pConv;
    int     nConvIndex;

    for (nConvIndex = 0, pConv = Conv;
   nConvIndex < nConvCount;
   nConvIndex++, pConv++)
    {
  if (pConv->hwndServerDDE == hwndServerDDE)
      return (pConv);
    }
    return (NULL);
}



/****************************************************************************

    FUNCTION: GetAdviseData

    PURPOSE:  Get advisory data for a specified conversation item.

****************************************************************************/
BOOL GetAdviseData(hwndServerDDE, nItem, szItemName, szItemValue,
                   pbDeferUpdate, pbAckRequest)
    HWND   hwndServerDDE;
    int    nItem;
    char * szItemName;
    char * szItemValue;
    BOOL * pbDeferUpdate;
    BOOL * pbAckRequest;
{
    struct ADVISE * pAdvise;

    if (!(pAdvise = FindAdvise(hwndServerDDE, nItem)))
        return (FALSE);
    strcpy(szItemName, "Item#");
    szItemName[4] = pAdvise->nItem==1? '1': pAdvise->nItem==2? '2': '3';
    *pbDeferUpdate = pAdvise->bDeferUpdate;
    *pbAckRequest = pAdvise->bAckRequest;
    if (!GetDlgItemText(hwndMain, nItem, szItemValue, ITEM_VALUE_MAX_SIZE))
        strcpy(szItemValue," ");

    return (TRUE);
}



/****************************************************************************

    FUNCTION: GetHwndClientDDE

    PURPOSE:  Get the hwnd of the client in conversation with a specified
        server DDE window.

****************************************************************************/
HWND GetHwndClientDDE(hwndServerDDE)
    HWND  hwndServerDDE;
{
    struct CONV * pConv;

    if (!(pConv = FindConv(hwndServerDDE)))
  return (NULL);
    return (pConv->hwndClientDDE);
}



/****************************************************************************

    FUNCTION: GetNextAdvise

    PURPOSE:  Find a client that needs to be notified that a value of
        a specified item (a hot or warm link item) has changed.
        Since a hot or warm link to the item may have been established
        for multiple conversations (multiple clients), this function
        is set up to step through the list of such conversations.
        Start the list by passing a NULL hwndClientDDE.  To get the
        next conversation in the list, pass the previous client returned
        by this function.

****************************************************************************/
HWND GetNextAdvise(hwndServerDDE, nItem)
    HWND hwndServerDDE;
    int  nItem;
{
    struct ADVISE * pAdvise;
    int             nAdviseIndex;

    if (hwndServerDDE)
    {
        for (nAdviseIndex = 0, pAdvise = Advise;
             nAdviseIndex < nAdviseCount;
             nAdviseIndex++, pAdvise++)
        {
      if (pAdvise->hwndServerDDE == hwndServerDDE
                && pAdvise->nItem == nItem)
            {
                pAdvise++;
                break;
            }
        }
        if (nAdviseIndex >= nAdviseCount)
            return (NULL);
    }
    else
    {
        pAdvise = Advise;
        nAdviseIndex = 0;
    }
    for ( ; nAdviseIndex < nAdviseCount; nAdviseIndex++, pAdvise++)
    {
        if (pAdvise->nItem == nItem)
        {
      return (pAdvise->hwndServerDDE);
        }
    }
    return (NULL);
}



/****************************************************************************

    FUNCTION: GetNextConv

    PURPOSE:  Get next client in list of conversations.  To get the
        first hwndServerDDE in the conversation list, pass in a NULL
        value for hwndServerDDE.

****************************************************************************/
HWND GetNextConv(hwndServerDDE)
    HWND hwndServerDDE;
{
    struct CONV * pConv;
    int     nConvIndex;

    if (hwndServerDDE)
    {
  for (nConvIndex = 0, pConv = Conv;
       nConvIndex < nConvCount;
       nConvIndex++, pConv++)
        {
      if (pConv->hwndServerDDE == hwndServerDDE)
            {
    if (++nConvIndex < nConvCount)
        return (++pConv)->hwndServerDDE;
                else
                    return (NULL);
            }
        }
        return (NULL);
    }
    if (nConvCount > 0)
  return (Conv[0].hwndServerDDE);
    else
        return (NULL);
}



/****************************************************************************

    FUNCTION: GlobalFreeSentData

    PURPOSE:  A global memory object (hData) was sent in a WM_DDE_DATA
        message, but the client never used it.  So, the server is
        responsible for freeing the object.

****************************************************************************/
void GlobalFreeSentData(hwndServerDDE, nItem)
    HWND  hwndServerDDE;
    int   nItem;
{
    struct ADVISE * pAdvise;

    if (!(pAdvise = FindAdvise(hwndServerDDE, nItem)))
        return;
    pAdvise->bAwaitingAck = FALSE;
    GlobalDeleteAtom(pAdvise->atomItem);
    GlobalFree(pAdvise->hData);
    return;
}



/****************************************************************************

    FUNCTION: IsConvInTerminateState

    PURPOSE:  Terminate whether conversation with specified client is
        in process of being terminated.

****************************************************************************/
BOOL IsConvInTerminateState(hwndServerDDE)
    HWND  hwndServerDDE;
{
    struct CONV * pConv;

    if (pConv = FindConv(hwndServerDDE))
  return (pConv->bInClientRequestedTerminate);
    else
        return (FALSE);
}


/****************************************************************************

    FUNCTION: RemoveAdvise

    PURPOSE:  Cancel a hot or warm link for a specified conversation item.
        If a 0 value is specified for nItem, then all hot/warm links
              for the specified client are removed.

****************************************************************************/
BOOL RemoveAdvise(hwndServerDDE, nItem)
    HWND  hwndServerDDE;
    int   nItem;
{
    struct ADVISE * pAdvise;
    int             nAdviseIndex;
    int             nRemoveCount;

    nRemoveCount = 0;
    for (nAdviseIndex = 0, pAdvise = Advise;
         nAdviseIndex < nAdviseCount;
         nAdviseIndex++, pAdvise++)
    {
        if (nRemoveCount)
        {
            *(pAdvise-nRemoveCount) = *pAdvise;
        }
  if (pAdvise->hwndServerDDE == hwndServerDDE
            && (!nItem || pAdvise->nItem == nItem))
        {
            nRemoveCount++;
        }

    }
    if (nRemoveCount)
    {
        nAdviseCount -= nRemoveCount;
        return (TRUE);
    }
    else
    {
        return (FALSE);
    }
}



/****************************************************************************

    FUNCTION: RemoveConv

    PURPOSE:  Remove conversation from conversation list, and remove
        all hot/warm links associated with that conversation
        from the advisory list.

****************************************************************************/
void RemoveConv(hwndServerDDE)
    HWND hwndServerDDE;
{
    struct CONV   * pConv;
    int       nConvIndex;
    struct ADVISE * pAdvise;
    struct ADVISE * pAdviseShift;
    int             nAdviseIndex;
    int             nAdviseDecrement;

    for (nConvIndex = 0, pConv = Conv;
   nConvIndex < nConvCount;
   nConvIndex++, pConv++)
    {
  if (pConv->hwndServerDDE == hwndServerDDE)
            break;
    }
    nConvCount--;
    while (nConvIndex < nConvCount)
    {
  *pConv = *(pConv+1);
  nConvIndex++;
  pConv++;
    }

    /* Remove each hot/warm link */

    pAdviseShift = Advise;
    nAdviseDecrement = 0;
    for (nAdviseIndex = 0, pAdvise = Advise;
         nAdviseIndex < nAdviseCount;
         nAdviseIndex++, pAdvise++)
    {
  if (pAdvise->hwndServerDDE == hwndServerDDE)
        {
            nAdviseDecrement++;
            if (pAdvise->bAwaitingAck)
            { /* Destroy objects perhaps not destroyed by client */
    GlobalDeleteAtom(pAdvise->atomItem);
                GlobalFree(pAdvise->hData);
            }
        }
        else
        {
            *(pAdviseShift++) = *pAdvise;
        }
    }
    nAdviseCount -= nAdviseDecrement;
    return;
}



/****************************************************************************

    FUNCTION: SetConvInTerminateState

    PURPOSE:  Set conversations's terminate state to TRUE.

****************************************************************************/
void SetConvInTerminateState(hwndServerDDE)
    HWND  hwndServerDDE;
{
    struct CONV * pConv;

    if (pConv = FindConv(hwndServerDDE))
  pConv->bInClientRequestedTerminate = TRUE;
    return;
}


SERVDDE.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\DDE\SERVDDE.C

/****************************************************************************

    MODULE: SERVDDE.C

    PURPOSE: Processes incoming and outgoing DDE messages


****************************************************************************/

#include "windows.h"

#include "dde.h"
#include "server.h"
#include <string.h>

#define DEFAULT_ACK_TIME_OUT_MILLISEC 10000
static int nAckTimeOut;

static BOOL   bTerminating = FALSE;

int  NEAR GetDocIndexGivenName(char*);
int  NEAR GetItemNumber(char*);




/****************************************************************************

    FUNCTION: DDEWndProc

    PURPOSE:  Handles all DDE messages received by the server application.

****************************************************************************/
long FAR PASCAL DDEWndProc(hwnd, message, wParam, lParam)
    HWND      hwnd;
    unsigned  message;
    WORD      wParam;
    LONG      lParam;
{
    switch (message)
    {

        case WM_DDE_ACK:
            ServerAcknowledge(hwnd, (HWND)wParam, lParam);
      return (0L);

  case WM_TIMER:   /* time out on waiting for ACK in response */
       /* to WM_DDE_DATA sent by this server      */

      ServerAcknowledge(hwnd, (HWND)wParam, 0L); /* simulates NACK */
      return (0L);

        case WM_DDE_ADVISE:
            ServerAdvise(hwnd, (HWND)wParam, lParam);
      return (0L);

        case WM_DDE_POKE:
            ServerPoke(hwnd, (HWND)wParam, lParam);
      return (0L);

        case WM_DDE_TERMINATE:
            ServerTerminate(hwnd, (HWND)wParam);
      return (0L);

        case WM_DDE_UNADVISE:
            ServerUnadvise(hwnd, (HWND)wParam, lParam);
      return (0L);

        case WM_DDE_REQUEST:
            ServerRequest(hwnd, (HWND)wParam, lParam);
      return (0L);

  case WM_DDE_EXECUTE:
      ServerExecute(hwnd, (HWND)wParam, (HANDLE)HIWORD(lParam));
      return (0L);

  default:
        return (DefWindowProc(hwnd, message, wParam, lParam));
    }
}



/****************************************************************************

    FUNCTION: GetItemNumber

    PURPOSE:  Get server control i.d. (1, 2, or 3) given item name.

****************************************************************************/
int NEAR GetItemNumber(szItem)
    char * szItem;
{
    int nItem;

    if (!strcmpi(szItem, "ITEM1"))
        nItem = 1;
    else if (!strcmpi(szItem, "ITEM2"))
        nItem = 2;
    else if (!strcmpi(szItem, "ITEM3"))
        nItem = 3;
    else
        nItem = 0;
    return (nItem);
}



/****************************************************************************

    FUNCTION: InitAckTimeOut

    PURPOSE:  Get DDE timeout value from win.ini.  Value is in milliseconds.

****************************************************************************/
void InitAckTimeOut(void)
{

   /* Finds value in win.ini section corresponding to application name */

   nAckTimeOut = GetPrivateProfileInt("Server",
             "DdeTimeOut",
             DEFAULT_ACK_TIME_OUT_MILLISEC,
                               "server.ini");
   return;
}



/****************************************************************************

    FUNCTION: SendData

    PURPOSE:  Send data to client.

****************************************************************************/
void SendData(hwndServerDDE, hwndClientDDE, szItemName, szItemValue,
                   bDeferUpdate, bAckRequest)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
    char * szItemName;
    char * szItemValue;
    BOOL   bDeferUpdate;
    BOOL   bAckRequest;
{
    ATOM          atomItem;
    HANDLE        hData;
    DDEDATA FAR * lpData;
    int           nItem;

    if (bDeferUpdate)
    {
        atomItem = GlobalAddAtom((LPSTR)szItemName);
  /* notify client with null data since advise was set up for */
  /* deferred update              */
  if (!PostMessage(hwndClientDDE,
                WM_DDE_DATA,
    hwndServerDDE,
    MAKELONG(0, atomItem)))
        {
            GlobalDeleteAtom(atomItem);
        }
        return;
    }

    /* Allocate size of DDE data header, plus the data:  a string,  */
    /* <CR> <LR> <NULL>.  The byte for the string null terminator */
    /* is counted by DDEDATA.Value[1].           */

    if (!(hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
            (LONG)sizeof(DDEDATA) +lstrlen(szItemValue) + 2)))
        return;
    if (!(lpData = (DDEDATA FAR*)GlobalLock(hData)))
    {
  GlobalFree(hData);
        return;
    }

    lpData->fAckReq = bAckRequest;
    lpData->cfFormat = CF_TEXT;
    lstrcpy((LPSTR)lpData->Value, (LPSTR)szItemValue);
    /* each line of CF_TEXT data is terminated by CR/LF */
    lstrcat((LPSTR)lpData->Value, (LPSTR)"\r\n");
    GlobalUnlock(hData);
    atomItem = GlobalAddAtom((LPSTR)szItemName);
    if (!PostMessage(hwndClientDDE,
            WM_DDE_DATA,
      hwndServerDDE,
            MAKELONG(hData, atomItem)))
    {
        GlobalFree(hData);
        GlobalDeleteAtom(atomItem);
  return;
    }
    if (bAckRequest)
    {
  SetTimer(hwndServerDDE, hwndClientDDE, nAckTimeOut, NULL);
  nItem = GetItemNumber(szItemName);
  /* hData is to be deleted if not read by client for some reason */
  CheckOutSentData(hwndServerDDE, nItem, atomItem, hData);
    }
    return;
}



/****************************************************************************

    FUNCTION: SendTerminate

    PURPOSE:  Post terminate message and indicate that conversation is
        in process ot being terminated.

****************************************************************************/
void SendTerminate(hwndServerDDE, hwndClientDDE)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
{
    SetConvInTerminateState(hwndServerDDE);
    PostMessage(hwndClientDDE, WM_DDE_TERMINATE, hwndServerDDE, 0L);
    return;
}



/****************************************************************************

    FUNCTION: ServerAcknowledge

    PURPOSE:  Called when server application receives ACK or NACK, or
        when server receives time out waiting for response to
        WM_DDE_DATA.

****************************************************************************/
void ServerAcknowledge(hwndServerDDE, hwndClientDDE, lParam)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
    LONG  lParam;
{
    char szItemName[ITEM_NAME_MAX_SIZE+1];
    int  nItem;

    KillTimer(hwndServerDDE, hwndClientDDE);

    if (!(LOWORD(lParam) & 0x8000))
    {
        GlobalGetAtomName(HIWORD(lParam), szItemName, ITEM_NAME_MAX_SIZE);
        nItem = GetItemNumber(szItemName);
  GlobalFreeSentData(hwndServerDDE, nItem);
  MessageBox(hwndMain,
            "DDE send data failed",
            "Server",
            MB_ICONEXCLAMATION | MB_OK);
    }
    if (HIWORD(lParam))    /* 0 if time-out, so don't try to delete */
  GlobalDeleteAtom(HIWORD(lParam));
    return;
}



/****************************************************************************

    FUNCTION: ServerAdvise

    PURPOSE:  Called when server application receives WM_DDE_ADVISE message.

****************************************************************************/
void ServerAdvise(hwndServerDDE, hwndClientDDE, lParam)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
    LONG  lParam;
{
    HANDLE          hDDEAdviseOptions;
    DDEADVISE FAR * lpDDEAdviseOptions;
    ATOM            atomItem;
    char            szItem[ITEM_NAME_MAX_SIZE+1];
    int             nItem;

    hDDEAdviseOptions = LOWORD(lParam);
    atomItem = HIWORD(lParam);

    GlobalGetAtomName(atomItem, szItem, ITEM_NAME_MAX_SIZE);

    if (!(nItem = GetItemNumber(szItem))
  || !AddAdvise(hwndServerDDE, hDDEAdviseOptions, atomItem, nItem))
    {
  PostMessage(hwndClientDDE,
            WM_DDE_ACK,
      hwndServerDDE,
            MAKELONG(0, atomItem)); /* negative acknowledgement */
        return;
    }
    PostMessage(hwndClientDDE,
        WM_DDE_ACK,
  hwndServerDDE,
        MAKELONG(0x8000, atomItem)); /* positive acknowledgement */
    return;
}



/****************************************************************************

    FUNCTION: ServerExecute

    PURPOSE:  Called when server application receives WM_DDE_EXECUTE message.

****************************************************************************/
void ServerExecute(hwndServerDDE, hwndClientDDE, hCommand)
    HWND    hwndServerDDE;
    HWND    hwndClientDDE;
    HANDLE  hCommand;
{
    LPSTR   lpstrCommand;
    char    szExecuteString[EXECUTE_STRING_MAX_SIZE+1];

    if (!(lpstrCommand = GlobalLock(hCommand)))
    {
  PostMessage(hwndClientDDE,
            WM_DDE_ACK,
      hwndServerDDE,
      MAKELONG(0, hCommand)); /* negative acknowledgement */
        return;
    }
    if (lstrlen(lpstrCommand) > EXECUTE_STRING_MAX_SIZE)
  lpstrCommand[EXECUTE_STRING_MAX_SIZE] = 0;
    lstrcpy(szExecuteString, lpstrCommand);
    GlobalUnlock(hCommand);
    PostMessage(hwndClientDDE,
  WM_DDE_ACK,
  hwndServerDDE,
  MAKELONG(0x8000, hCommand)); /* positive acknowledgement */

    MessageBox(hwndMain,
  szExecuteString,
  "Server Received Execute Command",
  MB_OK);
    return;
}




/****************************************************************************

    FUNCTION: ServerInitiate

    PURPOSE:  Called when server application receives WM_DDE_INITIATE message

****************************************************************************/
void ServerInitiate(hwndClientDDE, lParam)
    HWND  hwndClientDDE;
    LONG  lParam;
{
    HWND  hwndServerDDE;
    ATOM  atomApplicationRcvd;
    ATOM  atomTopicRcvd;
    ATOM  atomApplicationReturn;
    ATOM  atomTopicReturn;
    char  szApplication[APP_MAX_SIZE+1];
    char  szTopic[TOPIC_MAX_SIZE+1];

    if (!(hwndServerDDE = CreateWindow(
      "ServerDDEWndClass",
      "ServerDDE",
      WS_CHILD,  /* not visible */
      0, 0, 0, 0, /* no position or dimensions */
      hwndMain,  /* parent */
      NULL,  /* no menu */
      hInst,
      NULL)))
    {
  return;
    }

    if (atomApplicationRcvd = LOWORD(lParam))
        GlobalGetAtomName(atomApplicationRcvd, szApplication, APP_MAX_SIZE);
    if (atomApplicationRcvd && strcmpi(szApplication,"SERVER"))
    { /* if application was specified but it wasn't "server" */
        return;
    }
    if (atomTopicRcvd = HIWORD(lParam))
    {
        GlobalGetAtomName(atomTopicRcvd, szTopic, TOPIC_MAX_SIZE);
        if (strcmpi(szTopic, szDocName))
            return;
    }
    if (AddConv(hwndServerDDE, hwndClientDDE))
    {
        atomApplicationReturn = GlobalAddAtom("SERVER");
        atomTopicReturn = GlobalAddAtom(szDocName);
  if (!SendMessage(hwndClientDDE,
                WM_DDE_ACK,
    hwndServerDDE,
                MAKELONG(atomApplicationReturn, atomTopicReturn)))
        {
            GlobalDeleteAtom(atomApplicationReturn);
            GlobalDeleteAtom(atomTopicReturn);
        }
    }
    return;
}



/****************************************************************************

    FUNCTION: ServerPoke

    PURPOSE:  Called when server application receives WM_DDE_POKE message.

****************************************************************************/
void ServerPoke(hwndServerDDE, hwndClientDDE, lParam)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
    LONG  lParam;
{
    HANDLE        hPokeData;
    DDEPOKE FAR * lpPokeData;
    ATOM          atomItem;
    int           nItem;
    char          szItemName[ITEM_NAME_MAX_SIZE+1];
    char          szItemValue[ITEM_VALUE_MAX_SIZE+1];
    BOOL          bRelease;
    char        * pcCarriageReturn;


    hPokeData = LOWORD(lParam);
    atomItem = HIWORD(lParam);

    GlobalGetAtomName(atomItem, szItemName, ITEM_NAME_MAX_SIZE);
    if (!(lpPokeData = (DDEPOKE FAR *)GlobalLock(hPokeData))
        || lpPokeData->cfFormat != CF_TEXT
        || !(nItem = GetItemNumber(szItemName)))
    {
  PostMessage(hwndClientDDE,
           WM_DDE_ACK,
     hwndServerDDE,
           MAKELONG(0, atomItem)); /* negative acknowledgement */
  return;
    }

    lstrcpy(szItemValue, lpPokeData->Value);
    if (pcCarriageReturn = strchr(szItemValue, '\r'))
        *pcCarriageReturn = 0;  /* remove CR/LF */
    SetDlgItemText(hwndMain, nItem, szItemValue);
    MaybeAdviseData(nItem);

    /* Save value of fRelease, since pointer may be invalidated by */
    /*  GlobalUnlock()              */
    bRelease = lpPokeData->fRelease;
    GlobalUnlock(hPokeData);

    if (bRelease)
    {
        GlobalFree(hPokeData);
    }

    /* Since we are re-using the item atom, we should not delete it */
    /* if PostMessage fails:  the client should delete the atom     */
    /* when it gets a time-out on the expected ACK.        */
    PostMessage(hwndClientDDE,
  WM_DDE_ACK,
  hwndServerDDE,
  MAKELONG(0x8000, atomItem));  /* positive acknowledgement */
    return;
}



/****************************************************************************

    FUNCTION: ServerRequest

    PURPOSE:  Called when server application receives WM_DDE_REQUEST message.

****************************************************************************/
void ServerRequest(hwndServerDDE, hwndClientDDE, lParam)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
    LONG  lParam;
{
    char szItem[ITEM_NAME_MAX_SIZE+1];
    char szItemValue[ITEM_VALUE_MAX_SIZE+1];
    int  nItem;

    GlobalGetAtomName(HIWORD(lParam), szItem, ITEM_NAME_MAX_SIZE);
    if (!(nItem = GetItemNumber(szItem))
  || (LOWORD(lParam) != CF_TEXT)) /* this app supports only CF_TEXT */
    {
  PostMessage(hwndClientDDE,
            WM_DDE_ACK,
      hwndServerDDE,
            MAKELONG(0, HIWORD(lParam))); /* NACK */
        return;
    }
    if (!GetDlgItemText(hwndMain, nItem, szItemValue, ITEM_VALUE_MAX_SIZE))
    {
  strcpy(szItemValue," ");
    }
    /* send now, don't defer, and don't ask for ACK */
    SendData(hwndServerDDE, hwndClientDDE, szItem, szItemValue, FALSE, FALSE)
    GlobalDeleteAtom(HIWORD(lParam));
    return;
}


/****************************************************************************

    FUNCTION: ServerTerminate

    PURPOSE:  Called when server application receives WM_DDE_TERMINATE messag

****************************************************************************/
void ServerTerminate(hwndServerDDE, hwndClientDDE)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
{

    if (!IsConvInTerminateState(hwndClientDDE))
    { /* Client has requested terminate: respond with terminate */
  PostMessage(hwndClientDDE, WM_DDE_TERMINATE, hwndServerDDE, 0L);
    }

    RemoveConv(hwndServerDDE);
    DestroyWindow(hwndServerDDE);
    return;
}



/****************************************************************************

    FUNCTION: ServerUnadvise

    PURPOSE:  Called when server application receives WM_DDE_UNADIVSE message

****************************************************************************/
void ServerUnadvise(hwndServerDDE, hwndClientDDE, lParam)
    HWND  hwndServerDDE;
    HWND  hwndClientDDE;
    LONG  lParam;
{
    char szItem[ITEM_NAME_MAX_SIZE+1];
    int  nItem;
    BOOL bSuccess;

    if (HIWORD(lParam))
    {
        GlobalGetAtomName(HIWORD(lParam), szItem, ITEM_NAME_MAX_SIZE);
        nItem = GetItemNumber(szItem);
  bSuccess = RemoveAdvise(hwndServerDDE, nItem);
    }
    else
    {   /* HIWORD(lParam)==0 means remove all advises */
  bSuccess = RemoveAdvise(hwndServerDDE, 0);
    }
    if (bSuccess)
    {
  PostMessage(hwndClientDDE,
            WM_DDE_ACK,
      hwndServerDDE,
            MAKELONG(0x8000, HIWORD(lParam))); /* positive ack */
    }
    else
    {
  PostMessage(hwndClientDDE,
            WM_DDE_ACK,
      hwndServerDDE,
            MAKELONG(0, HIWORD(lParam))); /* negative ack */
    }
    return;
}

/****************************************************************************

    FUNCTION: TerminateConversations

    PURPOSE:  Processes WM_DESTROY message, terminates all conversations.

****************************************************************************/
void TerminateConversations()
{
   HWND  hwndServerDDE;
   LONG  lTimeOut;
   MSG   msg;


   /* Terminate each active conversation */
   hwndServerDDE = NULL;
   while (hwndServerDDE = GetNextConv(hwndServerDDE))
   {
  SendTerminate(hwndServerDDE, GetHwndClientDDE(hwndServerDDE));
   }

   /* Wait for all conversations to terminate OR for time out */
   lTimeOut = GetTickCount() + (LONG)nAckTimeOut;
   while (PeekMessage(&msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
   {
         DispatchMessage (&msg);
   if (msg.message == WM_DDE_TERMINATE)
   {
       if (!AtLeastOneConvActive())
     break;
   }
         if (GetTickCount() > lTimeOut)
             break;
   }

   return;
}


SERVER.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\DDE\SERVER.C

/***************************************************************************

    PROGRAM: SERVER

    PURPOSE: Illustrates server side of DDE conversation

    MODULES:

        SERVER.C    Window and dialog procedures.
  SERVDATA.C  Maintains conversation information.
        SERVDDE.C   Processes incoming and outgoing DDE messages.

****************************************************************************/

#include "windows.h"
#include "dde.h"
#include "server.h"
#include "servres.h"
#include <string.h>
#include <stdlib.h>



static int     xDelta;
static int     yDelta;

long FAR PASCAL MainWndProc(HWND, unsigned, WORD, LONG);
BOOL InitApplication(HANDLE);
void InitAddedInstance(HANDLE, HANDLE);
BOOL InitInstance(HANDLE, int);
void MaybeAdviseData(int);
BOOL FAR PASCAL AboutDlgProc(HWND, unsigned, WORD, LONG);


/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE:  Calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;
    HANDLE hAccel;

    if (!hPrevInstance)
    {
  if (!InitApplication(hInstance))
      return (FALSE);
    }
    else
    {
        InitAddedInstance(hInstance, hPrevInstance);
    }

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    hAccel = LoadAccelerators(hInstance, "ServerAcc");

    while (GetMessage(&msg, NULL, NULL, NULL))
    {
  if (!TranslateAccelerator(hwndMain, hAccel, &msg))
  {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
  }

    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    nDoc = 1;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "ServerMenu";
    wc.lpszClassName = "ServerWClass";

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

    wc.style = NULL;
    wc.lpfnWndProc = DDEWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = NULL;
    wc.hbrBackground = NULL;
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = "ServerDDEWndClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION: InitAddedInstance

    PURPOSE:  Increment document number

****************************************************************************/
void InitAddedInstance(hInstance, hPrevInstance)
    HANDLE  hInstance;
    HANDLE  hPrevInstance;
{
    GetInstanceData(hPrevInstance, (NPSTR)&nDoc, sizeof(int));
    nDoc++;
    return;
}



/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle, creates main window, and creates
        3 child edit controls with id's 1, 2, and 3.

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    char        szNumber[4];
    char        szCaption[20];
    HDC         hDC;
    PAINTSTRUCT ps;
    TEXTMETRIC  tm;
    int         nItem;
    int         nHorzRes, nVertRes;

    InitAckTimeOut(); /* in module SERVDDE */

    hInst = hInstance;

    strcpy(szDocName, "FILE");
    itoa(nDoc, szNumber, 10);
    strcat(szDocName, szNumber);
    strcpy(szCaption, "Server -- ");
    strcat(szCaption, szDocName);

    hwndMain = CreateWindow(
        "ServerWClass",
        szCaption,
        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwndMain)
        return (FALSE);

    hDC = GetDC(hwndMain);
    GetTextMetrics(hDC, (LPTEXTMETRIC)&tm);
    xDelta = tm.tmAveCharWidth;
    yDelta = tm.tmHeight + tm.tmExternalLeading;
    nHorzRes = GetDeviceCaps(hDC, HORZRES);
    nVertRes = GetDeviceCaps(hDC, VERTRES);
    ReleaseDC(hwndMain, hDC);

    MoveWindow(hwndMain,
  nHorzRes/2 + xDelta*(nDoc+5),
        ((nDoc-1)&1)*nVertRes/2 + yDelta*nDoc,
        xDelta*30,
  yDelta*12,
        FALSE);

    for (nItem = 1; nItem < 4; nItem++)
    {
        CreateWindow("edit", NULL,
      WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | WS_TABSTOP,
            9*xDelta, (2*nItem-1)*yDelta, 12*xDelta, (yDelta*3)/2,
      hwndMain, nItem, hInst, NULL);
    }

    if (!(cfLink = RegisterClipboardFormat("Link")))
  return (FALSE);

    ShowWindow(hwndMain, nCmdShow);
    UpdateWindow(hwndMain);


    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for server

****************************************************************************/

long FAR PASCAL MainWndProc(hwnd, message, wParam, lParam)
HWND hwnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    HDC  hDC;
    PAINTSTRUCT ps;
    HWND hctlItem;
    int  nItem;
    char szItemName[8];
    FARPROC lpAboutDlgProc;


    switch (message)
    {
        case WM_SETFOCUS:
            SetFocus(GetDlgItem(hwnd,1));
            break;

        case WM_PAINT:
      hDC = BeginPaint(hwnd, &ps);
            strcpy(szItemName, "Item1:");
            for (nItem = 1; nItem < 4; nItem++)
            {
    /* display labels for the edit controls */
                TextOut(hDC, xDelta, (2*nItem-1)*yDelta, szItemName, 6);
                szItemName[4]++;
            }
      EndPaint(hwnd, &ps);
            break;

        case WM_COMMAND:
            switch (wParam)
            {
               case IDM_COPY:
       hctlItem = GetFocus();
       for (nItem = 1; nItem <= 3; nItem++)
       {
           if (hctlItem == GetDlgItem(hwnd, nItem))
           {
         DoEditCopy(nItem);
         break;
           }
       }
       break;

         case ID_TAB:
         case ID_SHIFT_TAB:
       if (IsChild(hwndMain, GetFocus()))
       {
           nItem = GetWindowWord(GetFocus(), GWW_ID);
           if (wParam == ID_TAB)
           {
         if (nItem++ == 3)
             nItem = 1;
           }
           else
           {
         if (nItem-- == 1)
             nItem = 3;
           }
       }
       else
       {
           nItem = 1;
       }
       SetFocus(GetDlgItem(hwndMain, nItem));
       break;

         case 1:
               case 2:
               case 3:
                  if (HIWORD(lParam)==EN_KILLFOCUS)
                  {
                     hctlItem = GetDlgItem(hwnd, wParam);
                     if (SendMessage(hctlItem, EM_GETMODIFY, 0, 0L))
                     {
      MaybeAdviseData(wParam);
                        SendMessage(hctlItem, EM_SETMODIFY, 0, 0L);
                     }
                  }
                  break;

    case IDM_ABOUT:
        lpAboutDlgProc = MakeProcInstance(AboutDlgProc, hInst);
        DialogBox(hInst,
      "About",
      hwndMain,
      lpAboutDlgProc);
        FreeProcInstance(lpAboutDlgProc);
        break;
      }
            break;

        case WM_DDE_INITIATE:
            ServerInitiate((HWND)wParam, lParam);
            break;

        case WM_DESTROY:

            /* Terminate all DDE conversations before destroying
               client window */
      TerminateConversations();
            PostQuitMessage(0);
            break;

        default:
            return (DefWindowProc(hwnd, message, wParam, lParam));
    }
    return (0L);
}



/****************************************************************************

    FUNCTION: MaybeAdviseData

    PURPOSE:  Send data to all clients for which a hot or warm link
        has been established for the specified item.

****************************************************************************/
void MaybeAdviseData(nItem)
    int   nItem;
{
    HWND hwndServerDDE;
    char szItemName[ITEM_NAME_MAX_SIZE+1];
    char szItemValue[ITEM_VALUE_MAX_SIZE+1];
    BOOL bDeferUpdate;
    BOOL bAckRequest;

    hwndServerDDE = NULL;

    while (1)
    {
  if (hwndServerDDE = GetNextAdvise(hwndServerDDE, nItem))
        {
      GetAdviseData(hwndServerDDE,
                nItem,
                szItemName,
                szItemValue,
                &bDeferUpdate,
                &bAckRequest);

      SendData(hwndServerDDE,
    GetHwndClientDDE(hwndServerDDE),
                szItemName,
                szItemValue,
                bDeferUpdate,
                bAckRequest);
        }
        else return;
    }
}


/****************************************************************************

    FUNCTION: AboutDlgProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

****************************************************************************/

BOOL FAR PASCAL AboutDlgProc(hDlg, message, wParam, lParam)
    HWND hDlg;
    unsigned message;
    WORD wParam;
    LONG lParam;
{
    switch (message) {
  case WM_INITDIALOG:
      return (TRUE);

  case WM_COMMAND:
      if (wParam == IDOK || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
      }
      break;
    }
    return (FALSE);
}


SHOWDIB.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SHOWDIB\SHOWDIB.C

/****************************************************************************
 *                         *
 *  PROGRAM  : ShowDIB.c                   *
 *                         *
 *  PURPOSE  : Application to illustrate the use of the GDI           *
 *      DIB (Device Independent Bitmap) and Palette manager         *
 *      functions.                   *
 *                         *
 *  FUNCTIONS  : WinMain ()     -  Creates the app. window and enters *
 *              the message loop.           *
 *                         *
 *      WndProc()     -  Processes app. window messages.    *
 *                         *
 *      MenuCommand()    -  Processes menu commands.         *
 *                         *
 *      FreeDIB()     -  Frees currently active objects.    *
 *                         *
 *      InitDIB()     -  Reads DIB from a file and loads it.*
 *                         *
 ****************************************************************************

#include <windows.h>
#include <io.h>
#include <stdio.h>
#include "showdib.h"

DIBPARAMS      DIBParams;       /* params for the SETSCALING escape */
char         achFileName[128] = "";
DWORD          dwOffset;
NPLOGPALETTE   pLogPal;
HPALETTE       hpalSave = NULL;
HANDLE         hInst ;
RECT         rcClip;
extern         WORD _WinFlags;
static         HCURSOR hcurSave;

BOOL  fPalColors  = FALSE;        /* TRUE if the current DIB's color table
              /* contains palette indexes not rgb values */
WORD  nAnimating  = 0;        /* Palette animation count     */
WORD  UpdateCount = 0;

BOOL  bMemoryDIB    = FALSE; /* Load Entire DIB into memory in CF_DIB format
BOOL  bUpdateColors = TRUE;  /* Directly update screen colors          */
BOOL  bDIBToDevice  = FALSE; /* Use SetDIBitsToDevice() to BLT data.
BOOL  bNoUgly       = FALSE; /* Make window black on a WM_PALETTEISCHANGING
BOOL  bLegitDraw    = FALSE; /* We have a valid bitmap to draw         */

char  szBitmapExt[] = "*.BMP; *.DIB; *.RLE";     /* possible file extensions
WORD  wTransparent  = TRANSPARENT;       /* Mode of DC         */
char  szAppName[]   = "ShowDIB" ;       /* App. name          */

HPALETTE hpalCurrent   = NULL;         /* Handle to current palette         *
HANDLE   hdibCurrent   = NULL;         /* Handle to current memory DIB
HBITMAP  hbmCurrent    = NULL;         /* Handle to current memory BITMAP
HANDLE   hbiCurrent    = NULL;         /* Handle to current bitmap info struc
HWND   hWndApp;           /* Handle to app. window          */

/* Styles of app. window */
DWORD         dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
       WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME;

void PrintDIB (HWND hWnd, HDC hDC, int x, int y, int dx, int dy);
/****************************************************************************
 *                      *
 *  FUNCTION   : WinMain(HANDLE, HANDLE, LPSTR, int)          *
 *                      *
 *  PURPOSE    : Creates the app. window and enters the message loop.      *
 *                      *
 ****************************************************************************
int  PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)

HANDLE hInstance, hPrevInstance ;
LPSTR  lpszCmdLine ;
int    nCmdShow ;
{
     HWND   hWnd ;
     WNDCLASS   wndclass ;
     MSG   msg ;
     short   xScreen, yScreen ;
     char   ach[40];

     hInst = hInstance ;

     /* default to MEMORY DIB's if XWindows */
     bMemoryDIB = WinFlags & WF_PMODE;

     /* Initialize clip rectangle */
     SetRectEmpty(&rcClip);

     if (!hPrevInstance) {
   wndclass.style   = CS_DBLCLKS;
   wndclass.lpfnWndProc  = WndProc ;
   wndclass.cbClsExtra  = 0 ;
   wndclass.cbWndExtra  = 0 ;
   wndclass.hInstance  = hInstance ;
   wndclass.hIcon   = LoadIcon(hInst, "SHOWICON");
   wndclass.hCursor  = LoadCursor (NULL, IDC_ARROW) ;
   wndclass.hbrBackground = GetStockObject (BLACK_BRUSH) ;
   wndclass.lpszMenuName  = szAppName ;
   wndclass.lpszClassName = szAppName ;

   if (!RegisterClass (&wndclass))
     return FALSE ;
     }

     if (!GetProfileString("extensions", "bmp", "", ach, sizeof(ach)))
       WriteProfileString("extensions", "bmp", "showdib.exe ^.bmp");
     if (!GetProfileString("extensions", "dib", "", ach, sizeof(ach)))
       WriteProfileString("extensions", "dib", "showdib.exe ^.dib");

     /* Save the pointer to the command line */
     lstrcpy(achFileName, lpszCmdLine);

     xScreen = GetSystemMetrics (SM_CXSCREEN) ;
     yScreen = GetSystemMetrics (SM_CYSCREEN) ;

     /* Create the app. window */
     hWnd = CreateWindow( szAppName,
        szAppName,
        dwStyle,
        CW_USEDEFAULT,
        0,
        xScreen / 2,
        yScreen / 2,
        NULL,
        NULL,
        hInstance,
        NULL) ;

     ShowWindow (hWndApp = hWnd, nCmdShow) ;

     /* Enter message loop */
     while (GetMessage (&msg, NULL, 0, 0)) {
   TranslateMessage (&msg) ;
   DispatchMessage (&msg) ;
     }

     return msg.wParam ;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : WndProc (hWnd, iMessage, wParam, lParam)        *
 *                      *
 *  PURPOSE    : Processes window messages.            *
 *                      *
 ****************************************************************************
long  FAR PASCAL WndProc (hWnd, iMessage, wParam, lParam)

HWND     hWnd ;
unsigned iMessage ;
WORD     wParam ;
LONG     lParam ;

{
    PAINTSTRUCT      ps;
    HDC        hDC;
    HANDLE       h;
    int        i;
    int        iMax;
    int        iMin;
    int        iPos;
    int        dn;
    RECT       rc,Rect;
    HPALETTE       hOldPal;
    HMENU       hMenu;

    switch (iMessage) {
  case WM_DESTROY:
    /* Clean up and quit */
    FreeDib();
    PostQuitMessage(0);
    break ;

  case WM_CREATE:
    /* Allocate space for our logical palette */
    pLogPal = (NPLOGPALETTE) LocalAlloc( LMEM_FIXED,
                 (sizeof(LOGPALETTE) +
                 (sizeof(PALETTEENTRY)*(MAXPALETTE))));

    /* If DIB initialization fails, quit */
    if (achFileName[0] && !InitDIB(hWnd))
      PostQuitMessage (3) ;

    /* fall through */

  case WM_WININICHANGE:

    hMenu = GetMenu(hWnd);

    /* If printer initialization succeeds, enable appropriate
     * menu item and clean up.
     */
    if ( hDC = GetPrinterDC() ) {
        EnableMenuItem( hMenu,
            IDM_PRINT,
            (RC_DIBTODEV &
             GetDeviceCaps(hDC, RASTERCAPS)) ?
             MF_ENABLED :
             MF_GRAYED | MF_DISABLED);
        DeleteDC(hDC);
    }

    break;

  case WM_PALETTEISCHANGING:
    /* if SHOWDIB was not responsible for palette change and if
     * ok to hide changes, paint app. window black.
     */
    if (wParam != hWnd && bNoUgly) {
        GetClientRect(hWnd, &Rect);

        hDC = GetDC(hWnd);
        FillRect( hDC, (LPRECT) &Rect, GetStockObject(BLACK_BRUSH));
        ReleaseDC(hWnd, hDC);
    }
    break;

  case WM_ACTIVATE:
    if (!wParam)  /* app. is being de-activated */
       break;
    /* If the app. is moving to the foreground, fall through and
     * redraw full client area with the newly realized palette,
     * if the palette has changed.
     */

  case WM_QUERYNEWPALETTE:
    /* If palette realization causes a palette change,
     * we need to do a full redraw.
     */
    if (bLegitDraw) {
        hDC = GetDC (hWnd);
        hOldPal = SelectPalette (hDC, hpalCurrent, 0);

        i = RealizePalette(hDC);

        SelectPalette (hDC, hOldPal, 0);
        ReleaseDC (hWnd, hDC);

        if (i) {
      InvalidateRect (hWnd, (LPRECT) (NULL), 1);
      UpdateCount = 0;
      return 1;
        } else
      return FALSE;
    }
    else
        return FALSE;
    break;

  case WM_PALETTECHANGED:
    /* if SHOWDIB was not responsible for palette change and if
     * palette realization causes a palette change, do a redraw.
     */
     if (wParam != hWnd){
        if (bLegitDraw){
      hDC = GetDC (hWnd);
      hOldPal = SelectPalette (hDC, hpalCurrent, 0);

      i = RealizePalette (hDC);

      if (i){
          if (bUpdateColors){
        UpdateColors (hDC);
        UpdateCount++;
          }
          else
        InvalidateRect (hWnd, (LPRECT) (NULL), 1);
      }

      SelectPalette (hDC, hOldPal, 0);
      ReleaseDC (hWnd, hDC);
        }
    }
    break;

  case WM_RENDERALLFORMATS:
    /* Ensure that clipboard data can be rendered even tho'
     * app. is being destroyed.
     */
    SendMessage(hWnd,WM_RENDERFORMAT,CF_DIB,0L);
    SendMessage(hWnd,WM_RENDERFORMAT,CF_BITMAP,0L);
    SendMessage(hWnd,WM_RENDERFORMAT,CF_PALETTE,0L);
    break;

  case WM_RENDERFORMAT:
    /* Format data in manner specified and pass the data
     * handle to clipboard.
     */
    if (h = RenderFormat(wParam))
        SetClipboardData(wParam,h);
    break;

  case WM_COMMAND:
    /* Process menu commands */
    return MenuCommand (hWnd, wParam);
    break;

  case WM_TIMER:
    /* Signal for palette animation */
    hDC = GetDC(hWnd);
    hOldPal = SelectPalette(hDC, hpalCurrent, 0);
    {
        PALETTEENTRY peTemp;

        /* Shift all palette entries left by one position and wrap
         * around the first entry
         */
        peTemp = pLogPal->palPalEntry[0];
        for (i = 0; i < (pLogPal->palNumEntries - 1); i++)
       pLogPal->palPalEntry[i] = pLogPal->palPalEntry[i+1];
        pLogPal->palPalEntry[i] = peTemp;
    }
    /* Replace entries in logical palette with new entries*/
    AnimatePalette(hpalCurrent, 0, pLogPal->palNumEntries, pLogPal->palPalEnt

    SelectPalette(hDC, hOldPal, 0);
    ReleaseDC(hWnd, hDC);

    /* Decrement animation count and terminate animation
     * if it reaches zero
     */
    if (!(--nAnimating))
        PostMessage(hWnd,WM_COMMAND,IDM_ANIMATE0,0L);
    break;

  case WM_PAINT:
    /* If we have updated more than once, the rest of our
     * window is not in some level of degradation worse than
     * our redraw...  we need to redraw the whole area
     */
    if (UpdateCount > 1) {
        BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        UpdateCount = 0;
        InvalidateRect(hWnd, (LPRECT) (NULL), 1);
        break;
    }

    hDC = BeginPaint(hWnd, &ps);
    AppPaint(hWnd,
       hDC,
       GetScrollPos(hWnd,SB_HORZ),
       GetScrollPos(hWnd,SB_VERT) );
    EndPaint(hWnd, &ps);
    break ;

  case WM_SIZE:
      SetScrollRanges(hWnd);
      break;

  case WM_KEYDOWN:
      /* Translate keyboard messages to scroll commands */
      switch (wParam) {
    case VK_UP:    PostMessage (hWnd, WM_VSCROLL, SB_LINEUP,   0L);
             break;

    case VK_DOWN:  PostMessage (hWnd, WM_VSCROLL, SB_LINEDOWN, 0L);
             break;

    case VK_PRIOR: PostMessage (hWnd, WM_VSCROLL, SB_PAGEUP,   0L);
             break;

    case VK_NEXT:  PostMessage (hWnd, WM_VSCROLL, SB_PAGEDOWN, 0L);
             break;

    case VK_HOME:  PostMessage (hWnd, WM_HSCROLL, SB_PAGEUP,   0L);
             break;

    case VK_END:   PostMessage (hWnd, WM_HSCROLL, SB_PAGEDOWN, 0L);
             break;

    case VK_LEFT:  PostMessage (hWnd, WM_HSCROLL, SB_LINEUP,   0L);
             break;

    case VK_RIGHT: PostMessage (hWnd, WM_HSCROLL, SB_LINEDOWN, 0L);
             break;
      }
      break;

  case WM_KEYUP:
      switch (wParam) {
         case VK_UP:
         case VK_DOWN:
         case VK_PRIOR:
         case VK_NEXT:
      PostMessage (hWnd, WM_VSCROLL, SB_ENDSCROLL, 0L);
      break;

         case VK_HOME:
         case VK_END:
         case VK_LEFT:
         case VK_RIGHT:
      PostMessage (hWnd, WM_HSCROLL, SB_ENDSCROLL, 0L);
      break;
      }
      break;

  case WM_VSCROLL:
      /* Calculate new vertical scroll position */
      GetScrollRange (hWnd, SB_VERT, &iMin, &iMax);
      iPos = GetScrollPos (hWnd, SB_VERT);
      GetClientRect (hWnd, &rc);

      switch (wParam) {
    case SB_LINEDOWN:      dn =  rc.bottom / 16 + 1;
               break;

    case SB_LINEUP:        dn = -rc.bottom / 16 + 1;
               break;

    case SB_PAGEDOWN:      dn =  rc.bottom / 2  + 1;
               break;

    case SB_PAGEUP:        dn = -rc.bottom / 2  + 1;
               break;

    case SB_THUMBTRACK:
    case SB_THUMBPOSITION: dn = LOWORD(lParam)-iPos;
               break;

    default:         dn = 0;
      }
      /* Limit scrolling to current scroll range */
      if (dn = BOUND (iPos + dn, iMin, iMax) - iPos) {
    ScrollWindow (hWnd, 0, -dn, NULL, NULL);
    SetScrollPos (hWnd, SB_VERT, iPos + dn, TRUE);
      }
      break;

  case WM_HSCROLL:
      /* Calculate new horizontal scroll position */
      GetScrollRange (hWnd, SB_HORZ, &iMin, &iMax);
      iPos = GetScrollPos (hWnd, SB_HORZ);
      GetClientRect (hWnd, &rc);

      switch (wParam) {
    case SB_LINEDOWN:      dn =  rc.right / 16 + 1;
               break;

    case SB_LINEUP:        dn = -rc.right / 16 + 1;
               break;

    case SB_PAGEDOWN:      dn =  rc.right / 2  + 1;
               break;

    case SB_PAGEUP:        dn = -rc.right / 2  + 1;
               break;

    case SB_THUMBTRACK:
    case SB_THUMBPOSITION: dn = LOWORD (lParam) - iPos;
               break;

    default:         dn = 0;
      }
      /* Limit scrolling to current scroll range */
      if (dn = BOUND (iPos + dn, iMin, iMax) - iPos) {
    ScrollWindow (hWnd, -dn, 0, NULL, NULL);
    SetScrollPos (hWnd, SB_HORZ, iPos + dn, TRUE);
      }
      break;

  case WM_LBUTTONDOWN:
      /* Start rubberbanding a rect. and track it's dimensions.
       * set the clip rectangle to it's dimensions.
       */
      TrackMouse (hWnd, MAKEPOINT (lParam));
      break;

  case WM_LBUTTONDBLCLK:
      break;

  case WM_INITMENU:
      /* check/uncheck menu items depending on state  of related
       * flags
       */
      CheckMenuItem(wParam, IDM_UPDATECOL,
    (bUpdateColors ? MF_CHECKED : MF_UNCHECKED));
      CheckMenuItem(wParam, IDM_TRANSPARENT,
    (wTransparent == TRANSPARENT ? MF_CHECKED : MF_UNCHECKED));
      CheckMenuItem(wParam, IDM_DIBSCREEN,
    (bDIBToDevice ? MF_CHECKED : MF_UNCHECKED));
      CheckMenuItem(wParam, IDM_NOUGLY,
    (bNoUgly ? MF_CHECKED : MF_UNCHECKED));
      CheckMenuItem(wParam, IDM_MEMORYDIB,
    (bMemoryDIB ? MF_CHECKED : MF_UNCHECKED));
      EnableMenuItem(wParam, IDM_PASTEDIB,
    IsClipboardFormatAvailable(CF_DIB)?MF_ENABLED:MF_GRAYED);
      EnableMenuItem(wParam, IDM_PASTEDDB,
    IsClipboardFormatAvailable(CF_BITMAP)?MF_ENABLED:MF_GRAYED);
      EnableMenuItem(wParam, IDM_PASTEPAL,
    IsClipboardFormatAvailable(CF_PALETTE)?MF_ENABLED:MF_GRAYED);
      EnableMenuItem(wParam, IDM_PRINT,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      EnableMenuItem(wParam, IDM_SAVE,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      EnableMenuItem(wParam, IDM_COPY,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);

      EnableMenuItem(wParam, IDM_ANIMATE0,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      EnableMenuItem(wParam, IDM_ANIMATE5,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      EnableMenuItem(wParam, IDM_ANIMATE50,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      EnableMenuItem(wParam, IDM_ANIMATE100,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      EnableMenuItem(wParam, IDM_ANIMATE200,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      EnableMenuItem(wParam, IDM_ANIMATE201,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      EnableMenuItem(wParam, IDM_STEALCOL,
    bLegitDraw ? MF_ENABLED : MF_GRAYED);
      break;

  default:
      return DefWindowProc (hWnd, iMessage, wParam, lParam) ;

    }
    return 0L ;

}
/****************************************************************************
 *                      *
 *  FUNCTION   : MenuCommand ( HWND hWnd, WORD wParam)          *
 *                      *
 *  PURPOSE    : Processes menu commands.            *
 *                      *
 *  RETURNS    : TRUE  - if command could be processed.         *
 *     FALSE - otherwise              *
 *                      *
 ****************************************************************************
BOOL MenuCommand (hWnd, id)
HWND hWnd;
WORD id;

{
    BITMAPINFOHEADER bi;
    HDC        hDC;
    HANDLE       h;
    HBITMAP       hbm;
    HPALETTE       hpal;
    int        i;
    char       Name[40];
    BOOL       bSave;
    int        xSize, ySize, xRes, yRes, dx, dy;
    RECT       Rect;
    int        fh;
    WORD       fFileOptions;

    switch (id) {
  case IDM_ABOUT:
    /* Show About .. box */
    fDialog (ABOUTBOX, hWnd,AppAbout);
    break;

  case IDM_COPY:
    if (!bLegitDraw)
        return 0L;

    /* Clean clipboard of contents */
    if (OpenClipboard(hWnd)) {
        EmptyClipboard ();
        SetClipboardData (CF_DIB   ,NULL);
        SetClipboardData (CF_BITMAP  ,NULL);
        SetClipboardData (CF_PALETTE ,NULL);
        CloseClipboard ();
    }
    break;

  case IDM_PASTEPAL:
    if (OpenClipboard (hWnd)) {
        if (h = GetClipboardData (CF_PALETTE)) {
      /* Delete current palette and get the CF_PALETTE data
       * from the clipboard
       */
      if (hpalCurrent)
          DeleteObject (hpalCurrent);

      hpalCurrent = CopyPalette (h);

      /*
       * If we have a bitmap realized against the old palette
       * delete the bitmap and rebuild it using the new palette.
       */
      if (hbmCurrent){
          DeleteObject (hbmCurrent);
          hbmCurrent = NULL;

          if (hdibCurrent)
        hbmCurrent = BitmapFromDib (hdibCurrent, hpalCurrent);
      }
        }
        CloseClipboard();
    }
    break;

  case IDM_PASTEDIB:
    if (OpenClipboard (hWnd)) {
        if (h = GetClipboardData (CF_DIB)) {
      /* Delete current DIB and get CF_DIB and
       * CF_PALETTE format data from the clipboard
       */
      hpal = GetClipboardData (CF_PALETTE);

      FreeDib();
      hdibCurrent = CopyHandle (h);
      if (hdibCurrent) {
          bLegitDraw = TRUE;
          lstrcpy(achFileName,"<Clipboard>");
          hbiCurrent = hdibCurrent;

          /* If there is a CF_PALETTE object in the
           * clipboard, this is the palette to assume
           * the DIB should be realized against, otherwise
           * create a palette for it.
           */
          if (hpal)
        hpalCurrent = CopyPalette (hpal);
          else
        hpalCurrent = CreateDibPalette (hdibCurrent);

          SizeWindow(hWnd);
      }
      else {
          bLegitDraw = FALSE;
          ErrMsg("No Memory Available!");
      }
        }
        CloseClipboard();
    }
    break;

  case IDM_PASTEDDB:
    if (OpenClipboard (hWnd)) {
        if (hbm = GetClipboardData(CF_BITMAP)) {
      hpal = GetClipboardData(CF_PALETTE);
      FreeDib();

      /*
       * If there is a CF_PALETTE object in the
       * clipboard, this is the palette to assume
       * the bitmap is realized against.
       */
      if (hpal)
          hpalCurrent = CopyPalette(hpal);
      else
          hpalCurrent = GetStockObject(DEFAULT_PALETTE);

      hdibCurrent = DibFromBitmap(hbm,BI_RGB,0,hpalCurrent);

      if (hdibCurrent) {
          bLegitDraw = TRUE;
          lstrcpy(achFileName,"<Clipboard>");
          hbiCurrent = hdibCurrent;

          if (bMemoryDIB)
        hbmCurrent = BitmapFromDib (hdibCurrent, hpalCurrent);

          SizeWindow(hWnd);
      }
      else {
          bLegitDraw = FALSE;
          ErrMsg("No Memory Available!");
      }
        }
        CloseClipboard ();
    }
    break;

  case IDM_PRINT:
    GetWindowText(hWnd, Name, sizeof(Name));

    DibInfo(hbiCurrent, &bi);

    if (!IsRectEmpty(&rcClip))
    {
        bi.biWidth  = rcClip.right  - rcClip.left;
        bi.biHeight = rcClip.bottom - rcClip.top;
    }

    /* Initialise printer stuff */
    if (!(hDC = GetPrinterDC()))
      break;

    xSize = GetDeviceCaps(hDC, HORZRES);
    ySize = GetDeviceCaps(hDC, VERTRES);
    xRes  = GetDeviceCaps(hDC, LOGPIXELSX);
    yRes  = GetDeviceCaps(hDC, LOGPIXELSY);

    /* Use half inch margins on left and right
     * and one inch on top. Maintain the same aspect ratio.
     */

    dx = xSize - xRes;
    dy = (int)((long)dx * bi.biHeight/bi.biWidth);

    /* Fix bounding rectangle for the picture .. */
    Rect.top    = yRes;
    Rect.left   = xRes / 2;
    Rect.bottom = yRes + dy;
    Rect.right  = xRes / 2 + dx;

    /* ... and inform the driver */
    Escape(hDC, SET_BOUNDS, sizeof(RECT), (LPSTR)&Rect, NULL);

    bSave = TRUE;

    if (InitPrinting(hDC, hWnd, hInst, Name)) {

      PrintDIB(hWnd, hDC, xRes/2, yRes, dx, dy);

      /* Signal to the driver to begin translating the drawing
       * commands to printer output...
       */
      Escape (hDC, NEWFRAME, NULL, NULL, NULL);

      TermPrinting(hDC);
    }

    DeleteDC(hDC);
    break;

  case IDM_OPEN:
    /* Bring up File/Open ... dialog */
    fh = DlgOpenFile (hWnd,
          "Select a DIB to display",
          (LONG)OF_EXIST | OF_MUSTEXIST | OF_NOOPTIONS,
          szBitmapExt,
          achFileName,
          NULL
          );

    /*  Load up the DIB if the user did not press cancel */
    if (fh > 0) {
       StartWait();
       if (InitDIB (hWnd))
           InvalidateRect (hWnd, NULL, FALSE);
       else
           bLegitDraw = FALSE;
       EndWait();
    }
    break;

  case IDM_SAVE:
    DibInfo(hbiCurrent,&bi);
    fFileOptions = 0;

    /* Depending on compression type for current DIB,
     * set the appropriate bit in the fFileOptions flag
     */
    if (bi.biCompression == BI_RGB)
        fFileOptions |= F_RGB;
    else if (bi.biCompression == BI_RLE4)
        fFileOptions |= F_RLE4;
    else if (bi.biCompression == BI_RLE8)
        fFileOptions |= F_RLE8;

    /* Depending on bits/pixel type for current DIB,
     * set the appropriate bit in the fFileOptions flag
     */
    switch (bi.biBitCount){
        case  1:
      fFileOptions |= F_1BPP;
      break;

        case  4:
      fFileOptions |= F_4BPP;
      break;

        case  8:
      fFileOptions |= F_8BPP;
      break;

        case 24:
      fFileOptions |= F_24BPP;
    }

    /* Bring up File/Save... dialog and get info. about filename,
     * compression, and bits/pix. of DIB to be written.
     */
    fh = DlgOpenFile (hWnd,
          "Select File to save DIB to",
          (LONG)OF_EXIST | OF_SAVE | OF_NOSHOWSPEC,
          szBitmapExt,
          achFileName,
          &fFileOptions);

    /* Extract DIB specs. if the user did not press cancel */
    if (fh != 0){
        if (fFileOptions & F_RGB)
      bi.biCompression = BI_RGB;

        if (fFileOptions & F_RLE4)
      bi.biCompression = BI_RLE4;

        if (fFileOptions & F_RLE8)
      bi.biCompression = BI_RLE8;

        if (fFileOptions & F_1BPP)
      bi.biBitCount = 1;

        if (fFileOptions & F_4BPP)
      bi.biBitCount = 4;

        if (fFileOptions & F_8BPP)
      bi.biBitCount = 8;

        if (fFileOptions & F_24BPP)
      bi.biBitCount = 24;

        /* Realize a DIB in the specified format and obtain a
         * handle to it.
         */
        hdibCurrent = RealizeDibFormat(bi.biCompression,bi.biBitCount);
        if (!hdibCurrent){
      ErrMsg("Unable to save the specified file");
      return 0L;
        }

        /* Write the DIB */
        StartWait();
        if (!WriteDIB(achFileName,hdibCurrent))
      ErrMsg("Unable to save the specified file");
        EndWait();
    }
    break;

  case IDM_EXIT:
    PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0L);
    break;

  case IDM_UPDATECOL:
    /* Toggle state of "update screen colors" flag. If it is
     * off, clear the "hide changes" flag
     */
    bUpdateColors = !bUpdateColors;
    if (bUpdateColors)
        bNoUgly = 0;
    break;

  case IDM_DIBSCREEN:
    bDIBToDevice = !bDIBToDevice;
    InvalidateRect(hWnd, (LPRECT) (NULL), 1);
    break;

  case IDM_MEMORYDIB:
    bMemoryDIB = !bMemoryDIB;
    break;

  case IDM_NOUGLY:
    /* Toggle state of "hide changes" flag. If it is off, clear
     * the "update screen colors" flag. This will tell SHOWDIB
     * to paint itself black while the palette is changing.
     */
    bNoUgly = !bNoUgly;
    if (bNoUgly)
        bUpdateColors = 0;
    break;

  case IDM_TRANSPARENT:
    /* Toggle DC mode */
    wTransparent = wTransparent == TRANSPARENT ?
        OPAQUE : TRANSPARENT;
    break;

  case IDM_ANIMATE0:
    if (!hpalSave)
        break;

    /* Reset animation count and stop timer */
    KillTimer(hWnd, 1);
    nAnimating = 0;

    /* Restore palette which existed before animation started */
    DeleteObject(hpalCurrent);
    hpalCurrent = hpalSave;

    /* Rebuild bitmap based on newly realized information */
    hDC = GetDC (hWnd);
    SelectPalette (hDC, hpalCurrent, 0);
    RealizePalette (hDC);
    ReleaseDC (hWnd, hDC);

    if (hbmCurrent){
        DeleteObject (hbmCurrent);
        hbmCurrent = NULL;

        if (hdibCurrent)
           hbmCurrent = BitmapFromDib (hdibCurrent, hpalCurrent);
    }
    hpalSave = NULL;

    /* Force redraw with new palette for everyone */
    InvalidateRect(hWnd, NULL, TRUE);
    break;

  case IDM_STEALCOL:
  case IDM_ANIMATE5:
  case IDM_ANIMATE20:
  case IDM_ANIMATE50:
  case IDM_ANIMATE100:
  case IDM_ANIMATE200:
  case IDM_ANIMATE201:
    /* Set animation count i.e number of times animation is to
     * take place.
     */
    nAnimating = id;
    if (id == IDM_STEALCOL)
      nAnimating = 0;

    /* Save current palette */
    hpalSave = CopyPalette(hpalCurrent);

    GetObject(hpalCurrent, sizeof(int), (LPSTR)&pLogPal->palNumEntries);
    GetPaletteEntries(hpalCurrent, 0, pLogPal->palNumEntries, pLogPal->palPal

    /* Reserve all entries in the palette otherwise AnimatePalette()
     * will not modify them
     */
    for (i = 0; i < pLogPal->palNumEntries; i++) {
         pLogPal->palPalEntry[i].peFlags = (BYTE)PC_RESERVED;
    }

    SetPaletteEntries(hpalCurrent, 0, pLogPal->palNumEntries, pLogPal->palPal

    /* Rebuild bitmap based on newly realized information */
    if (hbmCurrent){
        DeleteObject (hbmCurrent);
        hbmCurrent = NULL;

        if (hdibCurrent)
           hbmCurrent = BitmapFromDib (hdibCurrent, hpalCurrent);
    }

    /* Force redraw with new palette for everyone */
    InvalidateRect(hWnd, NULL, TRUE);

    /* Initiate the timer so that palette can be animated in
     * response to a WM_TIMER message
     */
    if (nAnimating && !SetTimer(hWnd, 1, 250, (LPSTR) NULL))
      nAnimating = 0;

  default:
    break;
    }

    return TRUE;
}

/****************************************************************************
 *                      *
 *  FUNCTION   : InitDIB(hWnd)                *
 *                      *
 *  PURPOSE    : Reads a DIB from a file, obtains a handle to it's          *
 *     BITMAPINFO struct., sets up the palette and loads the DIB. *
 *                      *
 *  RETURNS    : TRUE  - DIB loads ok              *
 *     FALSE - otherwise              *
 *                      *
 ****************************************************************************
int InitDIB(hWnd)
HWND hWnd;
{
    unsigned         fh;
    LPBITMAPINFOHEADER lpbi;
    WORD FAR *         pw;
    int          i;
    BITMAPINFOHEADER   bi;
    OFSTRUCT         of;

    FreeDib();

    /* Open the file and get a handle to it's BITMAPINFO */

    fh = OpenFile (achFileName, (LPOFSTRUCT)&of, OF_READ);
    if (fh == -1) {
  ErrMsg("Can't open file '%ls'", (LPSTR)achFileName);
  return FALSE;
    }
    hbiCurrent = ReadDibBitmapInfo(fh);

    dwOffset = _llseek(fh, 0L, SEEK_CUR);
    _lclose (fh);

    if (hbiCurrent == NULL) {
  ErrMsg("%ls is not a Legitimate DIB File!", (LPSTR)achFileName);
  return FALSE;
    }
    DibInfo(hbiCurrent,&bi);

    /* Set up the palette */
    hpalCurrent = CreateDibPalette(hbiCurrent);
    if (hpalCurrent == NULL) {
  ErrMsg("CreatePalette() Failed");
  return FALSE;
    }

    /*  Convert the DIB color table to palette relative indexes, so
     *  SetDIBits() and SetDIBitsToDevice() can avoid color matching.
     *  We can do this because the palette we realize is identical
     *  to the color table of the bitmap, ie the indexes match 1 to 1
     *
     *  Now that the DIB color table is palette indexes not RGB values
     *  we must use DIB_PAL_COLORS as the wUsage parameter to SetDIBits()
     */
    lpbi = (VOID FAR *)GlobalLock(hbiCurrent);
    if (lpbi->biBitCount != 24) {
  fPalColors = TRUE;

  pw = (WORD FAR *)((LPSTR)lpbi + lpbi->biSize);

  for (i=0; i<(int)lpbi->biClrUsed; i++)
      *pw++ = (WORD)i;
    }
    GlobalUnlock(hbiCurrent);
    bLegitDraw = TRUE;

    /*  If the input bitmap is not in RGB FORMAT the banding code will
     *  not work!  we need to load the DIB bits into memory.
     *  if memory DIB, load it all NOW!  This will avoid calling the
     *  banding code.
     */
    if (bMemoryDIB || bi.biCompression != BI_RGB)
  hdibCurrent = OpenDIB(achFileName);

    /*  If the RLE could not be loaded all at once, exit gracefully NOW,
     *  to avoid calling the banding code
     */
    if ((bi.biCompression != BI_RGB) && !hdibCurrent){
  ErrMsg ("Could not load RLE!");
  FreeDib();
  return FALSE;
    }

    if (hdibCurrent && !bDIBToDevice && bMemoryDIB){
  hbmCurrent = BitmapFromDib(hdibCurrent,hpalCurrent);
  if (!hbmCurrent){
      ErrMsg ("Could not create bitmap!");
      FreeDib();
      return FALSE;
  }
    }

    SizeWindow(hWnd);

    return TRUE;
}
/****************************************************************************
 *                      *
 *  FUNCTION   : FreeDib(void)                *
 *                      *
 *  PURPOSE    : Frees all currently active bitmap, DIB and palette objects *
 *     and initializes their handles.           *
 *                      *
 ****************************************************************************
void FreeDib(void)
{
    if (hpalCurrent)
  DeleteObject(hpalCurrent);

    if (hbmCurrent)
  DeleteObject(hbmCurrent);

    if (hdibCurrent)
  GlobalFree(hdibCurrent);

    if (hbiCurrent && hbiCurrent != hdibCurrent)
  GlobalFree(hbiCurrent);

    fPalColors  = FALSE;
    bLegitDraw  = FALSE;
    hpalCurrent = NULL;
    hdibCurrent = NULL;
    hbmCurrent  = NULL;
    hbiCurrent  = NULL;
    SetRectEmpty (&rcClip);
}


SHOWFONT.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SHOWFONT\SHOWFONT.C

/****************************************************************************

    PROGRAM: Showfont.c

    PURPOSE: Adds, deletes, creates and displays fonts

    FUNCTIONS:

        WinMain() - calls initialization function, processes message loop
        EditfileInit() - initializes window data and registers window
        EditfileWndProc() - processes messages
        About() - processes messages for "About" dialog box
        SelectFont() - select a font
        GetSizes() - get size of current font
        GetFonts() - get available fonts
        SetMyDC() - initializes DC
        Metric() - Metric dialog box
        Log() - Log dialog box
        AddDlg() - dialog box for adding a font
        RemoveDlg() - dialog box for removing a font
        CFontDlg()
        _lstrcpy() - long strcpy()
        _lstrncpy() - long strncpy()
        _lstrlen()  - long strlen()
        CheckFileName() - check for valid filename
        SeparateFile() - Separate filename and pathname
        UpdateListBox() - update file list box
        AddExt() - add default extension
        SetFaceName() - update title with current font's face name

****************************************************************************/

#include "windows.h"
#include "showfont.h"

HANDLE hInst;

HFONT hOFont, hFFont, hVFont, hSFont, hDFont, hMFont, hFont;
int hFile;
char line[4][64];
char FontNameList[32][128];                          /* list of added fonts
int nFontIndex = 0;                                  /* position in FontList
int nLineSpace;

TEXTMETRIC TextMetric;
LOGFONT LogFont;
FARPROC lpCFontDlg;
POINT ptCurrent = {0, 0};
short nBkMode = OPAQUE;
DWORD rgbBkColor = RGB(255, 255, 255);
DWORD rgbTextColor = RGB(0, 0, 0);
DWORD rgbColor;
short nAlignLCR = TA_LEFT;
short nAlignTBB = TA_TOP;
WORD wPaint = 0;
FARPROC lpColors;
char FontList[MAXFONT][32];
BYTE CharSet[MAXFONT];
BYTE PitchAndFamily[MAXFONT];
int FontIndex = 0;
int SizeList[MAXSIZE];
int SizeIndex = 0;
int CurrentFont = 0;
int CurrentSize = 0;
FARPROC lpSelectFont;
FARPROC lpEnumFunc;
WORD wPrevVAlign = IDM_ALIGNBASE;
WORD wPrevHAlign = IDM_ALIGNLEFT;
WORD wPrevFont = IDM_SYSTEM;
char AppName[] = "ShowFont Sample Application   Font: ";
char WindowTitle[80];
char str[255];
char DefPath[128];
char DefExt[] = ".fon";
char DefSpec[13];
char FontFileName[128];

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    HWND hWnd;
    MSG msg;

    if (!hPrevInstance)
        if (!ShowFontInit(hInstance))
            return (FALSE);

    hInst = hInstance;

    strcpy(WindowTitle, AppName);
    strcat(WindowTitle, "SYSTEM");                 /* default is SYSTEM font

    hWnd = CreateWindow("ShowFont",
        WindowTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL);

    if (!hWnd)
        return (FALSE);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, NULL, NULL)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (msg.wParam);
}

/****************************************************************************

    FUNCTION: ShowFontInit(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

int ShowFontInit(hInstance)
HANDLE hInstance;
{
    HANDLE hMemory;
    PWNDCLASS pWndClass;
    BOOL bSuccess;

    hMemory = LocalAlloc(LPTR, sizeof(WNDCLASS));
    pWndClass = (PWNDCLASS) LocalLock(hMemory);
    pWndClass->hCursor = LoadCursor(NULL, IDC_ARROW);
    pWndClass->hIcon = LoadIcon(NULL, IDI_APPLICATION);
    pWndClass->lpszMenuName = (LPSTR) "ShowFont";
    pWndClass->lpszClassName = (LPSTR) "ShowFont";
    pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
    pWndClass->hInstance = hInstance;
    pWndClass->style = NULL;
    pWndClass->lpfnWndProc = ShowFontWndProc;

    bSuccess = RegisterClass((LPWNDCLASS) pWndClass);

    LocalUnlock(hMemory);
    LocalFree(hMemory);
    return (bSuccess);
}

/****************************************************************************

    FUNCTION: SetMyDC(HDC)

    PURPOSE: Initializes the DC

****************************************************************************/

HDC SetMyDC(hDC)
HDC hDC;
{
    SetBkMode(hDC, nBkMode);
    SetBkColor(hDC, rgbBkColor);
    SetTextColor(hDC, rgbTextColor);
    SetTextAlign(hDC, nAlignLCR | nAlignTBB);
}

/****************************************************************************

    FUNCTION: GetStringExtent(HDC, PSTR, HFONT)

    PURPOSE: get the string extent

****************************************************************************/

short GetStringExtent(hDC, pString, hFont)
HDC hDC;
PSTR pString;
HFONT hFont;
{
    HFONT hOldFont;
    DWORD dwExtent;

    if (hOldFont=SelectObject(hDC, hFont)) {
        dwExtent = GetTextExtent(hDC, pString, strlen(pString));
        SelectObject(hDC, hOldFont);
        return (LOWORD(dwExtent));
    }
    else
        return (0);
}

/****************************************************************************

    FUNCTION: GetStringExtent(HDC, PSTR, HFONT)

    PURPOSE: Sends string to application's window

****************************************************************************/

short StringOut(hDC, X, Y, pString, hFont)
HDC hDC;
short X;
short Y;
PSTR pString;
HFONT hFont;
{
    HFONT hOldFont;
    DWORD dwExtent;

    hOldFont = SelectObject(hDC, hFont);
    if (hOldFont != NULL) {
        dwExtent = GetTextExtent(hDC, pString, strlen(pString));
        TextOut(hDC, X, Y, pString, strlen(pString));
        SelectObject(hDC, hOldFont);
    }
    return (LOWORD(dwExtent));
}

/****************************************************************************

    FUNCTION: ShowString(HWND)

    PURPOSE: Show string in current font

****************************************************************************/

void ShowString(hWnd)
HWND hWnd;
{
    HFONT hItalicFont;
    HFONT hBoldFont;
    HFONT hUnderlineFont;
    HFONT hStrikeOutFont;
    HDC hDC;
    short X, tmpX;
    short Y;
    short nAlign;

    GetObject(hFont, sizeof(LOGFONT), (LPSTR) &LogFont);
    LogFont.lfItalic = TRUE;
    hItalicFont = CreateFontIndirect(&LogFont);
    LogFont.lfItalic = FALSE;
    LogFont.lfUnderline = TRUE;
    hUnderlineFont = CreateFontIndirect(&LogFont);
    LogFont.lfUnderline = FALSE;
    LogFont.lfStrikeOut = TRUE;
    hStrikeOutFont = CreateFontIndirect(&LogFont);
    LogFont.lfStrikeOut = FALSE;
    LogFont.lfWeight = FW_BOLD;
    hBoldFont = CreateFontIndirect(&LogFont);

    hDC=GetDC(hWnd);
    SetMyDC(hDC);
    X=ptCurrent.x;
    Y=ptCurrent.y;
    nAlign =  nAlignLCR | nAlignTBB;                   /* GetTextAlign(hDC);
    if ((nAlign & TA_CENTER) == TA_CENTER) {
        tmpX = X;
        nAlignLCR = TA_LEFT;
        SetTextAlign(hDC, nAlignLCR | nAlignTBB);
        X += StringOut(hDC, X, Y, ", and ", hFont);
        X += StringOut(hDC, X, Y, "strikeout", hStrikeOutFont);
        X += StringOut(hDC, X, Y, " in a single line.", hFont);
        X = tmpX;
        nAlignLCR = TA_RIGHT;
        SetTextAlign(hDC, nAlignLCR | nAlignTBB);
        X -= StringOut(hDC, X, Y, "underline", hUnderlineFont);
        X -= StringOut(hDC, X, Y, ", ", hFont);
        X -= StringOut(hDC, X, Y, "italic", hItalicFont);
        X -= StringOut(hDC, X, Y, ", ", hFont);
        X -= StringOut(hDC, X, Y, "bold", hBoldFont);
        X -= StringOut(hDC, X, Y, "You can use ", hFont);
        nAlignLCR = TA_CENTER;
    }
    else if ((nAlign & TA_CENTER) == TA_RIGHT) {
        X -= StringOut(hDC, X, Y, " in a single line.", hFont);
        X -= StringOut(hDC, X, Y, "strikeout", hStrikeOutFont);
        X -= StringOut(hDC, X, Y, ", and ", hFont);
        X -= StringOut(hDC, X, Y, "underline", hUnderlineFont);
        X -= StringOut(hDC, X, Y, ", ", hFont);
        X -= StringOut(hDC, X, Y, "italic", hItalicFont);
        X -= StringOut(hDC, X, Y, ", ", hFont);
        X -= StringOut(hDC, X, Y, "bold", hBoldFont);
        X -= StringOut(hDC, X, Y, "You can use ", hFont);
    }
    else  {
        X += StringOut(hDC, X, Y, "You can use ", hFont);
        X += StringOut(hDC, X, Y, "bold", hBoldFont);
        X += StringOut(hDC, X, Y, ", ", hFont);
        X += StringOut(hDC, X, Y, "italic", hItalicFont);
        X += StringOut(hDC, X, Y, ", ", hFont);
        X += StringOut(hDC, X, Y, "underline", hUnderlineFont);
        X += StringOut(hDC, X, Y, ", and ", hFont);
        X += StringOut(hDC, X, Y, "strikeout", hStrikeOutFont);
        X += StringOut(hDC, X, Y, " in a single line.", hFont);
    }
    ReleaseDC(hWnd, hDC);

    DeleteObject(hItalicFont);
    DeleteObject(hUnderlineFont);
    DeleteObject(hStrikeOutFont);
    DeleteObject(hBoldFont);
}

/****************************************************************************

    FUNCTION: ShowCharacterSet(HDC, HFONT)

    PURPOSE: display character set using current font

****************************************************************************/

void ShowCharacterSet(hDC, hFont)
HDC hDC;
HFONT hFont;
{
    HFONT hOldFont;
    TEXTMETRIC TextMetric;
    int LineSpace;
    short X;
    short Y;

    if (!(hOldFont = SelectObject(hDC, hFont)))
        return;
    GetTextMetrics(hDC, &TextMetric);
    nLineSpace = (TextMetric.tmHeight + TextMetric.tmExternalLeading)*2;
    X = ptCurrent.x;
    Y = ptCurrent.y;
    TextOut(hDC, X, Y, line[0], 64);
    TextOut(hDC, X, Y += nLineSpace, line[1], 64);
    TextOut(hDC, X, Y += nLineSpace, line[2], 64);
    TextOut(hDC, X, Y += nLineSpace, line[3], 64);
    SelectObject(hDC, hOldFont);
}

/****************************************************************************

    FUNCTION: ShowLogFont(HWND, HFONT)

    PURPOSE: Create dialog box to show information about logical font

****************************************************************************/

void ShowLogFont(hWnd, hFont)
HWND hWnd;
HFONT hFont;
{
    HFONT hOldFont;
    FARPROC lpProcLog;
    HDC hDC;
    TEXTMETRIC TextMetric;
    HANDLE hDlgBox;
    char buf[80];
    char DialogTitle[100];

    hDC = GetDC(hWnd);
    if (!(hOldFont = SelectObject(hDC, hSFont)))
        return;
    GetTextMetrics(hDC, &TextMetric);
    nLineSpace = TextMetric.tmHeight + TextMetric.tmExternalLeading;
    GetObject(hFont, sizeof(LOGFONT), (LPSTR) &LogFont);

    lpProcLog = MakeProcInstance((FARPROC) Log, hInst);
    hDlgBox = CreateDialog(hInst, "LogBox", hWnd, lpProcLog);

    strcpy(DialogTitle, "Log Font: ");
    strcat(DialogTitle, LogFont.lfFaceName);
    SetWindowText(hDlgBox, (LPSTR) DialogTitle);

    SelectObject(hDC, hOldFont);
    ReleaseDC(hWnd, hDC);
}

/****************************************************************************

    FUNCTION: ShowMetricFont(HWND, HFONT)

    PURPOSE: Create dialog box to show information about metric font

****************************************************************************/

void ShowTextMetric(hWnd, hFont)
HWND hWnd;
HFONT hFont;
{
    FARPROC lpProcMetric;
    HFONT hOldFont;
    TEXTMETRIC LocalTextMetric;
    HDC hDC;
    HANDLE hDlgBox;
    char buf[80];
    char DialogTitle[100];

    hDC = GetDC(hWnd);
    if (!(hOldFont = SelectObject(hDC, hFont)))
        return;
    GetTextMetrics(hDC, &TextMetric);

    lpProcMetric = MakeProcInstance((FARPROC) Metric, hInst);
    hDlgBox = CreateDialog(hInst, "MetricBox", hWnd, lpProcMetric);

    strcpy(DialogTitle, "Metric Font: ");
    GetTextFace(hDC, 80, buf);
    strcat(DialogTitle, buf);
    SetWindowText(hDlgBox, (LPSTR) DialogTitle);

    SelectObject(hDC, hOldFont);
    ReleaseDC(hWnd, hDC);
}

/****************************************************************************

    FUNCTION: Colors(HWND, unsigned, WORD LONG)

    PURPOSE: Dialog box for changing background color of text

****************************************************************************/

BOOL FAR PASCAL Colors(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    int Red, Green, Blue;

    switch (message) {
        case WM_INITDIALOG:
            SetDlgItemInt(hDlg, ID_RED, GetRValue(rgbColor), FALSE);
            SetDlgItemInt(hDlg, ID_GREEN, GetGValue(rgbColor), FALSE);
            SetDlgItemInt(hDlg, ID_BLUE, GetBValue(rgbColor), FALSE);
            return (TRUE);
            break;

        case WM_COMMAND:
            switch (wParam) {
                case IDOK:
                    Red = GetDlgItemInt(hDlg, ID_RED, NULL, FALSE);
                    Green = GetDlgItemInt(hDlg, ID_GREEN, NULL, FALSE);
                    Blue = GetDlgItemInt(hDlg, ID_BLUE, NULL, FALSE);
                    rgbColor = RGB(Red, Green, Blue);
                    EndDialog(hDlg, 1);
                    break;

                case IDCANCEL:
                    EndDialog(hDlg, 0);
                    break;
            }
            break;
    }
    return (FALSE);
}

/****************************************************************************

    FUNCTION: EnumFunc(LPLOGFONT, LPTEXTMETRIC, short, LPSTR)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

int FAR PASCAL EnumFunc(lpLogFont, lpTextMetric, FontType, lpData)
LPLOGFONT lpLogFont;
LPTEXTMETRIC lpTextMetric;
short FontType;
LPSTR lpData;
{
    switch (LOWORD(lpData)) {
        case 0:
            if (FontIndex >= MAXFONT)
                return (0);
            _lstrcpy((LPSTR) FontList[FontIndex],
                (LPSTR) (lpLogFont->lfFaceName));
            CharSet[FontIndex] = lpLogFont->lfCharSet;
            PitchAndFamily[FontIndex] = lpLogFont->lfPitchAndFamily;
            return (++FontIndex);

        case 1:
            if (SizeIndex >= MAXSIZE)
                return (0);
            SizeList[SizeIndex] = lpLogFont->lfHeight;
            return (++SizeIndex);
    }
}

/****************************************************************************

    FUNCTION: GetFonts(HWND)

    PURPOSE: Get available fonts

****************************************************************************/

void GetFonts(hWnd)
HWND hWnd;
{

    HDC hDC;

    FontIndex = 0;
    SizeIndex = 0;
    hDC = GetDC(hWnd);
    lpEnumFunc = MakeProcInstance(EnumFunc, hInst);
    EnumFonts(hDC, (LPSTR) NULL, lpEnumFunc, (LPSTR) NULL);
    FreeProcInstance(lpEnumFunc);
    ReleaseDC(hWnd, hDC);
}

/****************************************************************************

    FUNCTION: GetSizes(hWnd, CurrentFont)

    PURPOSE: Get size of current font

****************************************************************************/

void GetSizes(hWnd, CurrentFont)
HWND hWnd;
int CurrentFont;
{
    HDC hDC;

    SizeIndex = 0;
    hDC = GetDC(hWnd);
    lpEnumFunc = MakeProcInstance(EnumFunc, hInst);
    EnumFonts(hDC, FontList[CurrentFont], lpEnumFunc, (LPSTR) 1L);
    FreeProcInstance(lpEnumFunc);
    ReleaseDC(hWnd, hDC);
}


/****************************************************************************

    FUNCTION: SelectFont(HWND, unsigned, WORD, LONG)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL FAR PASCAL SelectFont(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{

    int i;
    int index;
    char buf[LF_FACESIZE];

    switch (message) {
        case WM_INITDIALOG:
            for (i=0; i<FontIndex; i++) {        /* displays available fonts
                SendDlgItemMessage(hDlg, ID_TYPEFACE, LB_ADDSTRING,
                    NULL, (LONG) (LPSTR) FontList[i]);
                SendDlgItemMessage(hDlg, ID_TYPEFACE, LB_SETCURSEL,
                    0, 0L);
            }
            GetSizes(hDlg, 0);
            for (i=0; i<SizeIndex; i++) {        /* displays font sizes
                sprintf(buf, "%d", SizeList[i]);
                SendDlgItemMessage(hDlg, ID_SIZE, LB_ADDSTRING,
                    0, (LONG) (LPSTR) buf);
                SendDlgItemMessage(hDlg, ID_SIZE, LB_SETCURSEL,
                    0, 0L);
            }
            return (TRUE);
            break;

        case WM_COMMAND:
            switch (wParam) {
                case IDOK:
okay:
                    index=SendDlgItemMessage(hDlg, ID_TYPEFACE,
                        LB_GETCURSEL, 0, 0L);
                    if (index == LB_ERR) {
                        MessageBox(hDlg, "No font selected",
                            "Select Font", MB_OK | MB_ICONEXCLAMATION);
                    break;
            }
            CurrentFont = index;
            index = SendDlgItemMessage(hDlg, ID_SIZE,
                LB_GETCURSEL, 0, 0L);
            if (index == LB_ERR) {
                MessageBox(hDlg, "No size selected",
                    "Select Font", MB_OK | MB_ICONEXCLAMATION);
                break;
            }
            CurrentSize = index;
            EndDialog(hDlg, 1);
            break;

        case IDCANCEL:
            EndDialog(hDlg, 0);
            break;

        case ID_TYPEFACE:
            switch (HIWORD(lParam)) {
                case LBN_SELCHANGE:
                    index = SendDlgItemMessage(hDlg, ID_TYPEFACE,
                        LB_GETCURSEL, 0, 0L);
                    if (index == LB_ERR)
                        break;
                    SendDlgItemMessage(hDlg, ID_SIZE, LB_RESETCONTENT, 0, 0L)
                    GetSizes(hDlg, index);
                    for (i = 0; i < SizeIndex; i++) {
                        sprintf(buf, "%d", SizeList[i]);
                        SendDlgItemMessage(hDlg, ID_SIZE,
                            LB_ADDSTRING, 0, (LONG) (LPSTR) buf);
                        SendDlgItemMessage(hDlg, ID_SIZE, LB_SETCURSEL, 0, 0L
            }
            break;

                case LBN_DBLCLK:
                goto okay;
                break;
            }
            break;

        case ID_SIZE:
            if(HIWORD(lParam) == LBN_DBLCLK)
                goto okay;
            break;
        }
        break;
    }
    return (FALSE);
}

/****************************************************************************

    FUNCTION: ShowFontWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE: Processes messages

****************************************************************************/

long FAR PASCAL ShowFontWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC lpProcAbout, lpAddDlg, lpRemoveDlg;
    HDC hDC;
    PAINTSTRUCT ps;
    HFONT hOldFont;
    int i;
    short Y;
    char buf[80];

    switch(message) {
        case WM_CREATE:
            GetFonts(hWnd);
            hMFont = CreateFont(
                10,                                      /* height
                10,                                      /* width
                0,                                       /* escapement
                0,                                       /* orientation
                FW_NORMAL,                               /* weight
                FALSE,                                   /* italic
                FALSE,                                   /* underline
                FALSE,                                   /* strikeout
                OEM_CHARSET,                             /* charset
                OUT_DEFAULT_PRECIS,                      /* out precision
                CLIP_DEFAULT_PRECIS,                     /* clip precision
                DEFAULT_QUALITY,                         /* quality
                FIXED_PITCH | FF_MODERN,                 /* pitch and family
                "Courier");                              /* typeface
            hOFont = GetStockObject(OEM_FIXED_FONT);
            hFFont = GetStockObject(ANSI_FIXED_FONT);
            hVFont = GetStockObject(ANSI_VAR_FONT);
            hSFont = GetStockObject(SYSTEM_FONT);
            hDFont = GetStockObject(DEVICE_DEFAULT_FONT);
            hFont = hSFont;
            GetObject(hFont, sizeof(LOGFONT), (LPSTR) &LogFont);
            strcpy(WindowTitle, AppName);
            strcat(WindowTitle, "SYSTEM");
            SetWindowText(hWnd, (LPSTR) WindowTitle);

            for (i=0; i<64; i++) {
                line[0][i] = i;
                line[1][i] = i+64;
                line[2][i] = i+128;
                line[3][i] = i+192;
            }
            break;

        case WM_PAINT:
            hDC = BeginPaint(hWnd, &ps);
            SetMyDC(hDC);
            switch (wPaint) {
                case IDM_SHOWCHARSET:
                ShowCharacterSet(hDC, hFont);
                break;
            }
            EndPaint(hWnd, &ps);
            break;

        case WM_COMMAND:
            switch (wParam) {

                /* File menu */

                case IDM_ADDFONT:

                    /* Call AddDlg() to get the filename */

                    lpAddDlg = MakeProcInstance((FARPROC) AddDlg, hInst);
                    if (DialogBox(hInst, "Add", hWnd, lpAddDlg)) {

                        /* Check to see if it is a new font name */

                        for (i = 0; i < nFontIndex; i++) {
                            if (!strcmp(FontFileName, &FontNameList[i][0])) {
                                MessageBox(hWnd, "Font already exists",
                                    "Add Font", MB_OK | MB_ICONQUESTION);
                                FreeProcInstance(lpAddDlg);
                                return (0L);
                            }
                        }

                        /* Tell Windows to add the font resource */

                        AddFontResource((LPSTR) FontFileName);

                        /* Let all applications know there is a new font
                         * resource
                         */

                        SendMessage((HWND) 0xFFFF, WM_FONTCHANGE, NULL,
                            (LONG) NULL);

                        /* Copy the name selected to the list of fonts added

                        strcpy(&FontNameList[nFontIndex++][0], FontFileName);
                    }

                    FreeProcInstance(lpAddDlg);
                    break;

                case IDM_DELFONT:
                    if (!nFontIndex) {
                        MessageBox(hWnd, "No fonts to delete",
                            "Remove Font", MB_OK | MB_ICONQUESTION);
                        break;
                    }

                    lpRemoveDlg = MakeProcInstance((FARPROC) RemoveDlg, hInst
                    if (DialogBox(hInst, "Remove", hWnd, lpRemoveDlg)) {
                        for (i = 0; i < nFontIndex; i++) {
                            if (!strcmp(FontFileName, &FontNameList[i][0])) {
                                RemoveFontResource((LPSTR) FontFileName);
                                SendMessage((HWND) 0xFFFF, WM_FONTCHANGE, NUL
                                    (LONG) NULL);
                                strcpy(&FontNameList[i][0],
                                    &FontNameList[--nFontIndex][0]);
                                break;
                            }
                        }
                    }
                    FreeProcInstance(lpRemoveDlg);
                    break;

                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;

                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance((FARPROC) About, hInst);
                    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
                    FreeProcInstance(lpProcAbout);
                    break;

                /* Show menu */

                case IDM_SHOWSTRING:
                    ShowString(hWnd);
                    break;

                case IDM_SHOWCHARSET:
                    InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
                    wPaint = wParam;
                    break;

                case IDM_SHOWLOGFONT:
                    ShowLogFont(hWnd, hFont);
                    break;

                case IDM_SHOWTEXTMETRICS:
                    ShowTextMetric(hWnd, hFont);
                    break;

                case IDM_CLEAR:
                    InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
                    wPaint = 0;
                    break;

                /* Font menu */

                case IDM_OEM:
                    hFont = hOFont;
                    SetFaceName(hWnd);                  /* sets window title
                    CheckMenuItem(GetMenu(hWnd), wPrevFont, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevFont = wParam;
                    break;

                case IDM_ANSIFIXED:
                    hFont = hFFont;
                    SetFaceName(hWnd);
                    CheckMenuItem(GetMenu(hWnd), wPrevFont, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevFont = wParam;
                    break;

                case IDM_ANSIVAR:
                    hFont = hVFont;
                    SetFaceName(hWnd);
                    CheckMenuItem(GetMenu(hWnd), wPrevFont, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevFont = wParam;
                    break;

                case IDM_SYSTEM:
                    hFont = hSFont;
                    SetFaceName(hWnd);
                    CheckMenuItem(GetMenu(hWnd), wPrevFont, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevFont = wParam;
                    break;

                case IDM_DEVICEDEF:
                    hFont = hDFont;
                    SetFaceName(hWnd);
                    CheckMenuItem(GetMenu(hWnd), wPrevFont, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevFont = wParam;
                    break;

                case IDM_SELECTFONT:
                    lpSelectFont = MakeProcInstance(SelectFont, hInst);
                    if (DialogBox(hInst, "SelectFont", hWnd, lpSelectFont)) {
                        DeleteObject(hMFont);
                        hMFont = CreateFont(
                            SizeList[CurrentSize],
                            0,
                            0,
                            0,
                            FW_NORMAL,
                            FALSE,
                            FALSE,
                            FALSE,
                            CharSet[CurrentFont],
                            OUT_DEFAULT_PRECIS,
                            CLIP_DEFAULT_PRECIS,
                            DEFAULT_QUALITY,
                            PitchAndFamily[CurrentFont],
                            FontList[CurrentFont]);
                        hFont = hMFont;
                        SetFaceName(hWnd);
                    }
                    FreeProcInstance(lpSelectFont);
                    break;

                case IDM_CFONT:
                    lpCFontDlg = MakeProcInstance(CFontDlg, hInst);
                    GetObject(hMFont, sizeof(LOGFONT), (LPSTR) &CLogFont);
                    if (DialogBox(hInst, "CFont", hWnd, lpCFontDlg)) {
                        DeleteObject(hMFont);
                        hMFont = CreateFontIndirect(&CLogFont);
                        hFont = hMFont;
                        SetFaceName(hWnd);
                    }
                    FreeProcInstance(lpCFontDlg);
                    break;

                /* Options menu */

                case IDM_TEXTCOLOR:
                    lpColors = MakeProcInstance(Colors, hInst);
                    rgbColor = rgbTextColor;
                    if (DialogBox(hInst, "Colors", hWnd, lpColors))
                        rgbTextColor = rgbColor;
                    FreeProcInstance(lpColors);
                    break;

                case IDM_BACKGROUNDCOLOR:
                    lpColors = MakeProcInstance(Colors, hInst);
                    rgbColor = rgbBkColor;
                    if (DialogBox(hInst, "Colors", hWnd, lpColors))
                        rgbBkColor = rgbColor;
                    FreeProcInstance(lpColors);
                    break;

                case IDM_OPAQUE:
                    nBkMode = OPAQUE;
                    CheckMenuItem(GetMenu(hWnd), IDM_TRANSPARENT, MF_UNCHECKE
                    CheckMenuItem(GetMenu(hWnd), IDM_OPAQUE, MF_CHECKED);
                    break;

                case IDM_TRANSPARENT:
                    nBkMode = TRANSPARENT;
                    CheckMenuItem(GetMenu(hWnd), IDM_OPAQUE,  MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), IDM_TRANSPARENT,  MF_CHECKED
                    break;

                case IDM_ALIGNLEFT:
                    nAlignLCR = TA_LEFT;
                    CheckMenuItem(GetMenu(hWnd), wPrevHAlign, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevHAlign = wParam;
                    break;

                case IDM_ALIGNCENTER:
                    nAlignLCR = TA_CENTER;
                    CheckMenuItem(GetMenu(hWnd), wPrevHAlign, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevHAlign = wParam;
                    break;

                case IDM_ALIGNRIGHT:
                    nAlignLCR = TA_RIGHT;
                    CheckMenuItem(GetMenu(hWnd), wPrevHAlign, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevHAlign = wParam;
                    break;

                case IDM_ALIGNTOP:
                    nAlignTBB = TA_TOP;
                    CheckMenuItem(GetMenu(hWnd), wPrevVAlign, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevVAlign = wParam;
                    break;

                case IDM_ALIGNBASE:
                    nAlignTBB = TA_BASELINE;
                    CheckMenuItem(GetMenu(hWnd), wPrevVAlign, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevVAlign = wParam;
                    break;

                case IDM_ALIGNBOTTOM:
                    nAlignTBB = TA_BOTTOM;
                    CheckMenuItem(GetMenu(hWnd), wPrevVAlign, MF_UNCHECKED);
                    CheckMenuItem(GetMenu(hWnd), wParam, MF_CHECKED);
                    wPrevVAlign = wParam;
                    break;
            }
            break;

        case WM_LBUTTONUP:
            ptCurrent.x = LOWORD(lParam);
            ptCurrent.y = HIWORD(lParam);
            ShowString(hWnd);
            break;

        case WM_FONTCHANGE:
            GetFonts(hWnd);
            break;

        case WM_DESTROY:

            /* Remove any fonts that were added */

            for (i = 0; i < nFontIndex; i++)
                RemoveFontResource((LPSTR) &FontNameList[i][0]);

            /* Notify any other applications know the fonts have been deleted

            SendMessage((HWND) 0xFFFF, WM_FONTCHANGE, NULL, (LONG) NULL);
            PostQuitMessage(0);
            break;

        default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (0L);
}

/****************************************************************************

    FUNCTION: Metric(HWND, unsigned, WORD, LONG)

    PURPOSE: Modeless dialog box to display metric font information

****************************************************************************/

BOOL FAR PASCAL Metric(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
        case WM_INITDIALOG:
            SetDlgItemInt(hDlg, IDMB_HEIGHT, TextMetric.tmHeight, FALSE);
            SetDlgItemInt(hDlg, IDMB_ASCENT, TextMetric.tmAscent, FALSE);
            SetDlgItemInt(hDlg, IDMB_DESCENT, TextMetric.tmDescent, FALSE);
            SetDlgItemInt(hDlg, IDMB_INTERNALLEADING,
                TextMetric.tmInternalLeading, FALSE);
            SetDlgItemInt(hDlg, IDMB_EXTERNALLEADING,
                TextMetric.tmExternalLeading, FALSE);
            SetDlgItemInt(hDlg, IDMB_AVECHARWIDTH, TextMetric.tmAveCharWidth,
                FALSE);
            SetDlgItemInt(hDlg, IDMB_MAXCHARWIDTH, TextMetric.tmMaxCharWidth,
                FALSE);
            SetDlgItemInt(hDlg, IDMB_WEIGHT, TextMetric.tmWeight, FALSE);
            SetDlgItemInt(hDlg, IDMB_ITALIC, TextMetric.tmItalic, FALSE);
            SetDlgItemInt(hDlg, IDMB_UNDERLINED, TextMetric.tmUnderlined,
                FALSE);
            SetDlgItemInt(hDlg, IDMB_STRUCKOUT, TextMetric.tmStruckOut, FALSE
            SetDlgItemInt(hDlg, IDMB_FIRSTCHAR, TextMetric.tmFirstChar, FALSE
            SetDlgItemInt(hDlg, IDMB_LASTCHAR, TextMetric.tmLastChar, FALSE);
            SetDlgItemInt(hDlg, IDMB_DEFAULTCHAR, TextMetric.tmDefaultChar,
                FALSE);
            SetDlgItemInt(hDlg, IDMB_BREAKCHAR, TextMetric.tmBreakChar, FALSE
            SetDlgItemInt(hDlg, IDMB_PITCHANDFAMILY,
                TextMetric.tmPitchAndFamily, FALSE);
            SetDlgItemInt(hDlg, IDMB_CHARSET, TextMetric.tmCharSet, FALSE);
            SetDlgItemInt(hDlg, IDMB_OVERHANG, TextMetric.tmOverhang, FALSE);
            SetDlgItemInt(hDlg, IDMB_DIGITIZEDASPECTX,
                TextMetric.tmDigitizedAspectX, FALSE);
            SetDlgItemInt(hDlg, IDMB_DIGITIZEDASPECTY,
    TextMetric.tmDigitizedAspectY, FALSE);
            return (TRUE);

        case WM_CLOSE:
            DestroyWindow(hDlg);
            break;
    }
    return (FALSE);
}

/****************************************************************************

    FUNCTION: Log(HWND, unsigned, WORD, LONG)

    PURPOSE: Displays logical font information

****************************************************************************/

BOOL FAR PASCAL Log(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{

    switch (message) {
        case WM_INITDIALOG:
            SetDlgItemInt(hDlg, IDMI_HEIGHT, LogFont.lfHeight, FALSE);
            SetDlgItemInt(hDlg, IDMI_WIDTH, LogFont.lfWidth, FALSE);
            SetDlgItemInt(hDlg, IDMI_ESCAPEMENT, LogFont.lfEscapement, FALSE)
            SetDlgItemInt(hDlg, IDMI_ORIENTATION, LogFont.lfOrientation, FALS
            SetDlgItemInt(hDlg, IDMI_WEIGHT, LogFont.lfWeight, FALSE);
            SetDlgItemInt(hDlg, IDMI_ITALIC, LogFont.lfItalic, FALSE);
            SetDlgItemInt(hDlg, IDMI_UNDERLINED, LogFont.lfUnderline, FALSE);
            SetDlgItemInt(hDlg, IDMI_STRIKEOUT, LogFont.lfStrikeOut, FALSE);
            SetDlgItemInt(hDlg, IDMI_CHARSET, LogFont.lfCharSet, FALSE);
            SetDlgItemInt(hDlg, IDMI_OUTPRECISION, LogFont.lfOutPrecision,
                FALSE);
            SetDlgItemInt(hDlg, IDMI_CLIPPRECISION, LogFont.lfClipPrecision,
                FALSE);
            SetDlgItemInt(hDlg, IDMI_QUALITY, LogFont.lfQuality, FALSE);
            SetDlgItemInt(hDlg, IDMI_PITCHANDFAMILY,
                LogFont.lfPitchAndFamily, FALSE);
            return (TRUE);

        case WM_CLOSE:
            DestroyWindow(hDlg);
            break;

    }
    return (FALSE);
}

/****************************************************************************

    FUNCTION: AddDlg(HWND, unsigned, WORD, LONG)

    PURPOSE: Used to add a font

    COMMENTS:

        This dialog box displays all the availble font files on the currently
        selected directory, and lets the user select a font to add.

****************************************************************************/

BOOL FAR PASCAL AddDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
        case WM_COMMAND:
            switch (wParam) {
                case ID_LISTBOX:

                    switch (HIWORD(lParam)) {
                        case LBN_SELCHANGE:
                            /* If item is a directory name, append "*.*" */
                            if (DlgDirSelect(hDlg, str, ID_LISTBOX))
                                strcat(str, DefSpec);

                            SetDlgItemText(hDlg, ID_EDIT, str);
                            SendDlgItemMessage(hDlg,
                                ID_EDIT,
                                EM_SETSEL,
                                NULL,
                                MAKELONG(0, 0x7fff));
                            break;

                        case LBN_DBLCLK:
                            goto CheckSelection;
                            break;

                    }                              /* end of ID_LISTBOX case
                    return (TRUE);

                case IDOK:
CheckSelection:
                    /* Get the filename from the edit control */

                    GetDlgItemText(hDlg, ID_EDIT, str, 128);
                    GetDlgItemText(hDlg, ID_PATH, DefPath, 128);

                    /* Check for wildcard.  If found, use the string as a new
                     * search path.
                     */

                    if (strchr(str, '*') ||
                        strchr(str, '?')) {

                    /* Separate filename from path.  The path is stored in
                     * str which is discarded if null, else it is used for a
                     * search path.
                     */

                        SeparateFile(hDlg, (LPSTR) str, (LPSTR) DefSpec,
                            (LPSTR) str);
                        if (str[0])
                            strcpy(DefPath, str);

                        UpdateListBox(hDlg);
                        return (TRUE);
                    }

                    /* Ignore it if no filename specified */

                    if (!str[0]) {
                        MessageBox(hDlg, "No filename specified.",
                            NULL, MB_OK | MB_ICONQUESTION);
                        return (TRUE);
                    }

                    /* Append the default extension if needed */

                    strcpy(FontFileName, DefPath);
                    strcat(FontFileName, str);
                    AddExt(FontFileName, DefExt);
                    EndDialog(hDlg, TRUE);
                    return (TRUE);

                case IDCANCEL:

                    /* Let the caller know the user cancelled */

                    EndDialog(hDlg, FALSE);
                    return (TRUE);
            }
            break;

        case WM_INITDIALOG:
            SetWindowText(hDlg, (LPSTR) "Add Font Resource");
            strcpy(DefSpec, "*.fon");
            UpdateListBox(hDlg);
            SetDlgItemText(hDlg, ID_EDIT, DefSpec);
            SendDlgItemMessage(hDlg, ID_EDIT, EM_SETSEL, NULL,
                MAKELONG(0, 0x7fff));
            SetFocus(GetDlgItem(hDlg, ID_EDIT));
            return (FALSE);
    }
    return FALSE;
}

/****************************************************************************

    FUNCTION: RemoveDlg(HANDLE)

    PURPOSE: Used to remove a font

    COMMENTS:

        This dialog box displays all fonts which have been added to the syste
        and lets the user select which font to delete.

****************************************************************************/

BOOL FAR PASCAL RemoveDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    WORD index;
    int i;

    switch (message) {
        case WM_COMMAND:

            switch (wParam) {
                case ID_LISTBOX:

                    switch (HIWORD(lParam)) {
                        case LBN_SELCHANGE:
                            index = SendDlgItemMessage(hDlg,
                                ID_LISTBOX,
                                LB_GETCURSEL,         /* get index command
                                NULL,
                                (LONG) NULL);
                            SendDlgItemMessage(hDlg,
                                ID_LISTBOX,
                                LB_GETTEXT,           /* copy string command
                                index,
                                (LONG) (LPSTR) FontFileName);
                            SetDlgItemText(hDlg, ID_EDIT, FontFileName);
                            break;

                        case LBN_DBLCLK:
                            GetDlgItemText(hDlg, ID_EDIT, FontFileName, 128);
                            EndDialog(hDlg, TRUE);
                            return (TRUE);
                    }
                    return (TRUE);

                case IDOK:

                    /* Get the filename from the edit control */

                    GetDlgItemText(hDlg, ID_EDIT, FontFileName, 128);

                    /* Ignore it if no filename specified */

                    if (!FontFileName[0]) {
                        MessageBox(hDlg, "No filename specified.",
                            NULL, MB_OK | MB_ICONQUESTION);
                        return (TRUE);
                    }

                    EndDialog(hDlg, TRUE);
                    return (TRUE);

                case IDCANCEL:
                    EndDialog(hDlg, FALSE);
                    return (TRUE);
            }
            break;

        case WM_INITDIALOG:
            SetWindowText(hDlg, (LPSTR) "Remove Font Resource");
            for (i = 0; i < nFontIndex; i++)
                SendDlgItemMessage(hDlg,
                    ID_LISTBOX,
                    LB_ADDSTRING,
                    NULL,
                    (LONG)(char far *) &FontNameList[i][0]);

            SetFocus(GetDlgItem(hDlg, ID_EDIT));
            return (FALSE);
    }
    return FALSE;
}

/****************************************************************************

    FUNCTION: UpdateListBox(HWND);

    PURPOSE: Update the list box of OpenDlg

****************************************************************************/

void UpdateListBox(hDlg)
HWND hDlg;
{
    strcpy(str, DefPath);
    strcat(str, DefSpec);
    DlgDirList(hDlg, str, ID_LISTBOX, ID_PATH, 0x4010);
    SetDlgItemText(hDlg, ID_EDIT, DefSpec);
}

/****************************************************************************

    FUNCTION: AddExt(PSTR, PSTR);

    PURPOSE: Add default extension

/***************************************************************************/

void AddExt(Name, Ext)
PSTR Name, Ext;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr != '.')
        strcat(Name, Ext);
}

/****************************************************************************

    FUNCTION: SeparateFile(HWND, LPSTR, LPSTR, LPSTR)

    PURPOSE: Separate filename and pathname

    COMMENTS:

        This function takes a source filespec and splits it into a path and a
        filename, and copies these into the strings specified.  Because it
        uses the AnsiPrev call, it will work in any language.

****************************************************************************/

void SeparateFile(hDlg, lpDestPath, lpDestFileName, lpSrcFileName)
HWND hDlg;
LPSTR lpDestPath, lpDestFileName, lpSrcFileName;
{
    LPSTR lpTmp;

    lpTmp = lpSrcFileName + (long) _lstrlen(lpSrcFileName);
    while (*lpTmp != ':' && *lpTmp != '\\' && lpTmp > lpSrcFileName)
        lpTmp = AnsiPrev(lpSrcFileName, lpTmp);
    if (*lpTmp != ':' && *lpTmp != '\\') {                  /* no path given
        _lstrcpy(lpDestFileName, lpSrcFileName);
        lpDestPath[0] = 0;
        return;
    }
    _lstrcpy(lpDestFileName, lpTmp + 1L);
    _lstrncpy(lpDestPath, lpSrcFileName, (int) (lpTmp - lpSrcFileName) + 1);
    lpDestPath[(lpTmp - lpSrcFileName) + 1] = 0;
}

/****************************************************************************

    FUNCTION: _lstrlen(LPSTR)

    PURPOSE:  uses a long far pointer to the string, returns the length

****************************************************************************/

int _lstrlen(lpStr)
LPSTR lpStr;
{
    int i;
    for (i=0; *lpStr++; i++);
    return (i);
}

/****************************************************************************

    FUNCTION: _lstrncpy(LPSTR, LPSTR)

    PURPOSE:  FAR version of strncpy()

****************************************************************************/

void _lstrncpy(lpDest, lpSrc, n)
LPSTR lpDest, lpSrc;
int n;
{
    while (n--)
        if(!(*lpDest++ = *lpSrc++))
            return;
}

/****************************************************************************

    FUNCTION: _lstrcpy(LPSTR, LPSTR)

    PURPOSE:  FAR version of strcpy()

****************************************************************************/

void _lstrcpy(lpDest, lpSrc)
LPSTR lpDest, lpSrc;
{
    while(*lpDest++ = *lpSrc++);
}

/****************************************************************************

    FUNCTION: SetFaceName(HWND)

    PURPOSE: Retireves current font's face name, places it in WindowTitle

****************************************************************************/

SetFaceName(hWnd)
HWND hWnd;
{
    char buf[80];
    HDC hDC;

    hDC = GetDC(hWnd);
    SelectObject(hDC, hFont);
    strcpy(WindowTitle, AppName);
    GetTextFace(hDC, 80, buf);
    strcat(WindowTitle, buf);
    SetWindowText(hWnd, (LPSTR) WindowTitle);

    ReleaseDC(hWnd, hDC);
}

/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
        case WM_INITDIALOG:
            return (TRUE);

        case WM_COMMAND:
            if (wParam == IDOK) {
                EndDialog(hDlg, TRUE);
                return (TRUE);
            }
            return (TRUE);
    }
    return (FALSE);
}


TTY.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\TTY\TTY.C

//

//  FILE:    TTY.c

//  PURPOSE: This sample terminal application demonstrates
//          the basic uses of Windows Communications functions.
//          It also shows the basic structure for a terminal program.

//  FUNCTIONS:
//          WinMain() - Initializes app, calls all other functions.
//          TTYWndProc() - Window procedure for terminal window.
//          About() - Window procedure for About dialog box.
//          AboutInit() - Initialization procedure for About dialog box.
//          SettingDlgProc() - Window procedure for Comm Settings dialog.
//

#include <windows.h>
#include <string.h>
#include "wstdio.h"
#include "tty.h"


//========================================================================\\

// Declarations

//========================================================================\\

#define COM1  "com1"
#define COM2  "com2"
#define CommSettings "com1:96,n,8,1"

#define BufMax      160     // Size of line buffer used for displaying text
#define cbInBuf    1024    // Size of receive buffer
#define cbOutBuf   128     // size of transmit buffer

DCB CommDCB;               // DCB for comm port
int PortID;                // The comm port id
COMSTAT CommStat;          // COMSTAT info buffer for comm port
char MsgBuff[BufMax + 1];  // Buffer to hold incoming characters
BOOL bConnected;           // Flag to indicate if connected
short nCommErr;            // Storage for communications error data
WORD wCommEvt;             // Storage for communications event data

HWND hTTYWnd;              // Handle to application window

char sTemp[256];

static HANDLE hInst;       // Global instance handle
FARPROC lpprocAbout;       // Pointer to "About" dialog box procedure
FARPROC lpfnOldTTYProc;    // Pointer to TTY proc prior to subclassing

long FAR PASCAL TTYWndProc(HWND, unsigned, WORD, LONG);


//========================================================================\\

// FUNCTION: About(HWND, unsigned, WORD, LONG)

// PURPOSE:  Processes messages for About dialog box.

//========================================================================\\

BOOL FAR PASCAL About( hDlg, message, wParam, lParam )
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    if (message == WM_COMMAND
  || message == WM_LBUTTONDOWN) {

  // if we click the mouse in the dialog, or press 'Enter', then go away
        EndDialog( hDlg, TRUE );
        return TRUE;
        }
    else if (message == WM_INITDIALOG)
        return TRUE;
    else return FALSE;
}


//========================================================================\\

// FUNCTION: AboutInit(HWND, HANDLE)

// PURPOSE:  About box initialization.

//========================================================================\\

BOOL AboutInit(HWND hWnd, HANDLE hInstance)
{
    HMENU hMenu;

    /* Bind callback function with module instance */
    lpprocAbout = MakeProcInstance( (FARPROC)About, hInstance );

    return TRUE;
}


//========================================================================\\

// FUNCTION: SettingDlgProc(HWND, unsigned, WORD, LONG)

// PURPOSE:  Processes messages for Communications Settings dialog.

//========================================================================\\

BOOL FAR PASCAL SettingDlgProc( hDlg, message, wParam, lParam )
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
   int theButton;
   static DCB dlgDCB;

   switch(message) {
      case WM_COMMAND:

         // if the Ok button is pressed the new settings are saved

         if(wParam == IDOK) {
            // save the new settings
            CommDCB = dlgDCB;
            // close the dialog
            EndDialog( hDlg, TRUE );

         } else

         // otherwise, the settings are not saved and changes are discarded

            if(wParam == IDCANCEL)
               EndDialog(hDlg,FALSE);
            else
               if(HIWORD(lParam) == BN_CLICKED) {

                  // if a button is clicked and it is a radiobutton,
                  // then we uncheck the current selection and check
                  // the one that was clicked.

                  HWND hStartWnd = GetDlgItem(hDlg,wParam);

                  if(LOWORD(GetWindowLong(hStartWnd,GWL_STYLE))
                     == BS_AUTORADIOBUTTON){
                     HWND hCurrWnd = hStartWnd;
                     do{
                        hCurrWnd = GetNextDlgGroupItem(hDlg,hCurrWnd,1);
                        SendMessage(hCurrWnd, BM_SETCHECK, hCurrWnd == hStart
                        } while(hCurrWnd != hStartWnd);
                  }

      // now we set the appropriate value in the DCB for the
      // button that was clicked

      switch (wParam){
                            case RBBAUD_300: dlgDCB.BaudRate = 300; break;
                            case RBBAUD_1200: dlgDCB.BaudRate = 1200; break;
                            case RBBAUD_2400: dlgDCB.BaudRate = 2400; break;
          case RBBAUD_9600: dlgDCB.BaudRate = 9600; break;

                            case RBDBITS_7: dlgDCB.ByteSize = 7; break;
          case RBDBITS_8: dlgDCB.ByteSize = 8; break;

                            case RBPARITY_EVEN: dlgDCB.Parity = EVENPARITY; b
                            case RBPARITY_ODD: dlgDCB.Parity = ODDPARITY; bre
          case RBPARITY_NONE: dlgDCB.Parity = NOPARITY; break;

                            case RBSBITS_2: dlgDCB.StopBits = TWOSTOPBITS; br
          case RBSBITS_1: dlgDCB.StopBits = ONESTOPBIT; break;

          case CBXONXOFF: dlgDCB.fInX = dlgDCB.fInX?0:1; break;

                            case RBPORT_COM1: dlgDCB.Id = 1; break;
                            case RBPORT_COM2: dlgDCB.Id = 2; break;
                        }
        } else
                       return FALSE;

            break;

  case WM_INITDIALOG:

    // make a copy of the current DCB
    // we will change this copy, and copy back to the original
    // if we click Ok

    dlgDCB = CommDCB;

    // set buttons as reflected by the current DCB

    // if the current port isn't com2, set com1 button
    theButton = (dlgDCB.Id == 2 ? RBPORT_COM2 : RBPORT_COM1);
    SendDlgItemMessage(hDlg,theButton,BM_SETCHECK,1,0L);

    // set baud button
                switch(dlgDCB.BaudRate){
                    case 300: theButton = RBBAUD_300; break;
                    case 1200: theButton = RBBAUD_1200; break;
                    case 2400: theButton = RBBAUD_2400; break;
                    case 9600: theButton = RBBAUD_9600; break;
                    default: theButton = RBBAUD_300; break;
    }
    SendDlgItemMessage(hDlg,theButton,BM_SETCHECK,1,0L);

    // set data bits button. if it's not 8, then it's 7
                theButton = (dlgDCB.ByteSize == 8 ? RBDBITS_8 : RBDBITS_7);
    SendDlgItemMessage(hDlg,theButton,BM_SETCHECK,1,0L);

    // set parity button
                switch(dlgDCB.Parity){
                    case EVENPARITY: theButton = RBPARITY_EVEN; break;
                    case ODDPARITY: theButton = RBPARITY_ODD; break;
                    case NOPARITY: theButton = RBPARITY_NONE; break;
                    default: theButton = RBPARITY_NONE; break;
                }
    SendDlgItemMessage(hDlg,theButton,BM_SETCHECK,1,0L);

    // set stop bits button. if it's not 2, then it's 1
                theButton = (dlgDCB.StopBits == TWOSTOPBITS ? RBSBITS_2 : RBS
    SendDlgItemMessage(hDlg,theButton,BM_SETCHECK,1,0L);

    // set Xon/Xoff check box to on or off
    SendDlgItemMessage(hDlg,CBXONXOFF,BM_SETCHECK,dlgDCB.fInX,0L);

            break;

        default:
            return FALSE;
    }
    return TRUE;
}


//========================================================================\\

// FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

// PURPOSE:  Main procedure of the application.

//========================================================================\\

int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
HANDLE hInstance, hPrevInstance;
LPSTR lpszCmdLine;
int cmdShow;
{
    MSG   msg;
    BOOL bMsgAvail;
    short iNumRead,iError;

    hInst = hInstance;

    // initialize the stdio window library
    if(!hPrevInstance)
        if(!stdioInit(hInstance)) return FALSE;

    // create a stdio window
    if(!(hTTYWnd = CreateStdioWindow(
                      "TTY",
                      WS_OVERLAPPEDWINDOW,
                      CW_USEDEFAULT,
                      CW_USEDEFAULT,
                      CW_USEDEFAULT,
                      CW_USEDEFAULT,
                      NULL,
                      hInstance,
                      TRUE)))
        return FALSE;

    // subclass the stdio window

    lpfnOldTTYProc = (FARPROC) SetWindowLong(hTTYWnd,GWL_WNDPROC,
                                          (DWORD) TTYWndProc);

    // add the about box to the system menu
    AboutInit(hTTYWnd,hInstance);

    // add the terminal menu
    SetMenu(hTTYWnd,LoadMenu(hInstance, "TTYMENU"));

    // set the application icon
    SetClassWord(hTTYWnd, GCW_HICON,
                 LoadIcon( hInstance, MAKEINTRESOURCE(TTYICON) ));

    bConnected = FALSE;

    // initialize the DCB to default settings
    if(BuildCommDCB(CommSettings,&CommDCB) != 0) {
        MessageBox(GetFocus(),"Error Building DCB!","",MB_OK);
    }

    CommDCB.CtsTimeout = 100;         // Set Cts Timeout value
    CommDCB.DsrTimeout = 100;         // Set Dsr Timeout value
    CommDCB.fOutX = 1;           // output Xon/Xoff flow control on
    CommDCB.fInX = 1;           // input Xon/Xoff flow control on
    CommDCB.XonChar = 0x11;         // specify the Xon character
    CommDCB.XoffChar = 0x13;         // specify the Xoff character
    CommDCB.fNull = 1;           // strip null characters
    CommDCB.XonLim = 30;         // distance from queue empty to Xon
    CommDCB.XoffLim = (cbInBuf/2) + 1; // distance from queue full to Xoff
    CommDCB.fBinary = 0;

    // show the window
    ShowWindow( hTTYWnd, cmdShow );
    UpdateWindow( hTTYWnd );

    // PeekMessage loop to poll the comm port and pull messages from the queu
    // if there is a message available, process it.
    // otherwise, check the port and handle any available characters.

    while(TRUE){
        bMsgAvail = PeekMessage(&msg,NULL,0,0,PM_REMOVE);

        if(bMsgAvail){

            if(msg.message == WM_QUIT) break;

            TranslateMessage((LPMSG)&msg);
            DispatchMessage((LPMSG)&msg);

        } else {

            // check the comm port and process any available
            // characters. you could also use a timer instead, and have
            // the timer case of the wndproc check the port.
            if(bConnected){

                // get the CommStat record and get the # of characters availa
    GetCommError(CommDCB.Id,&CommStat);

                // get the number of characters available
                iNumRead = CommStat.cbInQue;

                if(iNumRead > 0) {

                    // get the number of characters rounded to the buffer siz
                    if(iNumRead > BufMax) iNumRead = BufMax;

                    // read the characters
                    iNumRead = ReadComm(CommDCB.Id,MsgBuff,
                                        iNumRead);

                    // check for errors
                    if(iNumRead < 0) {
                        iNumRead = -iNumRead;
                        nCommErr = GetCommError(CommDCB.Id,&CommStat);
                        // clear the event mask
      wCommEvt = GetCommEventMask(CommDCB.Id,0xFFFF);
                        // display what the error was
                        LoadString(hInst, nCommErr, sTemp, 20);
                        MessageBox(GetFocus(), sTemp, "Comm Read Error!",MB_O
                    }

                    MsgBuff[iNumRead] = '\0';

                    // send the characters to the tty window for processing
                    SendMessage(hTTYWnd,COMM_CHARS,
        iNumRead,(LONG)(LPSTR)MsgBuff);
        //wputs((LPSTR) MsgBuff);  // could just do this instead
                }


            }
        }
    }

}

//========================================================================\\

// FUNCTION: TTYWndProc(HWND, unsigned, WORD, LONG)

// PURPOSE:  Processes messages for the terminal window.

//========================================================================\\

long FAR PASCAL TTYWndProc( hWnd, message, wParam, lParam )
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    FARPROC   lpSettingDlgProc;
    char      szErr[22];
    unsigned  nErr;

    switch (message)
    {

    case WM_DESTROY:
        PostQuitMessage( 0 );
        break;

    case WM_ENDSESSION:
  if (wParam && bConnected)
      SendMessage(hWnd, WM_COMMAND, TTYCONNECT, 1L);
  break;

    case WM_CLOSE:
        // disconnect if still connected
        if(bConnected)SendMessage(hWnd,WM_COMMAND,TTYCONNECT,0L);
        // go ahead and close down
        return CallWindowProc(lpfnOldTTYProc,hWnd,
                              message,wParam,lParam);
        break;

    case WM_COMMAND:
        switch(wParam){

        case IDSABOUT:
            DialogBox( hInst, MAKEINTRESOURCE(ABOUTBOX), hWnd, lpprocAbout );
            break;

        case TTYEXIT:
            PostMessage(hWnd,WM_CLOSE,0,0L);
            break;

        case TTYCONNECT:
            // connect to port if not already connected
            if(!bConnected){
    if((PortID = OpenComm((CommDCB.Id == 2?COM2:COM1),cbInBuf,cbOutBuf)) < 0)
                    MessageBox(hWnd,"Error Opening Comm Port!","",MB_OK);
                    break;
                }

                FlushComm(PortID,0);
                FlushComm(PortID,1);

    CommDCB.Id = PortID;
    if(CommDCB.fInX) {
        CommDCB.fOutX = 1;      // enable output Xon/Xoff flow ctl
        CommDCB.fInX = 1;      // enable input Xon/Xoff flow ctl
        CommDCB.fRtsflow = 0;   // disable hardware flow ctl
        CommDCB.fDtrflow = 0;   // disable hardware flow ctl
    } else {
        CommDCB.fOutX = 0;      // disable ouput Xon/Xoff flow ctl
        CommDCB.fInX = 0;      // disable input Xon/Xoff flow ctl
        CommDCB.fRtsflow = 1;   // enable hardware flow ctl
        CommDCB.fDtrflow = 1;   // enable hardware flow ctl
    }


                if(SetCommState(&CommDCB) !=0 ){
                    MessageBox(hWnd,"Error Setting CommState!","",MB_OK);
                    break;
                }
                bConnected = TRUE;
                CheckMenuItem(GetMenu(hWnd),TTYCONNECT,MF_CHECKED);
                EnableMenuItem(GetMenu(hWnd),TTYSETTINGS,MF_DISABLED | MF_GRA
                MessageBox(hWnd,"Connection was successful.","",MB_OK);

            }else{
                // otherwise disconnect
                FlushComm(CommDCB.Id,0);
                FlushComm(CommDCB.Id,1);
                CloseComm(CommDCB.Id);
    if (!lParam)
                    MessageBox(hWnd,"Connection closed.","",MB_OK);
                bConnected = FALSE;
                CheckMenuItem(GetMenu(hWnd),TTYCONNECT,MF_UNCHECKED);
                EnableMenuItem(GetMenu(hWnd),TTYSETTINGS,MF_ENABLED);

            }
            break;

        case TTYSETTINGS:
            // settings dialog
            lpSettingDlgProc = MakeProcInstance(SettingDlgProc,hInst);
            DialogBox(hInst,"SETTINGSDLG",hWnd,lpSettingDlgProc);
            FreeProcInstance(lpSettingDlgProc);
            break;
        }
        break;

    case WM_CHAR:
  if(!bConnected) break;

  // if we're connected, send any keyboard characters to the port

        nCommErr = WriteComm(CommDCB.Id,(LPSTR) &wParam,1);
        if(nCommErr != 1) {
      nCommErr = GetCommError(CommDCB.Id,&CommStat);
            if(nCommErr != 0) {
    sTemp[0] = 0;
    for (nErr = 1; nErr != 0; nErr = nErr << 1) {
        if (nErr & nCommErr) {
      LoadString(hInst, nErr, szErr, 20);
      strcat(sTemp, szErr);
      strcat(sTemp, "\n");
        }
    }
    MessageBox(hWnd, sTemp, "Comm Write Error!", MB_OK);
            }
      wCommEvt = GetCommEventMask(CommDCB.Id, 0xFFFF);
        }
        break;

    case COMM_CHARS:
  // display available characters
        if(wParam > 0)
            wputs((LPSTR) lParam);

        break;

    // Pass all other messages to class's window procedure, since window
    // was subclassed
    default:
        return CallWindowProc(lpfnOldTTYProc,hWnd,
                              message,wParam,lParam);
        break;
    }
    return(0L);
}


WSTDIO.C
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\TTY\WSTDIO.C

//

//  FILE:    WSTDIO.c

//  PURPOSE: Contains functions for managing a standard I/O window.
//          Provides a means for easily sending text to a window
//          for debugging, etc.

//  FUNCTIONS:
//          SetupStdioDC() - Initializes DC.
//          ResetStdioDC() - Selects former font into DC.
//          GetStdioLine() - Returns pointer to specified line in buffer.
//          StdioUpdate() - Scroll and update displayed text.
//          putStdio() - Process I/O window messages.
//          StdioPaint() - Paint procedure for I/O window.
//          InitStdio() - Initialize values used by I/O window.
//          stdioInit() - Define/register I/O window class.
//          wopen() - Create a default I/O window.
//          CreateStdioWindow() - Create a customized I/O window.
//          wputs() - Puts a string in the I/O window.
//          StdioWndProc() - Processes messages for the I/O window.
//


#include <windows.h>
#include "wstdio.h"

//========================================================================\\

// Declarations

//========================================================================\\

#define MaxLines   25
#define MaxLine    MaxLines - 1
char sScrBuff[MaxLines][81];  // Array of characters on TTY
                                 // could make this heap object so can reallo
short nFirstLine;    // Index of first line on TTY in the array
short nLastLine;    // Index of last line on TTY in the array

short nCurrPos;     // Current TTY line output position
short nStdioCharWidth,
      nStdioCharHeight;   // width and height of Stdio font chars


DWORD StdiobkRGB;                      // background color
DWORD StdiotextRGB;                    // text color
#define Stdio_FONT SYSTEM_FIXED_FONT   // font used for display
HFONT hOldFont;
HWND hWndStdio = NULL;                 // Handle to standard I/O window
HANDLE hStdioInst;
BOOL bStdioQuit;
BOOL bInited = FALSE;


//========================================================================\\

// FUNCTION: SetupStdioDC(HWND, HDC)

// PURPOSE:  Sets up the I/O window DC. Called at GetDC/BeginPaint time.

//========================================================================\\

void SetupStdioDC(HWND hWnd, HDC hDC)
{
    RECT rClRect;

    GetClientRect(hWnd,&rClRect);

    // set origin to 25(+1 extra) lines from the bottom of the window
    SetViewportOrg(hDC,0,rClRect.bottom - ((MaxLines+1) * nStdioCharHeight));

    SetMapMode(hDC, MM_ANISOTROPIC);

    // Set the extents such that one unit horizontally or
    // vertically is one character width or height.
    SetWindowExt(hDC,1,1);

    // Set the viewport such that the last line in the buffer is
    // displayed at the bottom of the window.
    SetViewportExt(hDC,nStdioCharWidth,nStdioCharHeight);

    // Set the background mode to opaque, and select the font.
    SetBkMode(hDC,OPAQUE);
    hOldFont = SelectObject(hDC,GetStockObject(Stdio_FONT));

}


//========================================================================\\

// FUNCTION: ResetStdioDC(HDC)

// PURPOSE:  Prepare to release the DC by selecting the system font.

//========================================================================\\

void ResetStdioDC(HDC hDC)
{
    SelectObject(hDC,hOldFont);
}



//========================================================================\\

// FUNCTION: GetStdioLine(short)

// PURPOSE:  Return a pointer to the specified line of the display.

//========================================================================\\

char *GetStdioLine(short ndx)
{
    short pos;

    // find the first line (one past the last line since we have a
    // circular buffer). index to the desired line from there.
    pos = nLastLine + 1;
    if(pos == MaxLines) pos = 0;

    pos = pos + ndx;
    if(pos > MaxLine) pos = pos - MaxLines;

    return(sScrBuff[pos]);
}



//========================================================================\\

// FUNCTION: StdioUpdate(HWND, HDC, int)

// PURPOSE:  Scroll the window by the number of lines we have received,
//           and display the text in the invalidated areas.

//========================================================================\\

void StdioUpdate(HWND hWnd, HDC hDC, int iLinesOut)
{
    RECT rcRect;

    if(iLinesOut > 0){

         // scroll screen by number of lines received
   GetClientRect(hWnd,&rcRect);
   rcRect.bottom -= nStdioCharHeight;

   ScrollWindow(hWnd,0,-(nStdioCharHeight * iLinesOut),&rcRect,NULL);
    }

    UpdateWindow(hWnd);
}


//========================================================================\\

// FUNCTION: putStdio(HWND, HDC, WORD, LPSTR)

// PURPOSE:  Process incoming text to Stdio window.

//========================================================================\\

void putStdio(HWND hWnd, HDC hDC, WORD wParam, LPSTR lParam)
{
    short i, j;
    char *sBuffer;
    RECT rClRect, rcInvalid;
    char *psLine;
    short iLinesOut = 0;    // # of lines to scroll

    sBuffer = sScrBuff[nLastLine]; // pointer to current line

    // scan the text, handle any special characters, and display the rest.

    for(i=0; i<wParam; i++){
  switch(lParam[i]) {

        case '\r': // return
            // move to the start of the line
      nCurrPos = 0;   // reset the current position in the line
            break;

        case '\n': // new line

      // "scroll" the window
      ++iLinesOut;    // increment lines to scroll
      nCurrPos = 0;   // reset the current position in the line

      ++nLastLine;
      if(nLastLine > MaxLine) nLastLine = 0;

            // clear the new line
      sBuffer = sScrBuff[nLastLine];
      for(j=0; j<80; j++) sBuffer[j] = '\0';
            break;

        case '\b': // backspace

            // move back one space
      if(nCurrPos > 0) {

    --nCurrPos;
    sBuffer[nCurrPos] = '\0';
    rcInvalid.top = MaxLine; rcInvalid.bottom = MaxLine + 1;
    rcInvalid.left = nCurrPos;
    rcInvalid.right = nCurrPos + 1;
    LPtoDP(hDC,(POINT *) &rcInvalid, 2);

    // invalidate the area so that it gets redrawn
    InvalidateRect(hWnd,&rcInvalid, TRUE);

      }
      break;

  case '\t':
      // ignore tabs for now
      break;

        default:
            //add char to buffer
      if(nCurrPos < 80){

    // put the character in the screen buffer
    sBuffer[nCurrPos] = lParam[i]; // add char to screen line

    // calculate area to invalidate
    rcInvalid.top = MaxLine; rcInvalid.bottom = MaxLine + 1;
    rcInvalid.left = nCurrPos;
    ++nCurrPos;
    rcInvalid.right = nCurrPos;

    // only need to invalidate the area if it is on the last line
    if(iLinesOut == 0) {
        LPtoDP(hDC,(POINT *) &rcInvalid, 2);
        // invalidate the area so that it gets redrawn
        InvalidateRect(hWnd,&rcInvalid, FALSE);
    }
            }
            break;
  }
  // force scroll after 2 lines. you will scroll faster if you increase
  // this, but it may not look good.

  if(iLinesOut > 2) {
      StdioUpdate(hWnd, hDC, iLinesOut);
      iLinesOut = 0;
  }
    }

    // force scroll and update at the end of each bunch of characters.
    StdioUpdate(hWnd, hDC, iLinesOut);
}

//========================================================================\\

// FUNCTION: StdioPaint(HWND)

// PURPOSE:  The I/O window paint procedure.  Draws necessary text in
//           the window.

//========================================================================\\

void StdioPaint(HWND hWnd )
{
    char *psLine;
    register int i;
    PAINTSTRUCT ps;
    HDC hDC;
    RECT rcUpdate, rcClient;
    int nVPaintBeg, nVPaintEnd, nHPaintBeg, nHPaintEnd;

    hDC = BeginPaint( hWnd, (LPPAINTSTRUCT)&ps );
    SetupStdioDC(hWnd,hDC);

    rcUpdate = ps.rcPaint;
    DPtoLP(hDC,(POINT *) &rcUpdate, 2);

    // calculate first and last lines to update
    nVPaintBeg = max (0, rcUpdate.top);
    nVPaintEnd = min (MaxLines, rcUpdate.bottom);

    // calculate the first and last columns to update
    nHPaintBeg = max (0, rcUpdate.left);
    nHPaintEnd = min (80, rcUpdate.right);

    // display the lines that need to be drawn
    for(i=nVPaintBeg; i<nVPaintEnd; i++){
  psLine = GetStdioLine(i) + nHPaintBeg;
        TextOut(hDC,
    nHPaintBeg,
                i,
    psLine,
    strlen(psLine));
    }
    ResetStdioDC(hDC);
    EndPaint( hWnd, (LPPAINTSTRUCT)&ps );

}


//========================================================================\\

// FUNCTION: InitStdio(HWND)

// PURPOSE:  Initialize variables used by I/O window.

//========================================================================\\

void InitStdio(HWND hWnd)
{
    int i,j;
    HDC hDC;
    TEXTMETRIC Metrics;

    // initialize screen buffer to nulls
    for(i=0; i<MaxLines; i++)
  for(j=0; j<81; j++)
      sScrBuff[i][j] = '\0';

    nFirstLine = 0;
    nLastLine = MaxLine;
    nCurrPos = 0;

    // get the text metrics for the font we are using
    hDC = GetDC(hWnd);
    hOldFont = SelectObject(hDC,GetStockObject(Stdio_FONT));
    GetTextMetrics(hDC,&Metrics);
    SelectObject(hDC,hOldFont);
    ReleaseDC(hWnd,hDC);

    // calculate the height and width of the font
    nStdioCharWidth = Metrics.tmMaxCharWidth;
    nStdioCharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;

    // get the background and forground colors we are going to use
    StdiobkRGB = GetSysColor(COLOR_WINDOW); // background color
    StdiotextRGB = GetSysColor(COLOR_WINDOWTEXT); // text color

    bInited = TRUE;

}


//========================================================================\\

// FUNCTION: stdioInit(HANDLE)

// PURPOSE:  Initialize the stdio module. Registers the window class.

// RETURNS:  Status of RegisterClass().

//========================================================================\\

BOOL stdioInit(HANDLE hInstance)
{

    PWNDCLASS pStdioClass;
    HWND hDummyWnd;

    if(hInstance == NULL) return FALSE;

    // create the stdio window
    pStdioClass = (PWNDCLASS)LocalAlloc( LPTR, sizeof(WNDCLASS) );

    pStdioClass->hCursor        = LoadCursor( NULL, IDC_ARROW );
    pStdioClass->lpszClassName  = (LPSTR)"Stdio";
    pStdioClass->hbrBackground  = COLOR_WINDOW + 1;
    pStdioClass->hInstance      = hInstance;
    pStdioClass->style    = CS_HREDRAW | CS_VREDRAW;
    pStdioClass->lpfnWndProc    = StdioWndProc;

    if (!RegisterClass( (LPWNDCLASS)pStdioClass ) )
        // Initialization failed.
        // Windows will automatically deallocate all allocated memory.
        return FALSE;

    LocalFree( (HANDLE)pStdioClass );

    hStdioInst = hInstance;
    return TRUE;
}


//========================================================================\\

// FUNCTION: wopen(HWND, BOOL)

// PURPOSE:  Create a default style stdio window. If bQuit is TRUE,
//           PostQuitMessage will be called when the window is closed.
//           Therefore, the stdio window can be used for the main
//           application window.

// RETURNS:  Handle to window created.

//========================================================================\\

HWND wopen(HWND hWndParent, BOOL bQuit)
{

    // if window already created, return handle
    if(hWndStdio != NULL) return hWndStdio;

    hWndStdio = CreateWindow((LPSTR)"Stdio",
                    (LPSTR)"STDIO",
        WS_OVERLAPPEDWINDOW,
                    CW_USEDEFAULT,
                    CW_USEDEFAULT,
                    CW_USEDEFAULT,
                    CW_USEDEFAULT,
                    (HWND)hWndParent,
                    (HMENU)NULL,
                    (HANDLE)hStdioInst,
                    (LPSTR)NULL
                );
    if(hWndStdio == NULL) return FALSE;
    ShowWindow(hWndStdio, SW_SHOW);
    UpdateWindow(hWndStdio);

    bStdioQuit = bQuit;
    return hWndStdio;

}


//========================================================================\\

// FUNCTION: CreateStdioWindow(LPSTR, DWORD, int, int, int, int, HWND,
//                            HANDLE, BOOL)

// PURPOSE:  Create an I/O window with definable name, style, size, etc.

// RETURNS:  Handle to window created.

//========================================================================\\

HWND CreateStdioWindow(LPSTR lpWindowName, DWORD dwStyle,
                       int X, int Y, int nWidth, int nHeight,
                       HWND hWndParent, HANDLE hInstance, BOOL bQuit)
{

    // if window already created, return handle
    if(hWndStdio != NULL) return hWndStdio;

    hWndStdio = CreateWindow((LPSTR)"Stdio",
                    (LPSTR)lpWindowName,
                    dwStyle,
                    X,
                    Y,
                    nWidth,
                    nHeight,
                    (HWND)hWndParent,
                    (HMENU)NULL,
                    (HANDLE)hInstance,
                    (LPSTR)NULL);

    if(hWndStdio == NULL) return FALSE;

    bStdioQuit = bQuit;
    return hWndStdio;
}


//========================================================================\\

// FUNCTION: wputs(LPSTR)

// PURPOSE:  Equivalent to puts() stdio function. Currently, '\n' is
//           not recognized as in '\r\n', as with normal puts(). Must
//           send '\r\n' explicitly.

// RETURNS:  Status of wopen(), if called, otherwise TRUE.

//========================================================================\\


BOOL wputs(LPSTR lpStr)
{
    HDC hDC;
    int nStrLen;

    // if being used for quick and dirty text output, a stdio window
    // will be opened if it hasn't been already.
    if(hWndStdio == NULL) if(wopen(NULL, FALSE) == NULL) return FALSE;

    hDC = GetDC(hWndStdio);
    SetupStdioDC(hWndStdio,hDC);
    nStrLen = lstrlen(lpStr);

    putStdio(hWndStdio,hDC,nStrLen,(LPSTR)lpStr);

    ResetStdioDC(hDC);
    ReleaseDC(hWndStdio,hDC);

    return TRUE;
}


//========================================================================\\

// FUNCTION: StdioWndProc(HWND, unsigned, WORD, LONG)

// PURPOSE:  Process messages for the I/O window. This function should
//           be exported in the application's .DEF file.

//========================================================================\\

long FAR PASCAL StdioWndProc( hWnd, message, wParam, lParam )
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    PAINTSTRUCT ps;
    HDC hDC;
    LPPOINT ptMinMaxInfo;


    switch (message)
    {

    case WM_CREATE:

  // initialize stdio variables
  InitStdio(hWnd);
  break;


    case WM_SYSCOLORCHANGE:

  // if the colors have been changed in the control panel,
  // we need to change also.

  StdiobkRGB = GetSysColor(COLOR_WINDOW); // background color
  StdiotextRGB = GetSysColor(COLOR_WINDOWTEXT); // text color
        return DefWindowProc( hWnd, message, wParam, lParam );
  break;

    case WM_GETMINMAXINFO:
  if(!bInited) InitStdio(hWnd);

  // constrain the sizing of the window to 80 by 25 characters.

  ptMinMaxInfo = (LPPOINT) lParam;

  ptMinMaxInfo[1].x = nStdioCharWidth * 80
           + 2 * GetSystemMetrics(SM_CXFRAME);
  ptMinMaxInfo[1].y = nStdioCharHeight * 26
           + 2 * GetSystemMetrics(SM_CYFRAME);

  ptMinMaxInfo[4].x = nStdioCharWidth * 80
           + 2 * GetSystemMetrics(SM_CXFRAME);
  ptMinMaxInfo[4].y = nStdioCharHeight * 26
           + 2 * GetSystemMetrics(SM_CYFRAME);
  break;

    case WM_PAINT:

  // repaint the Stdio window
  StdioPaint(hWnd);
  break;

    case WM_DESTROY:

  // if specified when created, PostQuitMessage should be called
  // when the window is destroyed.

        if(bStdioQuit)
            PostQuitMessage(0);
        break;

    case WM_CLOSE:
        // destroy stdio data
        hWndStdio = NULL;

        // go ahead and close down
        // -- fall through to default --
    default:
        return DefWindowProc( hWnd, message, wParam, lParam );
        break;
    }
    return(0L);
}




Microsoft Windows S.D.K. v3.0 Sample MASM Source Code


CLOCKDAT.ASM
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\CLOCK\CLOCKDAT.ASM

        title   Hardware Dependent Parameters
        %out    config
        page    ,132
OEM     segment public
dw         0,     -7999
dw         836,   -7956
dw         1663,  -7825
dw         2472,  -7608
dw         3253,  -7308
dw         3999,  -6928
dw         4702,  -6472
dw         5353,  -5945
dw         5945,  -5353
dw         6472,  -4702
dw         6928,  -4000
dw         7308,  -3253
dw         7608,  -2472
dw         7825,  -1663
dw         7956,  -836

dw         8000,  0
dw         7956,  836
dw         7825,  1663
dw         7608,  2472
dw         7308,  3253
dw         6928,  4000
dw         6472,  4702
dw         5945,  5353
dw         5353,  5945
dw         4702,  6472
dw         3999,  6928
dw         3253,  7308
dw         2472,  7608
dw         1663,  7825
dw         836,   7956

dw          0,    7999
dw         -836,  7956
dw         -1663, 7825
dw         -2472, 7608
dw         -3253, 7308
dw         -4000, 6928
dw         -4702, 6472
dw         -5353, 5945
dw         -5945, 5353
dw         -6472, 4702
dw         -6928, 3999
dw         -7308, 3253
dw         -7608, 2472
dw         -7825, 1663
dw         -7956, 836

dw         -7999, -0
dw         -7956, -836
dw         -7825, -1663
dw         -7608, -2472
dw         -7308, -3253
dw         -6928, -4000
dw         -6472, -4702
dw         -5945, -5353
dw         -5353, -5945
dw         -4702, -6472
dw         -3999, -6928
dw         -3253, -7308
dw         -2472, -7608
dw         -1663, -7825
dw         -836 , -7956
OEM     ends
end



DLGOPENA.ASM
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SHOWDIB\DLGOPENA.ASM

  title  dlgopena.asm
;****************************************************************************
;*                         *
;*  MODULE  : DLGOPENA.ASM                   *
;*                         *
;*  DESCRIPTION : Assembly language helper routines for DLGOPEN.C         *
;*                         *
;*  FUNCTIONS  : chdir ()  - change to specified asciiz directory.         *
;*                         *
;****************************************************************************
?WIN = 1

?PLM=1      ; PASCAL Calling convention is DEFAULT
?WIN=1      ; Windows calling convention
?386=0      ; Use 386 code?
include cmacros.inc

;*********************************************************************
;* The following structure should be used to access high and low
;* words of a DWORD.  This means that "word ptr foo[2]" -> "foo.hi".
;*********************************************************************

LONG    struc
lo      dw      ?
hi      dw      ?
LONG    ends

FARPOINTER      struc
off     dw      ?
sel     dw      ?
FARPOINTER      ends

;*********************************************************************
;               DATA SEGMENT DECLARATIONS
;*********************************************************************

ifndef SEGNAME
    SEGNAME equ <TEXT>
endif

if ?386
    .386p
    createSeg _%SEGNAME, CodeSeg, word, use16, CODE
else
    .286p
    createSeg _%SEGNAME, CodeSeg, word, public, CODE
endif

sBegin  DATA
sEnd  DATA

sBegin  CodeSeg

assumes CS,CodeSeg
assumes DS,DATA

;****************************************************************************
;*                      *
;*  FUNCTION   : chdir (p)                *
;*                      *
;*  PURPOSE    : Change to asciiz directory specified in p        *
;*                      *
;*  RETURNS    : 1 - Success                *
;*     0 - Error                *
;*                      *
;****************************************************************************

cProc  chdir,<PUBLIC,FAR,PASCAL>,<ds>
  parmD p
cBegin
  lds  dx,p
  mov  bx,dx

  cmp  BYTE PTR ds:[bx+1],':'
        jnz     chdnod                  ; No drive
        mov     dl,ds:[bx]
        or      dl,20h
        sub     dl,'a'

  mov  ah,0eh      ; Set current drive
  int  21h

  mov  ah,19h      ; Get current drive
  int  21h

  cmp  al,dl
  jne  chderror

  lds  dx,p
  add  dx,2
  mov  bx,dx
  cmp  BYTE PTR ds:[bx],0  ; If path name is "" we are there
  jz  chdok
chdnod:
  mov  ah,3bh
  int  21h
  jc  chderror
chdok:
  mov  ax,1
chdexit:
cEnd
chderror:
  xor  ax,ax
  jmp  short chdexit

sEnd  CodeSeg

end


GETTIME.ASM
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\CLOCK\GETTIME.ASM

; get the time more efficiently than cmerge and DOSCALL() do

?PLM=0
include cmacros.inc

time    struc
        hour    dw  ?
        minute  dw  ?
        second  dw  ?
time    ends

assumes CS,CODE
assumes DS,DATA

sBegin   CODE

cProc   GetTime, <PUBLIC, NEAR>
        parmW   pTime               ; pointer to the structure to fill

cBegin
        mov     ax, 2c00h           ; get time
        int     21h
        mov     bx, pTime
        cmp     ch, 12                  ; if hour <12
        jl      lt12                    ; we're ok
        sub     ch,12                   ; else adjust it
lt12:
        xor     ax,ax
        mov     al,ch
        mov     [bx].hour, ax
        mov     al,cl
        mov     [bx].minute, ax
        mov     al,dh
        mov     [bx].second, ax
cEnd
sEnd    CODE
        END


LIBENTRY.ASM
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\SELECT\LIBENTRY.ASM

PAGE,132
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;       LIBENTRY.ASM
;
;       Windows dynamic link library entry routine
;
;   This module generates a code segment called INIT_TEXT.
;   It initializes the local heap if one exists and then calls
;   the C routine LibMain() which should have the form:
;   BOOL FAR PASCAL LibMain(HANDLE hInstance,
;                           WORD   wDataSeg,
;                           WORD   cbHeap,
;                           LPSTR  lpszCmdLine);
;
;   The result of the call to LibMain is returned to Windows.
;   The C routine should return TRUE if it completes initialization
;   successfully, FALSE if some error occurs.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include cmacros.inc

externFP <LibMain>               ; the C routine to be called

createSeg INIT_TEXT, INIT_TEXT, BYTE, PUBLIC, CODE
sBegin  INIT_TEXT
assumes CS,INIT_TEXT

?PLM=0                           ; 'C'naming
externA  <_acrtused>             ; ensures that Win DLL startup code is linke

?PLM=1                           ; 'PASCAL' naming
externFP <LocalInit>             ; Windows heap init routine

cProc   LibEntry, <PUBLIC,FAR>   ; entry point into DLL

cBegin
        push    di               ; handle of the module instance
        push    ds               ; library data segment
        push    cx               ; heap size
        push    es               ; command line segment
        push    si               ; command line offset

        ; if we have some heap then initialize it
        jcxz    callc            ; jump if no heap specified

        ; call the Windows function LocalInit() to set up the heap
        ; LocalInit((LPSTR)start, WORD cbHeap);

        xor     ax,ax
        cCall   LocalInit <ds, ax, cx>
        or      ax,ax            ; did it do it ok ?
        jz      error            ; quit if it failed

        ; invoke the C routine to do any special initialization

callc:
        call    LibMain          ; invoke the 'C' routine (result in AX)
        jmp short exit           ; LibMain is responsible for stack clean up

error:
  pop  si     ; clean up stack on a LocalInit error
        pop     es
        pop     cx
        pop     ds
        pop     di

exit:

cEnd

sEnd  INIT_TEXT

end LibEntry


LIBENTRY.ASM
CD-ROM Disc Path:   \SAMPCODE\WIN_SDK\RAINBOW\LIBENTRY.ASM

PAGE,132
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;       LIBENTRY.ASM
;
;       Windows dynamic link library entry routine
;
;   This module generates a code segment called INIT_TEXT.
;   It initializes the local heap if one exists and then calls
;   the C routine LibMain() which should have the form:
;   BOOL FAR PASCAL LibMain(HANDLE hInstance,
;                           WORD   wDataSeg,
;                           WORD   cbHeap,
;                           LPSTR  lpszCmdLine);
;
;   The result of the call to LibMain is returned to Windows.
;   The C routine should return TRUE if it completes initialization
;   successfully, FALSE if some error occurs.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include cmacros.inc

externFP <LibMain>               ; the C routine to be called

createSeg INIT_TEXT, INIT_TEXT, BYTE, PUBLIC, CODE
sBegin  INIT_TEXT
assumes CS,INIT_TEXT

?PLM=0                           ; 'C'naming
externA  <_acrtused>             ; ensures that Win DLL startup code is linke

?PLM=1                           ; 'PASCAL' naming
externFP <LocalInit>             ; Windows heap init routine

cProc   LibEntry, <PUBLIC,FAR>   ; entry point into DLL

cBegin
        push    di               ; handle of the module instance
        push    ds               ; library data segment
        push    cx               ; heap size
        push    es               ; command line segment
        push    si               ; command line offset

        ; if we have some heap then initialize it
        jcxz    callc            ; jump if no heap specified

        ; call the Windows function LocalInit() to set up the heap
        ; LocalInit((LPSTR)start, WORD cbHeap);

        xor     ax,ax
        cCall   LocalInit <ds, ax, cx>
        or      ax,ax            ; did it do it ok ?
        jz      error            ; quit if it failed

        ; invoke the C routine to do any special initialization

callc:
        call    LibMain          ; invoke the 'C' routine (result in AX)
        jmp short exit           ; LibMain is responsible for stack clean up

error:
  pop  si     ; clean up stack on a LocalInit error
        pop     es
        pop     cx
        pop     ds
        pop     di

exit:

cEnd

sEnd  INIT_TEXT

end LibEntry




Programming Windows (C. Petzold) Sample `C' Source Code


ABOUT1.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP10\ABOUT1.C

/*------------------------------------------
   ABOUT1.C -- About Box Demo Program No. 1
         (c) Charles Petzold, 1990
  ------------------------------------------*/

#include <windows.h>
#include "about1.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG);

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
        LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "About1" ;
     MSG         msg;
     HWND        hwnd ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "About Box Demo Program",
                          WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd);

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

BOOL FAR PASCAL AboutDlgProc (HWND hDlg, WORD message, WORD wParam, LONG lPar
     {
     switch (message)
          {
          case WM_INITDIALOG:
               return TRUE ;

          case WM_COMMAND:
               switch (wParam)
                    {
        case IDOK:
        case IDCANCEL:
                         EndDialog (hDlg, 0) ;
       return TRUE ;
                    }
               break ;
          }
     return FALSE ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static FARPROC lpfnAboutDlgProc ;
     static HANDLE  hInstance ;

     switch (message)
          {
          case WM_CREATE:
               hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;

               lpfnAboutDlgProc = MakeProcInstance (AboutDlgProc, hInstance)
               return 0 ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_ABOUT:
                         DialogBox (hInstance, "AboutBox", hwnd,
                                        lpfnAboutDlgProc) ;
                         return 0 ;
                    }
               break ;

          case WM_DESTROY :
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


ABOUT2.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP10\ABOUT2.C

/*------------------------------------------
   ABOUT2.C -- About Box Demo Program No. 2
               (c) Charles Petzold, 1990
  ------------------------------------------*/

#include <windows.h>
#include "about2.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG);

short nCurrentColor  = IDD_BLACK,
      nCurrentFigure = IDD_RECT ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "About2" ;
     MSG         msg;
     HWND        hwnd ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "About Box Demo Program",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd);

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

void PaintWindow (HWND hwnd, short nColor, short nFigure)
     {
     static DWORD dwColor [8] = { RGB (0,     0, 0), RGB (  0,   0, 255),
                                  RGB (0,   255, 0), RGB (  0, 255, 255),
                                  RGB (255,   0, 0), RGB (255,   0, 255),
                                  RGB (255, 255, 0), RGB (255, 255, 255) } ;
     HBRUSH       hBrush ;
     HDC          hdc ;
     RECT         rect ;

     hdc = GetDC (hwnd) ;
     GetClientRect (hwnd, &rect) ;
     hBrush = CreateSolidBrush (dwColor [nColor - IDD_BLACK]) ;
     hBrush = SelectObject (hdc, hBrush) ;

     if (nFigure == IDD_RECT)
          Rectangle (hdc, rect.left, rect.top, rect.right, rect.bottom) ;
     else
          Ellipse   (hdc, rect.left, rect.top, rect.right, rect.bottom) ;

     DeleteObject (SelectObject (hdc, hBrush)) ;
     ReleaseDC (hwnd, hdc) ;
     }

void PaintTheBlock (HWND hCtrl, short nColor, short nFigure)
     {
     InvalidateRect (hCtrl, NULL, TRUE) ;
     UpdateWindow (hCtrl) ;
     PaintWindow (hCtrl, nColor, nFigure) ;
     }

BOOL FAR PASCAL AboutDlgProc (HWND hDlg, WORD message, WORD wParam, LONG lPar
     {
     static HWND  hCtrlBlock ;
     static short nColor, nFigure ;

     switch (message)
          {
          case WM_INITDIALOG:
               nColor  = nCurrentColor ;
               nFigure = nCurrentFigure ;

               CheckRadioButton (hDlg, IDD_BLACK, IDD_WHITE, nColor) ;
               CheckRadioButton (hDlg, IDD_RECT,  IDD_ELL,   nFigure) ;

               hCtrlBlock = GetDlgItem (hDlg, IDD_PAINT) ;

               SetFocus (GetDlgItem (hDlg, nColor)) ;
               return FALSE ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDOK:
                         nCurrentColor  = nColor ;
                         nCurrentFigure = nFigure ;
                         EndDialog (hDlg, TRUE) ;
                         return TRUE ;

                    case IDCANCEL:
                         EndDialog (hDlg, FALSE) ;
                         return TRUE ;

                    case IDD_BLACK:
                    case IDD_RED:
                    case IDD_GREEN:
                    case IDD_YELLOW:
                    case IDD_BLUE:
                    case IDD_MAGENTA:
                    case IDD_CYAN:
                    case IDD_WHITE:
                         nColor = wParam ;
                         CheckRadioButton (hDlg, IDD_BLACK, IDD_WHITE, wParam
                         PaintTheBlock (hCtrlBlock, nColor, nFigure) ;
                         return TRUE ;

                    case IDD_RECT:
                    case IDD_ELL:
                         nFigure = wParam ;
                         CheckRadioButton (hDlg, IDD_RECT, IDD_ELL, wParam) ;
                         PaintTheBlock (hCtrlBlock, nColor, nFigure) ;
                         return TRUE ;
                    }
               break ;

          case WM_PAINT:
               PaintTheBlock (hCtrlBlock, nColor, nFigure) ;
               break ;
          }
     return FALSE ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static FARPROC lpfnAboutDlgProc ;
     static HANDLE  hInstance ;
     PAINTSTRUCT    ps ;

     switch (message)
          {
          case WM_CREATE:
               hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;

               lpfnAboutDlgProc = MakeProcInstance (AboutDlgProc, hInstance)
               return 0 ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_ABOUT:
                         if (DialogBox (hInstance, "AboutBox", hwnd,
                                        lpfnAboutDlgProc))
                              InvalidateRect (hwnd, NULL, TRUE) ;
                         return 0 ;
                    }
               break ;

          case WM_PAINT:
               BeginPaint (hwnd, &ps) ;
               EndPaint (hwnd, &ps) ;

               PaintWindow (hwnd, nCurrentColor, nCurrentFigure) ;
               return 0 ;

          case WM_DESTROY :
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


ABOUT3.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP10\ABOUT3.C

/*------------------------------------------
   ABOUT3.C -- About Box Demo Program No. 3
               (c) Charles Petzold, 1990
  ------------------------------------------*/

#include <windows.h>
#include "about3.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
long FAR PASCAL EllipPushWndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "About3" ;
     MSG         msg;
     HWND        hwnd ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;

          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = EllipPushWndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = COLOR_WINDOW + 1 ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = "EllipPush" ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "About Box Demo Program",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd);

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

BOOL FAR PASCAL AboutDlgProc (HWND hDlg, WORD message, WORD wParam, LONG lPar
     {
     switch (message)
          {
          case WM_INITDIALOG:
               return TRUE ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDOK:
                         EndDialog (hDlg, 0) ;
                         return TRUE ;
                    }
               break ;
          }
     return FALSE ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static FARPROC lpfnAboutDlgProc ;
     static HANDLE  hInstance ;

     switch (message)
          {
          case WM_CREATE:
               hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;

               lpfnAboutDlgProc = MakeProcInstance (AboutDlgProc, hInstance)
               return 0 ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_ABOUT:
                         DialogBox (hInstance, "AboutBox", hwnd,
                                        lpfnAboutDlgProc) ;
                         return 0 ;
                    }
               break ;

          case WM_DESTROY :
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

long FAR PASCAL EllipPushWndProc (HWND hwnd, WORD message,
                                  WORD wParam, LONG lParam)
     {
     char        szText [40] ;
     HBRUSH      hBrush ;
     HDC         hdc ;
     PAINTSTRUCT ps ;
     RECT        rect ;

     switch (message)
          {
          case WM_PAINT:
               GetClientRect (hwnd, &rect) ;
               GetWindowText (hwnd, szText, sizeof szText) ;

               hdc = BeginPaint (hwnd, &ps) ;

               hBrush = CreateSolidBrush (GetSysColor (COLOR_WINDOW)) ;
               hBrush = SelectObject (hdc, hBrush) ;
               SetBkColor (hdc, GetSysColor (COLOR_WINDOW)) ;
               SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ;

               Ellipse (hdc, rect.left, rect.top, rect.right, rect.bottom) ;
               DrawText (hdc, szText, -1, &rect,
                              DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;

               DeleteObject (SelectObject (hdc, hBrush)) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_KEYUP:
               if (wParam != VK_SPACE)
                    break ;
                                        // fall through
          case WM_LBUTTONUP:
               SendMessage (GetParent (hwnd), WM_COMMAND,
                    GetWindowWord (hwnd, GWW_ID), (LONG) hwnd) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


ARCS.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP12\ARCS.C

/*-------------------------------------------------------
   ARCS.C -- Demonstrates Drawing Arcs, Chords, and Pies
             (c) Charles Petzold, 1990
  -------------------------------------------------------*/

#include <windows.h>
#include "arcs.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Arcs" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Arcs, Chords, and Pies",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static short cxClient, cyClient, x1, x2, x3, x4, y1, y2, y3, y4,
                  nFigure = IDM_ARC ;
     HDC          hdc ;
     HMENU        hMenu ;
     HPEN         hPen ;
     PAINTSTRUCT  ps ;
     short        x, y ;

     switch (message)
          {
          case WM_SIZE:
               x3 = y3 = 0 ;
               x4 = cxClient = LOWORD (lParam) ;
               y4 = cyClient = HIWORD (lParam) ;
               x2 = 3 * (x1 = cxClient / 4) ;
               y2 = 3 * (y1 = cyClient / 4) ;
               return 0 ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_ARC:
                    case IDM_CHORD:
                    case IDM_PIE:
                         hMenu = GetMenu (hwnd) ;
                         CheckMenuItem (hMenu, nFigure, MF_UNCHECKED) ;
                         CheckMenuItem (hMenu, nFigure = wParam, MF_CHECKED)
                         InvalidateRect (hwnd, NULL, FALSE) ;
                         return 0 ;
                    }
               break ;

          case WM_LBUTTONDOWN:
               if (!(wParam & MK_SHIFT))
                    {
                    x3 = LOWORD (lParam) ;
                    y3 = HIWORD (lParam) ;
                    InvalidateRect (hwnd, NULL, TRUE) ;
                    return 0 ;
                    }
                                        // fall through for MK_SHIFT
          case WM_RBUTTONDOWN:
               x4 = LOWORD (lParam) ;
               y4 = HIWORD (lParam) ;
               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               hPen = SelectObject (hdc, CreatePen (PS_DOT, 1, 0L)) ;
               Rectangle (hdc, x1, y1, x2, y2) ;
               Ellipse   (hdc, x1, y1, x2, y2) ;

               DeleteObject (SelectObject (hdc, CreatePen (PS_SOLID, 3, 0L)))

               switch (nFigure)
                    {
                    case IDM_ARC:
                         Arc (hdc, x1, y1, x2, y2, x3, y3, x4, y4) ;
                         break ;

                    case IDM_CHORD:
                         Chord (hdc, x1, y1, x2, y2, x3, y3, x4, y4) ;
                         break ;

                    case IDM_PIE:
                         Pie (hdc, x1, y1, x2, y2, x3, y3, x4, y4) ;
                         break ;
                    }

               DeleteObject (SelectObject (hdc, hPen)) ;

               MoveTo (hdc, x3, y3) ;
               LineTo (hdc, cxClient / 2, cyClient / 2) ;
               LineTo (hdc, x4, y4) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


BEEPER1.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP05\BEEPER1.C

/*-----------------------------------------
   BEEPER1.C  -- Timer Demo Program No. 1
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>
#define ID_TIMER    1

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Beeper1" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Beeper1 Timer Demo",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     while (!SetTimer (hwnd, ID_TIMER, 1000, NULL))
          if (IDCANCEL == MessageBox (hwnd,
                              "Too many clocks or timers!", szAppName,
                              MB_ICONEXCLAMATION | MB_RETRYCANCEL))
               return FALSE ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL fFlipFlop = FALSE ;
     HBRUSH      hBrush ;
     HDC         hdc ;
     PAINTSTRUCT ps ;
     RECT        rc ;

     switch (message)
          {
          case WM_TIMER:
               MessageBeep (0) ;

               fFlipFlop = !fFlipFlop ;
               InvalidateRect (hwnd, NULL, FALSE) ;

               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               GetClientRect (hwnd, &rc) ;

               hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) :
                                                      RGB(0,0,255)) ;
               FillRect (hdc, &rc, hBrush) ;
               EndPaint (hwnd, &ps) ;
               DeleteObject (hBrush) ;
               return 0 ;

          case WM_DESTROY:
               KillTimer (hwnd, ID_TIMER) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


BEEPER2.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP05\BEEPER2.C

/*----------------------------------------
   BEEPER2.C -- Timer Demo Program No. 2
                (c) Charles Petzold, 1990
  ----------------------------------------*/

#include <windows.h>
#define ID_TIMER    1

long FAR PASCAL WndProc   (HWND, WORD, WORD, LONG) ;
WORD FAR PASCAL TimerProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Beeper2" ;
     FARPROC     lpfnTimerProc ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Beeper2 Timer Demo",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     lpfnTimerProc = MakeProcInstance (TimerProc, hInstance) ;

     while (!SetTimer (hwnd, ID_TIMER, 1000, lpfnTimerProc))
          if (IDCANCEL == MessageBox (hwnd,
                              "Too many clocks or timers!", szAppName,
                              MB_ICONEXCLAMATION | MB_RETRYCANCEL))
               return FALSE ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     switch (message)
          {
          case WM_DESTROY:
               KillTimer (hwnd, ID_TIMER) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

WORD FAR PASCAL TimerProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL fFlipFlop = FALSE ;
     HBRUSH      hBrush ;
     HDC         hdc ;
     RECT        rc ;

     MessageBeep (0) ;
     fFlipFlop = !fFlipFlop ;

     GetClientRect (hwnd, &rc) ;

     hdc = GetDC (hwnd) ;
     hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ;

     FillRect (hdc, &rc, hBrush) ;
     ReleaseDC (hwnd, hdc) ;
     DeleteObject (hBrush) ;

     return 0 ;
     }


BITLIB.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP19\BITLIB.C

/*--------------------------------------------------------------
   BITLIB.C -- Code entry point for BITLIB dynamic link library
               (c) Charles Petzold,  1990
  --------------------------------------------------------------*/
#include <windows.h>

int FAR PASCAL LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize,
                        LPSTR lpszCmdLine)
     {
     if (wHeapSize > 0)
          UnlockData (0) ;

     return 1 ;
     }


BLOWUP1.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP04\BLOWUP1.C

/*------------------------------------------------
   BLOWUP1.C -- Screen Capture Mouse Demo Program
               (c) Charles Petzold, 1990
  ------------------------------------------------*/

#include <windows.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "BlowUp1" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Blow-Up Mouse Demo",
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

void InvertBlock (HWND hwnd, POINT ptBeg, POINT ptEnd)
     {
     HDC hdc ;

     hdc = CreateDC ("DISPLAY", NULL, NULL, NULL) ;
     ClientToScreen (hwnd, &ptBeg) ;
     ClientToScreen (hwnd, &ptEnd) ;
     PatBlt (hdc, ptBeg.x, ptBeg.y, ptEnd.x - ptBeg.x, ptEnd.y - ptBeg.y,
             DSTINVERT) ;
     DeleteDC (hdc) ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL  fCapturing, fBlocking ;
     static POINT ptBeg, ptEnd ;
     HDC          hdc ;
     RECT         rect ;

     switch (message)
          {
          case WM_LBUTTONDOWN:
               if (!fCapturing)
                    {
                    fCapturing = TRUE ;
                    SetCapture (hwnd) ;
                    SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
                    }
               else if (!fBlocking)
                    {
                    fBlocking = TRUE ;
                    ptBeg = MAKEPOINT (lParam) ;
                    }
               return 0 ;

          case WM_MOUSEMOVE:
               if (fBlocking)
                    {
                    ptEnd = MAKEPOINT (lParam) ;
                    InvertBlock (hwnd, ptBeg, ptEnd) ;
                    InvertBlock (hwnd, ptBeg, ptEnd) ;
                    }
               return 0 ;

          case WM_LBUTTONUP:
               if (fBlocking)
                    {
                    fCapturing = fBlocking = FALSE ;
                    ptEnd = MAKEPOINT (lParam) ;
                    SetCursor (LoadCursor (NULL, IDC_WAIT)) ;

                    hdc = GetDC (hwnd) ;
                    GetClientRect (hwnd, &rect) ;
                    StretchBlt (hdc, 0, 0, rect.right, rect.bottom,
                                hdc, ptBeg.x, ptBeg.y,
                                ptEnd.x - ptBeg.x, ptEnd.y - ptBeg.y,
                                SRCCOPY) ;

                    ReleaseDC (hwnd, hdc) ;
                    SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
                    ReleaseCapture () ;
                    }
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


BLOWUP2.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP16\BLOWUP2.C

/*------------------------------------------------
   BLOWUP2.C -- Capture Screen Image to Clipboard
                (c) Charles Petzold, 1990
  ------------------------------------------------*/

#include <windows.h>
#include <stdlib.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "Blowup2" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, szAppName,
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

void InvertBlock (HWND hwnd, POINT org, POINT len)
     {
     HDC   hdc ;

     hdc = CreateDC ("DISPLAY", NULL, NULL, NULL) ;
     ClientToScreen (hwnd, &org) ;
     PatBlt (hdc, org.x, org.y, len.x, len.y, DSTINVERT) ;
     DeleteDC (hdc) ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL  bCapturing, bBlocking ;
     static POINT org, len ;
     static short cxClient, cyClient ;
     BITMAP       bm ;
     HDC          hdc, hdcMem ;
     HBITMAP      hBitmap ;
     PAINTSTRUCT  ps ;

     switch (message)
          {
          case WM_SIZE:
               cxClient = LOWORD (lParam) ;
               cyClient = HIWORD (lParam) ;
               return 0 ;

          case WM_LBUTTONDOWN:
               if (!bCapturing)
                    {
                    bCapturing = TRUE ;
                    SetCapture (hwnd) ;
                    SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
                    }
               else if (!bBlocking)
                    {
                    bBlocking = TRUE ;
                    org = MAKEPOINT (lParam) ;
                    }
               return 0 ;

          case WM_MOUSEMOVE:
               if (bCapturing)
                    SetCursor (LoadCursor (NULL, IDC_CROSS)) ;

               if (bBlocking)
                    {
                    len = MAKEPOINT (lParam) ;
                    len.x -= org.x ;
                    len.y -= org.y ;

                    InvertBlock (hwnd, org, len) ;
                    InvertBlock (hwnd, org, len) ;
                    }
               return 0 ;

          case WM_LBUTTONUP:
               if (!bBlocking)
                    break ;

               bCapturing = bBlocking = FALSE ;
               SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
               ReleaseCapture () ;

               if (len.x == 0 || len.y == 0)
                    break ;

               hdc = GetDC (hwnd) ;
               hdcMem = CreateCompatibleDC (hdc) ;
               hBitmap = CreateCompatibleBitmap (hdc,
                                    abs (len.x), abs (len.y)) ;
               if (hBitmap)
                    {
                    SelectObject (hdcMem, hBitmap) ;
                    StretchBlt (hdcMem, 0, 0, abs (len.x), abs (len.y),
                         hdc, org.x, org.y, len.x, len.y, SRCCOPY) ;

                    OpenClipboard (hwnd) ;
                    EmptyClipboard () ;
                    SetClipboardData (CF_BITMAP, hBitmap) ;
                    CloseClipboard () ;

                    InvalidateRect (hwnd, NULL, TRUE) ;
                    }
               else
                    MessageBeep (0) ;

               DeleteDC (hdcMem) ;
               ReleaseDC (hwnd, hdc) ;
               return 0 ;

          case WM_PAINT:
               InvalidateRect (hwnd, NULL, TRUE) ;
               hdc = BeginPaint (hwnd, &ps) ;
               OpenClipboard (hwnd) ;

               if (hBitmap = GetClipboardData (CF_BITMAP))
                    {
                    SetCursor (LoadCursor (NULL, IDC_WAIT)) ;

                    hdcMem = CreateCompatibleDC (hdc) ;
                    SelectObject (hdcMem, hBitmap) ;
                    GetObject (hBitmap, sizeof (BITMAP), (LPSTR) &bm) ;

                    SetStretchBltMode (hdc, COLORONCOLOR) ;
                    StretchBlt (hdc, 0, 0, cxClient, cyClient,
                                hdcMem, 0, 0, bm.bmWidth, bm.bmHeight,
                                                  SRCCOPY) ;

                    SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
                    DeleteDC (hdcMem) ;
                    }

               CloseClipboard () ;
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


BOUNCE.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP13\BOUNCE.C

/*---------------------------------------
   BOUNCE.C -- Bouncing Ball Program
               (c) Charles Petzold, 1990
  ---------------------------------------*/

#include <windows.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "Bounce" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Bouncing Ball",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     if (!SetTimer (hwnd, 1, 50, NULL))
          {
          MessageBox (hwnd, "Too many clocks or timers!",
                      szAppName, MB_ICONEXCLAMATION | MB_OK) ;
          return FALSE ;
          }

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HANDLE hBitmap ;
     static short  cxClient, cyClient, xCenter, yCenter, cxTotal, cyTotal,
                   cxRadius, cyRadius, cxMove, cyMove, xPixel, yPixel ;
     HBRUSH        hBrush ;
     HDC           hdc, hdcMem ;
     short         nScale ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;
               xPixel = GetDeviceCaps (hdc, ASPECTX) ;
               yPixel = GetDeviceCaps (hdc, ASPECTY) ;
               ReleaseDC (hwnd, hdc) ;
               return 0 ;

          case WM_SIZE:
               xCenter = (cxClient = LOWORD (lParam)) / 2 ;
               yCenter = (cyClient = HIWORD (lParam)) / 2 ;

               nScale = min (cxClient * xPixel, cyClient * yPixel) / 16 ;

               cxRadius = nScale / xPixel ;
               cyRadius = nScale / yPixel ;

               cxMove = max (1, cxRadius / 4) ;
               cyMove = max (1, cyRadius / 4) ;

               cxTotal = 2 * (cxRadius + cxMove) ;
               cyTotal = 2 * (cyRadius + cyMove) ;

               if (hBitmap)
                    DeleteObject (hBitmap) ;

               hdc = GetDC (hwnd) ;
               hdcMem = CreateCompatibleDC (hdc) ;
               hBitmap = CreateCompatibleBitmap (hdc, cxTotal, cyTotal) ;
               ReleaseDC (hwnd, hdc) ;

               SelectObject (hdcMem, hBitmap) ;
               Rectangle (hdcMem, -1, -1, cxTotal + 1, cyTotal + 1) ;

               hBrush = CreateHatchBrush (HS_DIAGCROSS, 0L) ;
               SelectObject (hdcMem, hBrush) ;
               SetBkColor (hdcMem, RGB (255, 0, 255)) ;
               Ellipse (hdcMem, cxMove, cyMove, cxTotal - cxMove,
                                                cyTotal - cyMove) ;
               DeleteDC (hdcMem) ;
               DeleteObject (hBrush) ;
               return 0 ;

          case WM_TIMER:
               if (!hBitmap)
                    break ;

               hdc = GetDC (hwnd) ;
               hdcMem = CreateCompatibleDC (hdc) ;
               SelectObject (hdcMem, hBitmap) ;

               BitBlt (hdc, xCenter - cxTotal / 2,
                            yCenter - cyTotal / 2, cxTotal, cyTotal,
                       hdcMem, 0, 0, SRCCOPY) ;

               ReleaseDC (hwnd, hdc) ;
               DeleteDC (hdcMem) ;

               xCenter += cxMove ;
               yCenter += cyMove ;

               if ((xCenter + cxRadius >= cxClient) ||
                   (xCenter - cxRadius <= 0))
                         cxMove = -cxMove ;

               if ((yCenter + cyRadius >= cyClient) ||
                   (yCenter - cyRadius <= 0))
                         cyMove = -cyMove ;
               return 0 ;

          case WM_DESTROY:
               if (hBitmap)
                    DeleteObject (hBitmap) ;

               KillTimer (hwnd, 1) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


BTNLOOK.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP06\BTNLOOK.C

/*----------------------------------------
   BTNLOOK.C -- Button Look Program
                (c) Charles Petzold, 1990
  ----------------------------------------*/

#include <windows.h>
#include <stdio.h>

struct
     {
     long style ;
     char *text ;
     }
     button[] =
     {
     BS_PUSHBUTTON,      "PUSHBUTTON",
     BS_DEFPUSHBUTTON,   "DEFPUSHBUTTON",
     BS_CHECKBOX,        "CHECKBOX",
     BS_AUTOCHECKBOX,    "AUTOCHECKBOX",
     BS_RADIOBUTTON,     "RADIOBUTTON",
     BS_3STATE,          "3STATE",
     BS_AUTO3STATE,      "AUTO3STATE",
     BS_GROUPBOX,        "GROUPBOX",
     BS_USERBUTTON,      "USERBUTTON",
     BS_AUTORADIOBUTTON, "AUTORADIO",
     BS_PUSHBOX,         "PUSHBOX"
     } ;

#define NUM (sizeof button / sizeof button [0])

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "BtnLook" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Button Look",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static char  szPrm []    = "wParam       LOWORD(lParam)  HIWORD(lParam)"
                  szTop []    = "Control ID   Window Handle   Notification",
                  szUnd []    = "__________   _____________   ____________",
                  szFormat [] = " %5u           %4X          %5u",
                  szBuffer [50] ;
     static HWND  hwndButton [NUM] ;
     static RECT  rect ;
     static int   cxChar, cyChar ;
     HDC          hdc ;
     PAINTSTRUCT  ps ;
     int          i ;
     TEXTMETRIC   tm ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
               GetTextMetrics (hdc, &tm) ;
               cxChar = tm.tmAveCharWidth ;
               cyChar = tm.tmHeight + tm.tmExternalLeading ;
               ReleaseDC (hwnd, hdc) ;

               for (i = 0 ; i < NUM ; i++)
                    hwndButton [i] = CreateWindow ("button", button[i].text,
                              WS_CHILD | WS_VISIBLE | button[i].style,
                              cxChar, cyChar * (1 + 2 * i),
                              20 * cxChar, 7 * cyChar / 4,
                              hwnd, i,
                              ((LPCREATESTRUCT) lParam) -> hInstance, NULL) ;
               return 0 ;

          case WM_SIZE:
               rect.left   = 24 * cxChar ;
               rect.top    =  3 * cyChar ;
               rect.right  = LOWORD (lParam) ;
               rect.bottom = HIWORD (lParam) ;
               return 0 ;

          case WM_PAINT:
               InvalidateRect (hwnd, &rect, TRUE) ;

               hdc = BeginPaint (hwnd, &ps) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
               SetBkMode (hdc, TRANSPARENT) ;

               TextOut (hdc, 24 * cxChar, 1 * cyChar, szPrm, sizeof szPrm - 1
               TextOut (hdc, 24 * cxChar, 2 * cyChar, szTop, sizeof szTop - 1
               TextOut (hdc, 24 * cxChar, 2 * cyChar, szUnd, sizeof szUnd - 1

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_COMMAND:
               ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ;
               hdc = GetDC (hwnd) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

               TextOut (hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1)
                        szBuffer, sprintf (szBuffer, szFormat, wParam,
                        LOWORD (lParam), HIWORD (lParam))) ;

               ReleaseDC (hwnd, hdc) ;
               ValidateRect (hwnd, NULL) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


CHECKER1.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP04\CHECKER1.C

/*-------------------------------------------------
   CHECKER1.C -- Mouse Hit-Test Demo Program No. 1
                 (c) Charles Petzold, 1990
  -------------------------------------------------*/

#include <windows.h>
#define DIVISIONS 5

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR  lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Checker1" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Checker1 Mouse Hit-Test Demo",
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL  fState[DIVISIONS][DIVISIONS] ;
     static short cxBlock, cyBlock ;
     HDC          hdc ;
     PAINTSTRUCT  ps ;
     RECT         rect ;
     short        x, y ;

     switch (message)
          {
          case WM_SIZE:
               cxBlock = LOWORD (lParam) / DIVISIONS ;
               cyBlock = HIWORD (lParam) / DIVISIONS ;
               return 0 ;

          case WM_LBUTTONDOWN:
               x = LOWORD (lParam) / cxBlock ;
               y = HIWORD (lParam) / cyBlock ;

               if (x < DIVISIONS && y < DIVISIONS)
                    {
                    fState [x][y] ^= 1 ;

                    rect.left   = x * cxBlock ;
                    rect.top    = y * cyBlock ;
                    rect.right  = (x + 1) * cxBlock ;
                    rect.bottom = (y + 1) * cyBlock ;

                    InvalidateRect (hwnd, &rect, FALSE) ;
                    }
               else
                    MessageBeep (0) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               for (x = 0 ; x < DIVISIONS ; x++)
                    for (y = 0 ; y < DIVISIONS ; y++)
                         {
                         Rectangle (hdc, x * cxBlock, y * cyBlock,
                                   (x + 1) * cxBlock, (y + 1) * cyBlock) ;

                         if (fState [x][y])
                              {
                              MoveTo (hdc,  x    * cxBlock,  y    * cyBlock)
                              LineTo (hdc, (x+1) * cxBlock, (y+1) * cyBlock)
                              MoveTo (hdc,  x    * cxBlock, (y+1) * cyBlock)
                              LineTo (hdc, (x+1) * cxBlock,  y    * cyBlock)
                              }
                         }
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


CHECKER2.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP04\CHECKER2.C

/*-------------------------------------------------
   CHECKER2.C -- Mouse Hit-Test Demo Program No. 2
                 (c) Charles Petzold, 1990
  -------------------------------------------------*/

#include <windows.h>
#define DIVISIONS 5

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Checker2" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Checker2 Mouse Hit-Test Demo",
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL  fState[DIVISIONS][DIVISIONS] ;
     static short cxBlock, cyBlock ;
     HDC          hdc ;
     PAINTSTRUCT  ps ;
     POINT        point ;
     RECT         rect ;
     short        x, y ;

     switch (message)
          {
          case WM_SIZE:
               cxBlock = LOWORD (lParam) / DIVISIONS ;
               cyBlock = HIWORD (lParam) / DIVISIONS ;
               return 0 ;

          case WM_SETFOCUS:
               ShowCursor (TRUE) ;
               return 0 ;

          case WM_KILLFOCUS:
               ShowCursor (FALSE) ;
               return 0 ;

          case WM_KEYDOWN:
               GetCursorPos (&point) ;
               ScreenToClient (hwnd, &point) ;

               x = max (0, min (DIVISIONS - 1, point.x / cxBlock)) ;
               y = max (0, min (DIVISIONS - 1, point.y / cyBlock)) ;

               switch (wParam)
                    {
                    case VK_UP:
                         y -- ;
                         break ;

                    case VK_DOWN:
                         y ++ ;
                         break ;

                    case VK_LEFT:
                         x -- ;
                         break ;

                    case VK_RIGHT:
                         x ++ ;
                         break ;

                    case VK_HOME:
                         x = y = 0 ;
                         break ;

                    case VK_END:
                         x = y = DIVISIONS - 1 ;
                         break ;

                    case VK_RETURN:
                    case VK_SPACE:
                         SendMessage (hwnd, WM_LBUTTONDOWN, MK_LBUTTON,
                                   MAKELONG (x * cxBlock, y * cyBlock)) ;
                         break ;
                    }
               x = (x + DIVISIONS) % DIVISIONS ;
               y = (y + DIVISIONS) % DIVISIONS ;

               point.x = x * cxBlock + cxBlock / 2 ;
               point.y = y * cyBlock + cyBlock / 2 ;

               ClientToScreen (hwnd, &point) ;
               SetCursorPos (point.x, point.y) ;
               return 0 ;

          case WM_LBUTTONDOWN:
               x = LOWORD (lParam) / cxBlock ;
               y = HIWORD (lParam) / cyBlock ;

               if (x < DIVISIONS && y < DIVISIONS)
                    {
                    fState[x][y] ^= 1 ;

                    rect.left   = x * cxBlock ;
                    rect.top    = y * cyBlock ;
                    rect.right  = (x + 1) * cxBlock ;
                    rect.bottom = (y + 1) * cyBlock ;

                    InvalidateRect (hwnd, &rect, FALSE) ;
                    }
               else
                    MessageBeep (0) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               for (x = 0 ; x < DIVISIONS ; x++)
                    for (y = 0 ; y < DIVISIONS ; y++)
                         {
                         Rectangle (hdc, x * cxBlock, y * cyBlock,
                                   (x + 1) * cxBlock, (y + 1) * cyBlock) ;

                         if (fState [x][y])
                              {
                              MoveTo (hdc,  x    * cxBlock,  y    * cyBlock)
                              LineTo (hdc, (x+1) * cxBlock, (y+1) * cyBlock)
                              MoveTo (hdc,  x    * cxBlock, (y+1) * cyBlock)
                              LineTo (hdc, (x+1) * cxBlock,  y    * cyBlock)
                              }
                         }
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


CHECKER3.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP04\CHECKER3.C

/*-------------------------------------------------
   CHECKER3.C -- Mouse Hit-Test Demo Program No. 3
                 (c) Charles Petzold, 1990
  -------------------------------------------------*/

#include <windows.h>
#define DIVISIONS 5

long FAR PASCAL WndProc      (HWND, WORD, WORD, LONG) ;
long FAR PASCAL ChildWndProc (HWND, WORD, WORD, LONG) ;

char szChildClass[] = "Checker3_Child" ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Checker3" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;

          wndclass.lpfnWndProc   = ChildWndProc ;
          wndclass.cbWndExtra    = sizeof (WORD) ;
          wndclass.hIcon         = NULL ;
          wndclass.lpszClassName = szChildClass ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Checker3 Mouse Hit-Test Demo",
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HWND hwndChild [DIVISIONS] [DIVISIONS] ;
     short       cxBlock, cyBlock, x, y ;

     switch (message)
          {
          case WM_CREATE:
               for (x = 0 ; x < DIVISIONS ; x++)
                    for (y = 0 ; y < DIVISIONS ; y++)
                         {
                         hwndChild [x][y] = CreateWindow (szChildClass, NULL,
                              WS_CHILDWINDOW | WS_VISIBLE,
                              0, 0, 0, 0,
                              hwnd, y << 8 | x,
                              GetWindowWord (hwnd, GWW_HINSTANCE), NULL) ;
                         }
               return 0 ;

          case WM_SIZE:
               cxBlock = LOWORD (lParam) / DIVISIONS ;
               cyBlock = HIWORD (lParam) / DIVISIONS ;

               for (x = 0 ; x < DIVISIONS ; x++)
                    for (y = 0 ; y < DIVISIONS ; y++)
                         MoveWindow (hwndChild [x][y],
                              x * cxBlock, y * cyBlock,
                              cxBlock, cyBlock, TRUE) ;
               return 0 ;

          case WM_LBUTTONDOWN:
               MessageBeep (0) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

long FAR PASCAL ChildWndProc (HWND hwnd, WORD message, WORD wParam, LONG lPar
     {
     HDC         hdc ;
     PAINTSTRUCT ps ;
     RECT        rect ;

     switch (message)
          {
          case WM_CREATE:
               SetWindowWord (hwnd, 0, 0) ;       // on/off flag
               return 0 ;

          case WM_LBUTTONDOWN:
               SetWindowWord (hwnd, 0, 1 ^ GetWindowWord (hwnd, 0)) ;
               InvalidateRect (hwnd, NULL, FALSE) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               GetClientRect (hwnd, &rect) ;
               Rectangle (hdc, 0, 0, rect.right, rect.bottom) ;

               if (GetWindowWord (hwnd, 0))
                    {
                    MoveTo (hdc, 0,          0) ;
                    LineTo (hdc, rect.right, rect.bottom) ;
                    MoveTo (hdc, 0,          rect.bottom) ;
                    LineTo (hdc, rect.right, 0) ;
                    }

               EndPaint (hwnd, &ps) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


CLIPVIEW.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP16\CLIPVIEW.C

/*-----------------------------------------
   CLIPVIEW.C -- Simple Clipboard Viewer
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static    char szAppName [] = "ClipView" ;
     HWND      hwnd ;
     MSG       msg ;
     WNDCLASS  wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Simple Clipboard Viewer (Text Only)",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HWND hwndNextViewer ;
     HANDLE      hGMem ;
     HDC         hdc ;
     LPSTR       lpGMem ;
     PAINTSTRUCT ps ;
     RECT        rect ;

     switch (message)
          {
          case WM_CREATE:
               hwndNextViewer = SetClipboardViewer (hwnd) ;
               return 0 ;

          case WM_CHANGECBCHAIN :
               if (wParam == hwndNextViewer)
                    hwndNextViewer = LOWORD (lParam) ;

               else if (hwndNextViewer)
                    SendMessage (hwndNextViewer, message, wParam, lParam) ;

               return 0 ;

          case WM_DRAWCLIPBOARD :
               if (hwndNextViewer)
                    SendMessage (hwndNextViewer, message, wParam, lParam) ;

               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;
               GetClientRect (hwnd, &rect) ;
               OpenClipboard (hwnd) ;

               if (hGMem = GetClipboardData (CF_TEXT))
                    {
                    lpGMem = GlobalLock (hGMem) ;
                    DrawText (hdc, lpGMem, -1, &rect, DT_EXPANDTABS) ;
                    GlobalUnlock (hGMem) ;
                    }

               CloseClipboard () ;
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               ChangeClipboardChain (hwnd, hwndNextViewer) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


CLOVER.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP12\CLOVER.C

/*--------------------------------------------------
   CLOVER.C -- Clover Drawing Program using Regions
               (c) Charles Petzold, 1990
  --------------------------------------------------*/

#include <windows.h>
#include <math.h>
#define TWO_PI (2.0 * 3.14159)

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Clover" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Draw a Clover",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HRGN  hRgnClip ;
     static short cxClient, cyClient ;
     double       fAngle, fRadius ;
     HCURSOR      hCursor ;
     HDC          hdc ;
     HRGN         hRgnTemp [6] ;
     PAINTSTRUCT  ps ;
     short        i ;

     switch (message)
          {
          case WM_SIZE:
               cxClient = LOWORD (lParam) ;
               cyClient = HIWORD (lParam) ;

               hCursor = SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
               ShowCursor (TRUE) ;

               if (hRgnClip)
                    DeleteObject (hRgnClip) ;

               hRgnTemp [0] = CreateEllipticRgn (0, cyClient / 3,
                                        cxClient / 2, 2 * cyClient / 3) ;
               hRgnTemp [1] = CreateEllipticRgn (cxClient / 2, cyClient / 3,
                                        cxClient, 2 * cyClient / 3) ;
               hRgnTemp [2] = CreateEllipticRgn (cxClient / 3, 0,
                                        2 * cxClient / 3, cyClient / 2) ;
               hRgnTemp [3] = CreateEllipticRgn (cxClient / 3, cyClient / 2,
                                        2 * cxClient / 3, cyClient) ;
               hRgnTemp [4] = CreateRectRgn (0, 0, 1, 1) ;
               hRgnTemp [5] = CreateRectRgn (0, 0, 1, 1) ;
               hRgnClip     = CreateRectRgn (0, 0, 1, 1) ;

               CombineRgn (hRgnTemp [4], hRgnTemp [0], hRgnTemp [1], RGN_OR)
               CombineRgn (hRgnTemp [5], hRgnTemp [2], hRgnTemp [3], RGN_OR)
               CombineRgn (hRgnClip,     hRgnTemp [4], hRgnTemp [5], RGN_XOR)

               for (i = 0 ; i < 6 ; i++)
                    DeleteObject (hRgnTemp [i]) ;

               SetCursor (hCursor) ;
               ShowCursor (FALSE) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               SetViewportOrg (hdc, cxClient / 2, cyClient / 2) ;
               SelectClipRgn (hdc, hRgnClip) ;

               fRadius = hypot (cxClient / 2.0, cyClient / 2.0) ;

               for (fAngle = 0.0 ; fAngle < TWO_PI ; fAngle += TWO_PI / 360)
                    {
                    MoveTo (hdc, 0, 0) ;
                    LineTo (hdc, (short) ( fRadius * cos (fAngle) + 0.5),
                                 (short) (-fRadius * sin (fAngle) + 0.5)) ;
                    }
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               DeleteObject (hRgnClip) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


COLORS1.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP06\COLORS1.C

/*----------------------------------------
   COLORS1.C -- Colors Using Scroll Bars
                (c) Charles Petzold, 1990
  ----------------------------------------*/

#include <windows.h>
#include <stdlib.h>

long FAR PASCAL WndProc    (HWND, WORD, WORD, LONG) ;
long FAR PASCAL ScrollProc (HWND, WORD, WORD, LONG) ;

FARPROC lpfnOldScr[3] ;
HWND    hwndScrol[3], hwndLabel[3], hwndValue[3], hwndRect ;
short   color[3], nFocus ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Colors1" ;
     static char *szColorLabel[] = { "Red", "Green", "Blue" } ;
     FARPROC     lpfnScrollProc ;
     HWND        hwnd ;
     MSG         msg;
     short       n ;
     WNDCLASS    wndclass ;

     if (hPrevInstance)
          return FALSE ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = NULL ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = CreateSolidBrush (0L) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     RegisterClass (&wndclass) ;

     hwnd = CreateWindow (szAppName, "Color Scroll",
                          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     hwndRect = CreateWindow ("static", NULL,
                              WS_CHILD | WS_VISIBLE | SS_WHITERECT,
                              0, 0, 0, 0,
                              hwnd, 9, hInstance, NULL) ;

     lpfnScrollProc = MakeProcInstance ((FARPROC) ScrollProc, hInstance) ;

     for (n = 0 ; n < 3 ; n++)
          {
          hwndScrol[n] = CreateWindow ("scrollbar", NULL,
                              WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_VERT,
                              0, 0, 0, 0,
                              hwnd, n, hInstance, NULL) ;

          hwndLabel[n] = CreateWindow ("static", szColorLabel[n],
                              WS_CHILD | WS_VISIBLE | SS_CENTER,
                              0, 0, 0, 0,
                              hwnd, n + 3, hInstance, NULL) ;

          hwndValue[n] = CreateWindow ("static", "0",
                              WS_CHILD | WS_VISIBLE | SS_CENTER,
                              0, 0, 0, 0,
                              hwnd, n + 6, hInstance, NULL) ;

          lpfnOldScr[n] = (FARPROC) GetWindowLong (hwndScrol[n], GWL_WNDPROC)
          SetWindowLong (hwndScrol[n], GWL_WNDPROC, (LONG) lpfnScrollProc) ;

          SetScrollRange (hwndScrol[n], SB_CTL, 0, 255, FALSE) ;
          SetScrollPos   (hwndScrol[n], SB_CTL, 0, FALSE) ;
          }

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd);

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HBRUSH hBrush[3] ;
     char          szbuffer[10] ;
     HDC           hdc ;
     POINT         point ;
     short         n, cxClient, cyClient, cyChar ;
     TEXTMETRIC    tm ;

     switch (message)
          {
          case WM_CREATE :
               hBrush[0] = CreateSolidBrush (RGB (255, 0, 0)) ;
               hBrush[1] = CreateSolidBrush (RGB (0, 255, 0)) ;
               hBrush[2] = CreateSolidBrush (RGB (0, 0, 255)) ;
               return 0 ;

          case WM_SIZE :
               cxClient = LOWORD (lParam) ;
               cyClient = HIWORD (lParam) ;

               hdc = GetDC (hwnd) ;
               GetTextMetrics (hdc, &tm) ;
               cyChar = tm.tmHeight ;
               ReleaseDC (hwnd, hdc) ;

               MoveWindow (hwndRect, 0, 0, cxClient / 2, cyClient, TRUE) ;

               for (n = 0 ; n < 3 ; n++)
                    {
                    MoveWindow (hwndScrol[n],
                         (2 * n + 1) * cxClient / 14, 2 * cyChar,
                         cxClient / 14, cyClient - 4 * cyChar, TRUE) ;

                    MoveWindow (hwndLabel[n],
                         (4 * n + 1) * cxClient / 28, cyChar / 2,
                         cxClient / 7, cyChar, TRUE) ;

                    MoveWindow (hwndValue[n],
                         (4 * n + 1) * cxClient / 28, cyClient - 3 * cyChar /
                         cxClient / 7, cyChar, TRUE) ;
                    }
               SetFocus (hwnd) ;
               return 0 ;

          case WM_SETFOCUS:
               SetFocus (hwndScrol[nFocus]) ;
               return 0 ;

          case WM_VSCROLL :
               n = GetWindowWord (HIWORD (lParam), GWW_ID) ;

               switch (wParam)
                    {
                    case SB_PAGEDOWN :
                         color[n] += 15 ;         /* fall through */
                    case SB_LINEDOWN :
                         color[n] = min (255, color[n] + 1) ;
                         break ;
                    case SB_PAGEUP :
                         color[n] -= 15 ;         /* fall through */
                    case SB_LINEUP :
                         color[n] = max (0, color[n] - 1) ;
                         break ;
                    case SB_TOP:
                         color[n] = 0 ;
                         break ;
                    case SB_BOTTOM :
                         color[n] = 255 ;
                         break ;
                    case SB_THUMBPOSITION :
                    case SB_THUMBTRACK :
                         color[n] = LOWORD (lParam) ;
                         break ;
                    default :
                         break ;
                    }
               SetScrollPos  (hwndScrol[n], SB_CTL, color[n], TRUE) ;
               SetWindowText (hwndValue[n], itoa (color[n], szbuffer, 10)) ;

               DeleteObject (GetClassWord (hwnd, GCW_HBRBACKGROUND)) ;
               SetClassWord (hwnd, GCW_HBRBACKGROUND,
                    CreateSolidBrush (RGB (color[0], color[1], color[2]))) ;

               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_CTLCOLOR:
               if (HIWORD (lParam) == CTLCOLOR_SCROLLBAR)
                    {
                    SetBkColor (wParam, GetSysColor (COLOR_CAPTIONTEXT)) ;
                    SetTextColor (wParam, GetSysColor (COLOR_WINDOWFRAME)) ;

                    n = GetWindowWord (LOWORD (lParam), GWW_ID) ;
                    point.x = point.y = 0 ;
                    ClientToScreen (hwnd, &point) ;
                    UnrealizeObject (hBrush[n]) ;
                    SetBrushOrg (wParam, point.x, point.y) ;
                    return ((DWORD) hBrush[n]) ;
                    }
               break ;

          case WM_DESTROY:
               DeleteObject (GetClassWord (hwnd, GCW_HBRBACKGROUND)) ;
               for (n = 0 ; n < 3 ; DeleteObject (hBrush [n++])) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

long FAR PASCAL ScrollProc (HWND hwnd, WORD message, WORD wParam, LONG lParam
     {
     short n = GetWindowWord (hwnd, GWW_ID) ;

     switch (message)
          {
          case WM_KEYDOWN:
               if (wParam == VK_TAB)
                    SetFocus (hwndScrol[(n +
                         (GetKeyState (VK_SHIFT) < 0 ? 2 : 1)) % 3]) ;
               break ;

          case WM_SETFOCUS:
               nFocus = n ;
               break ;
          }
     return CallWindowProc (lpfnOldScr[n], hwnd, message, wParam, lParam) ;
     }


COLORS2.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP10\COLORS2.C

/*--------------------------------------------------------
   COLORS2.C -- Version using Modeless Dialog Box Version
                (c) Charles Petzold, 1990
  --------------------------------------------------------*/

#include <windows.h>

long FAR PASCAL WndProc     (HWND, WORD, WORD, LONG) ;
BOOL FAR PASCAL ColorScrDlg (HWND, WORD, WORD, LONG) ;

HWND hDlgModeless ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Colors2" ;
     HWND        hwnd ;
     MSG         msg;
     WNDCLASS    wndclass ;

     if (hPrevInstance)
          return FALSE ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = NULL ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = CreateSolidBrush (0L) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     RegisterClass (&wndclass) ;

     hwnd = CreateWindow (szAppName, "Color Scroll",
                          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd);

     hDlgModeless = CreateDialog (hInstance, "ColorScrDlg", hwnd,
                         MakeProcInstance (ColorScrDlg, hInstance)) ;

     while (GetMessage (&msg, NULL, 0, 0))
          {
          if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
               {
               TranslateMessage (&msg) ;
               DispatchMessage  (&msg) ;
               }
          }
     return msg.wParam ;
     }

BOOL FAR PASCAL ColorScrDlg (HWND hDlg, WORD message, WORD wParam, LONG lPara
     {
     static short color [3] ;
     HWND         hwndParent, hCtrl ;
     short        nCtrlID, nIndex ;

     switch (message)
          {
          case WM_INITDIALOG:
               for (nCtrlID = 10 ; nCtrlID < 13 ; nCtrlID++)
                    {
                    hCtrl = GetDlgItem (hDlg, nCtrlID) ;
                    SetScrollRange (hCtrl, SB_CTL, 0, 255, FALSE) ;
                    SetScrollPos   (hCtrl, SB_CTL, 0, FALSE) ;
                    }
               return TRUE ;

          case WM_VSCROLL :
               hCtrl   = HIWORD (lParam) ;
               nCtrlID = GetWindowWord (hCtrl, GWW_ID) ;
               nIndex  = nCtrlID - 10 ;
               hwndParent = GetParent (hDlg) ;

               switch (wParam)
                    {
                    case SB_PAGEDOWN :
                         color [nIndex] += 15 ;        // fall through
                    case SB_LINEDOWN :
                         color [nIndex] = min (255, color [nIndex] + 1) ;
                         break ;
                    case SB_PAGEUP :
                         color [nIndex] -= 15 ;        // fall through
                    case SB_LINEUP :
                         color [nIndex] = max (0, color [nIndex] - 1) ;
                         break ;
                    case SB_TOP:
                         color [nIndex] = 0 ;
                         break ;
                    case SB_BOTTOM :
                         color [nIndex] = 255 ;
                         break ;
                    case SB_THUMBPOSITION :
                    case SB_THUMBTRACK :
                         color [nIndex] = LOWORD (lParam) ;
                         break ;
                    default :
                         return FALSE ;
                    }
               SetScrollPos  (hCtrl, SB_CTL,      color [nIndex], TRUE) ;
               SetDlgItemInt (hDlg,  nCtrlID + 3, color [nIndex], FALSE) ;

               DeleteObject (GetClassWord (hwndParent, GCW_HBRBACKGROUND)) ;
               SetClassWord (hwndParent, GCW_HBRBACKGROUND,
                    CreateSolidBrush (RGB (color [0], color [1], color [2])))

               InvalidateRect (hwndParent, NULL, TRUE) ;
               return TRUE ;
          }
     return FALSE ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     switch (message)
          {
          case WM_DESTROY:
               DeleteObject (GetClassWord (hwnd, GCW_HBRBACKGROUND)) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


CONNECT.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP04\CONNECT.C

/*--------------------------------------------------
   CONNECT.C -- Connect-the-Dots Mouse Demo Program
                (c) Charles Petzold, 1990
  --------------------------------------------------*/

#include <windows.h>
#define MAXPOINTS 1000

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Connect" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Connect-the-Points Mouse Demo",
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static POINT points[MAXPOINTS] ;
     static short nCount ;
     HDC          hdc ;
     PAINTSTRUCT  ps ;
     short        i, j ;

     switch (message)
          {
          case WM_LBUTTONDOWN:
               nCount = 0 ;
               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_MOUSEMOVE:
               if (wParam & MK_LBUTTON && nCount < 1000)
                    {
                    points [nCount++] = MAKEPOINT (lParam) ;
                    hdc = GetDC (hwnd) ;
                    SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0L) ;
                    ReleaseDC (hwnd, hdc) ;
                    }
               return 0 ;

          case WM_LBUTTONUP:
               InvalidateRect (hwnd, NULL, FALSE) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               for (i = 0 ; i < nCount - 1 ; i++)
                    for (j = i ; j < nCount ; j++)
                         {
                         MoveTo (hdc, points[i].x, points[i].y) ;
                         LineTo (hdc, points[j].x, points[j].y) ;
                         }

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


DDEPOP.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP17\DDEPOP.C

/*--------------------------------------------
   DDEPOP.C -- DDE Server for Population Data
               (c) Charles Petzold, 1990
  --------------------------------------------*/

#include <windows.h>
#include <dde.h>
#include <string.h>
#include <time.h>

struct
     {
     char *szState ;
     long lPop70 ;
     long lPop80 ;
     long lPop ;
     }
     pop [] = {
              "AL",   3444354,   3894025, 0, "AK",    302583,    401851, 0,
              "AZ",   1775399,   2716598, 0, "AR",   1923322,   2286357, 0,
              "CA",  19971069,  23667764, 0, "CO",   2209596,   2889735, 0,
              "CT",   3032217,   3107564, 0, "DE",    548104,    594338, 0,
              "DC",    756668,    638432, 0, "FL",   6791418,   9746961, 0,
              "GA",   4587930,   5462982, 0, "HI",    769913,    964691, 0,
              "ID",    713015,    944127, 0, "IL",  11110285,  11427409, 0,
              "IN",   5195392,   5490212, 0, "IA",   2825368,   2913808, 0,
              "KS",   2249071,   2364236, 0, "KY",   3220711,   3660324, 0,
              "LA",   3644637,   4206116, 0, "ME",    993722,   1125043, 0,
              "MD",   3923897,   4216933, 0, "MA",   5689170,   5737093, 0,
              "MI",   8881826,   9262044, 0, "MN",   3806103,   4075970, 0,
              "MS",   2216994,   2520770, 0, "MO",   4677623,   4916762, 0,
              "MT",    694409,    786690, 0, "NE",   1485333,   1569825, 0,
              "NV",    488738,    800508, 0, "NH",    737681,    920610, 0,
              "NJ",   7171112,   7365011, 0, "NM",   1017055,   1303302, 0,
              "NY",  18241391,  17558165, 0, "NC",   5084411,   5880415, 0,
              "ND",    617792,    652717, 0, "OH",  10657423,  10797603, 0,
              "OK",   2559463,   3025487, 0, "OR",   2091533,   2633156, 0,
              "PA",  11800766,  11864720, 0, "RI",    949723,    947154, 0,
              "SC",   2590713,   3120730, 0, "SD",    666257,    690768, 0,
              "TN",   3926018,   4591023, 0, "TX",  11198655,  14225513, 0,
              "UT",   1059273,   1461037, 0, "VT",    444732,    511456, 0,
              "VA",   4651448,   5346797, 0, "WA",   3413244,   4132353, 0,
              "WV",   1744237,   1950186, 0, "WI",   4417821,   4705642, 0,
              "WY",    332416,    469557, 0, "US", 203302031, 226542580, 0
              } ;

#define NUM_STATES (sizeof (pop) / sizeof (pop [0]))

typedef struct
     {
     unsigned int fAdvise:1 ;
     unsigned int fDeferUpd:1 ;
     unsigned int fAckReq:1 ;
     unsigned int dummy:13 ;
     long         lPopPrev ;
     }
     POPADVISE ;

#define ID_TIMER    1
#define DDE_TIMEOUT 3000

long FAR PASCAL WndProc         (HWND, WORD, WORD, LONG) ;
long FAR PASCAL ServerProc      (HWND, WORD, WORD, LONG) ;
BOOL FAR PASCAL TimerEnumProc   (HWND, LONG) ;
BOOL FAR PASCAL CloseEnumProc   (HWND, LONG) ;
BOOL            PostDataMessage (HWND, HWND, int, BOOL, BOOL, BOOL) ;

char   szAppName []     = "DdePop" ;
char   szServerClass [] = "DdePop.Server" ;
HANDLE hInst ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (hPrevInstance)
          return FALSE ;

     hInst = hInstance ;

               // Register window class

     wndclass.style         = 0 ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     RegisterClass (&wndclass) ;

               // Register window class for DDE Server

     wndclass.style         = 0 ;
     wndclass.lpfnWndProc   = ServerProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 2 * sizeof (WORD) ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = NULL ;
     wndclass.hCursor       = NULL ;
     wndclass.hbrBackground = NULL ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szServerClass ;

     RegisterClass (&wndclass) ;

     hwnd = CreateWindow (szAppName, "DDE Population Server",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     SendMessage (hwnd, WM_TIMER, 0, 0L) ;   // initialize 'pop' structure

     if (!SetTimer (hwnd, ID_TIMER, 5000, NULL))
          {
          MessageBox (hwnd, "Too many clocks or timers!", szAppName,
                      MB_ICONEXCLAMATION | MB_OK) ;

          return FALSE ;
          }

     ShowWindow (hwnd, SW_SHOWMINNOACTIVE) ;
     UpdateWindow (hwnd) ;

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

     KillTimer (hwnd, ID_TIMER) ;

     return msg.wParam ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static FARPROC lpTimerEnumProc, lpCloseEnumProc ;
     static char    szTopic [] = "US_Population" ;
     ATOM           aApp, aTop ;
     HWND           hwndClient, hwndServer ;
     int            i ;
     long double    ldSecsInDecade, ldSecSince1970 ;
     time_t         lSecSince1970 ;

     switch (message)
          {
          case WM_CREATE:
               lpTimerEnumProc = MakeProcInstance (TimerEnumProc, hInst) ;
               lpCloseEnumProc = MakeProcInstance (CloseEnumProc, hInst) ;
               return 0 ;

          case WM_DDE_INITIATE:

                    // wParam          -- sending window handle
                    // LOWORD (lParam) -- application atom
                    // HIWORD (lParam) -- topic atom

               hwndClient = wParam ;

               aApp = GlobalAddAtom (szAppName) ;
               aTop = GlobalAddAtom (szTopic) ;

                    // Check for matching atoms, create window, and acknowled

               if ((LOWORD (lParam) == NULL || LOWORD (lParam) == aApp) &&
                   (HIWORD (lParam) == NULL || HIWORD (lParam) == aTop))
                    {
                    hwndServer = CreateWindow (szServerClass, NULL,
                                               WS_CHILD, 0, 0, 0, 0,
                                               hwnd, NULL, hInst, NULL) ;

                    SetWindowWord (hwndServer, 0, hwndClient) ;
                    SendMessage (wParam, WM_DDE_ACK, hwndServer,
                                 MAKELONG (aApp, aTop)) ;
                    }

                    // Otherwise, delete the atoms just created

               else
                    {
                    GlobalDeleteAtom (aApp) ;
                    GlobalDeleteAtom (aTop) ;
                    }

               return 0 ;

          case WM_TIMER:
          case WM_TIMECHANGE:
               time (&lSecSince1970) ;

                    // Calculate new current populations

               ldSecSince1970 = (long double) lSecSince1970 ;
               ldSecsInDecade = (long double) 3652 * 24 * 60 * 60 ;

               for (i = 0 ; i < NUM_STATES ; i++)
                    {
                    pop[i].lPop = (long)
                         (((ldSecsInDecade - ldSecSince1970) * pop[i].lPop70
                            ldSecSince1970 * pop[i].lPop80) / ldSecsInDecade
                                   + .5) ;
                    }

                    // Notify all child windows

               EnumChildWindows (hwnd, lpTimerEnumProc, 0L) ;
               return 0 ;

          case WM_QUERYOPEN:
               return 0 ;

          case WM_CLOSE:

                    // Notify all child windows

               EnumChildWindows (hwnd, lpCloseEnumProc, 0L) ;

               break ;                  // for default processing

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

long FAR PASCAL ServerProc (HWND hwnd, WORD message, WORD wParam, LONG lParam
     {
     ATOM          aItem ;
     char          szItem [10], szPopulation [10] ;
     DDEACK        DdeAck ;
     DDEADVISE     Advise ;
     DDEADVISE FAR *lpDdeAdvise ;
     DDEDATA FAR   *lpDdeData ;
     DWORD         dwTime ;
     GLOBALHANDLE  hPopAdvise, hDdeData, hDdeAdvise, hCommands, hDdePoke ;
     int           i ;
     HWND          hwndClient ;
     MSG           msg ;
     POPADVISE FAR *lpPopAdvise ;
     WORD          cfFormat, wStatus ;

     switch (message)
          {
          case WM_CREATE:

                    // Allocate memory for POPADVISE structures

               hPopAdvise = GlobalAlloc (GHND, NUM_STATES * sizeof (POPADVISE

               if (hPopAdvise == NULL)
                    DestroyWindow (hwnd) ;
               else
                    SetWindowWord (hwnd, 2, hPopAdvise) ;

               return 0 ;

          case WM_DDE_REQUEST:

                    // wParam          -- sending window handle
                    // LOWORD (lParam) -- data format
                    // HIWORD (lParam) -- item atom

               hwndClient = wParam ;
               cfFormat   = LOWORD (lParam) ;
               aItem      = HIWORD (lParam) ;

                    // Check for matching format and data item

               if (cfFormat == CF_TEXT)
                    {
                    GlobalGetAtomName (aItem, szItem, sizeof (szItem)) ;

                    for (i = 0 ; i < NUM_STATES ; i++)
                         if (strcmp (szItem, pop[i].szState) == 0)
                              break ;

                    if (i < NUM_STATES)
                         {
                         GlobalDeleteAtom (aItem) ;
                         PostDataMessage (hwnd, hwndClient, i,
                                          FALSE, FALSE, TRUE) ;
                         return 0 ;
                         }
                    }

                    // Negative acknowledge if no match

               DdeAck.bAppReturnCode = 0 ;
               DdeAck.reserved       = 0 ;
               DdeAck.fBusy          = FALSE ;
               DdeAck.fAck           = FALSE ;

               wStatus = * (WORD *) & DdeAck ;

               if (!PostMessage (hwndClient, WM_DDE_ACK, hwnd,
                                 MAKELONG (wStatus, aItem)))
                    {
                    GlobalDeleteAtom (aItem) ;
                    }

               return 0 ;

          case WM_DDE_ADVISE:

                    // wParam          -- sending window handle
                    // LOWORD (lParam) -- DDEADVISE memory handle
                    // HIWORD (lParam) -- item atom

               hwndClient = wParam ;
               hDdeAdvise = LOWORD (lParam) ;
               aItem      = HIWORD (lParam) ;

               lpDdeAdvise = (DDEADVISE FAR *) GlobalLock (hDdeAdvise) ;

                    // Check for matching format and data item

               if (lpDdeAdvise->cfFormat == CF_TEXT)
                    {
                    GlobalGetAtomName (aItem, szItem, sizeof (szItem)) ;

                    for (i = 0 ; i < NUM_STATES ; i++)
                         if (strcmp (szItem, pop[i].szState) == 0)
                              break ;

                         // Fill in the POPADVISE structure and acknowledge

                    if (i < NUM_STATES)
                         {
                         hPopAdvise = GetWindowWord (hwnd, 2) ;
                         lpPopAdvise = (POPADVISE FAR *)
                                              GlobalLock (hPopAdvise) ;

                         lpPopAdvise[i].fAdvise   = TRUE ;
                         lpPopAdvise[i].fDeferUpd = lpDdeAdvise->fDeferUpd ;
                         lpPopAdvise[i].fAckReq   = lpDdeAdvise->fAckReq ;
                         lpPopAdvise[i].lPopPrev  = pop[i].lPop ;

                         GlobalUnlock (hDdeAdvise) ;
                         GlobalFree (hDdeAdvise) ;

                         DdeAck.bAppReturnCode = 0 ;
                         DdeAck.reserved       = 0 ;
                         DdeAck.fBusy          = FALSE ;
                         DdeAck.fAck           = TRUE ;

                         wStatus = * (WORD *) & DdeAck ;

                         if (!PostMessage (hwndClient, WM_DDE_ACK, hwnd,
                                           MAKELONG (wStatus, aItem)))
                              {
                              GlobalDeleteAtom (aItem) ;
                              }
                         else
                              {
                              PostDataMessage (hwnd, hwndClient, i,
                                               lpPopAdvise[i].fDeferUpd,
                                               lpPopAdvise[i].fAckReq,
                                               FALSE) ;
                              }

                         GlobalUnlock (hPopAdvise) ;
                         return 0 ;
                         }
                    }

                         // Otherwise post a negative WM_DDE_ACK

               GlobalUnlock (hDdeAdvise) ;

               DdeAck.bAppReturnCode = 0 ;
               DdeAck.reserved       = 0 ;
               DdeAck.fBusy          = FALSE ;
               DdeAck.fAck           = FALSE ;

               wStatus = * (WORD *) & DdeAck ;

               if (!PostMessage (hwndClient, WM_DDE_ACK, hwnd,
                                 MAKELONG (wStatus, aItem)))
                    {
                    GlobalFree (hDdeAdvise) ;
                    GlobalDeleteAtom (aItem) ;
                    }

               return 0 ;

          case WM_DDE_UNADVISE:

                    // wParam          -- sending window handle
                    // LOWORD (lParam) -- data format
                    // HIWORD (lParam) -- item atom

               hwndClient = wParam ;
               cfFormat   = LOWORD (lParam) ;
               aItem      = HIWORD (lParam) ;

               DdeAck.bAppReturnCode = 0 ;
               DdeAck.reserved       = 0 ;
               DdeAck.fBusy          = FALSE ;
               DdeAck.fAck           = TRUE ;

               hPopAdvise  = GetWindowWord (hwnd, 2) ;
               lpPopAdvise = (POPADVISE FAR *) GlobalLock (hPopAdvise) ;

                    // Check for matching format and data item

               if (cfFormat == CF_TEXT || cfFormat == 0)
                    {
                    if (aItem == NULL)
                         for (i = 0 ; i < NUM_STATES ; i++)
                              lpPopAdvise[i].fAdvise = FALSE ;
                    else
                         {
                         GlobalGetAtomName (aItem, szItem, sizeof (szItem)) ;

                         for (i = 0 ; i < NUM_STATES ; i++)
                              if (strcmp (szItem, pop[i].szState) == 0)
                                   break ;

                         if (i < NUM_STATES)
                              lpPopAdvise[i].fAdvise = FALSE ;
                         else
                              DdeAck.fAck = FALSE ;
                         }
                    }
               else
                    DdeAck.fAck = FALSE ;

                    // Acknowledge either positively or negatively

               wStatus = * (WORD *) & DdeAck ;

               if (!PostMessage (hwndClient, WM_DDE_ACK, hwnd,
                                 MAKELONG (wStatus, aItem)))
                    {
                    if (aItem != NULL)
                         GlobalDeleteAtom (aItem) ;
                    }

               GlobalUnlock (hPopAdvise) ;
               return 0 ;

          case WM_DDE_EXECUTE:

                    // Post negative acknowledge

               hwndClient = wParam ;
               hCommands  = HIWORD (lParam) ;

               DdeAck.bAppReturnCode = 0 ;
               DdeAck.reserved       = 0 ;
               DdeAck.fBusy          = FALSE ;
               DdeAck.fAck           = FALSE ;

               wStatus = * (WORD *) & DdeAck ;

               if (!PostMessage (hwndClient, WM_DDE_ACK, hwnd,
                                 MAKELONG (wStatus, hCommands)))
                    {
                    GlobalFree (hCommands) ;
                    }
               return 0 ;

          case WM_DDE_POKE:

                    // Post negative acknowledge

               hwndClient = wParam ;
               hDdePoke   = LOWORD (lParam) ;
               aItem      = HIWORD (lParam) ;

               DdeAck.bAppReturnCode = 0 ;
               DdeAck.reserved       = 0 ;
               DdeAck.fBusy          = FALSE ;
               DdeAck.fAck           = FALSE ;

               wStatus = * (WORD *) & DdeAck ;

               if (!PostMessage (hwndClient, WM_DDE_ACK, hwnd,
                                 MAKELONG (wStatus, aItem)))
                    {
                    GlobalFree (hDdePoke) ;
                    GlobalDeleteAtom (aItem) ;
                    }

               return 0 ;

          case WM_DDE_TERMINATE:

                    // Respond with another WM_DDE_TERMINATE message

               hwndClient = wParam ;
               PostMessage (hwndClient, WM_DDE_TERMINATE, hwnd, 0L) ;
               DestroyWindow (hwnd) ;
               return 0 ;

          case WM_TIMER:

                    // Post WM_DDE_DATA messages for changed populations

               hwndClient  = GetWindowWord (hwnd, 0) ;
               hPopAdvise  = GetWindowWord (hwnd, 2) ;
               lpPopAdvise = (POPADVISE FAR *) GlobalLock (hPopAdvise) ;

               for (i = 0 ; i < NUM_STATES ; i++)
                    if (lpPopAdvise[i].fAdvise)
                         if (lpPopAdvise[i].lPopPrev != pop[i].lPop)
                              {
                              if (!PostDataMessage (hwnd, hwndClient, i,
                                                    lpPopAdvise[i].fDeferUpd,
                                                    lpPopAdvise[i].fAckReq,
                                                    FALSE))
                                   break ;

                              lpPopAdvise[i].lPopPrev = pop[i].lPop ;
                              }

               GlobalUnlock (hPopAdvise) ;
               return 0 ;

          case WM_CLOSE:

                    // Post a WM_DDE_TERMINATE message to the client

               hwndClient = GetWindowWord (hwnd, 0) ;
               PostMessage (hwndClient, WM_DDE_TERMINATE, hwnd, 0L) ;

               dwTime = GetCurrentTime () ;

               while (GetCurrentTime () - dwTime < DDE_TIMEOUT)
                    if (PeekMessage (&msg, hwnd, WM_DDE_TERMINATE,
                                     WM_DDE_TERMINATE, PM_REMOVE))
                         break ;

               DestroyWindow (hwnd) ;
               return 0 ;

          case WM_DESTROY:
               hPopAdvise = GetWindowWord (hwnd, 2) ;
               GlobalFree (hPopAdvise) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

BOOL FAR PASCAL TimerEnumProc (HWND hwnd, LONG lParam)
     {
     SendMessage (hwnd, WM_TIMER, 0, 0L) ;

     return TRUE ;
     }

BOOL FAR PASCAL CloseEnumProc (HWND hwnd, LONG lParam)
     {
     SendMessage (hwnd, WM_CLOSE, 0, 0L) ;

     return TRUE ;
     }

BOOL PostDataMessage (HWND hwndServer, HWND hwndClient, int iState,
                      BOOL fDeferUpd, BOOL fAckReq, BOOL fResponse)
     {
     ATOM         aItem ;
     char         szPopulation [10] ;
     DDEACK       DdeAck ;
     DDEDATA FAR  *lpDdeData ;
     DWORD        dwTime ;
     GLOBALHANDLE hDdeData ;
     MSG          msg ;

     aItem = GlobalAddAtom (pop[iState].szState) ;

          // Allocate a DDEDATA structure if not defered update

     if (fDeferUpd)
          {
          hDdeData = NULL ;
          }
     else
          {
          wsprintf (szPopulation, "%ld\r\n", pop[iState].lPop) ;

          hDdeData = GlobalAlloc (GHND | GMEM_DDESHARE,
                                  sizeof (DDEDATA) + strlen (szPopulation)) ;

          lpDdeData = (DDEDATA FAR *) GlobalLock (hDdeData) ;

          lpDdeData->fResponse = fResponse ;
          lpDdeData->fRelease  = TRUE ;
          lpDdeData->fAckReq   = fAckReq ;
          lpDdeData->cfFormat  = CF_TEXT ;

          lstrcpy ((LPSTR) lpDdeData->Value, szPopulation) ;

          GlobalUnlock (hDdeData) ;
          }

          // Post the WM_DDE_DATA message

     if (!PostMessage (hwndClient, WM_DDE_DATA, hwndServer,
                       MAKELONG (hDdeData, aItem)))
          {
          if (hDdeData != NULL)
               GlobalFree (hDdeData) ;

          GlobalDeleteAtom (aItem) ;
          return FALSE ;
          }

          // Wait for the acknowledge message if it's requested

     if (fAckReq)
          {
          DdeAck.fAck = FALSE ;

          dwTime = GetCurrentTime () ;

          while (GetCurrentTime () - dwTime < DDE_TIMEOUT)
               {
               if (PeekMessage (&msg, hwndServer, WM_DDE_ACK, WM_DDE_ACK,
                                PM_REMOVE))
                    {
                    DdeAck = * (DDEACK *) & LOWORD (msg.lParam) ;
                    aItem  = HIWORD (msg.lParam) ;
                    GlobalDeleteAtom (aItem) ;
                    break ;
                    }
               }

          if (DdeAck.fAck == FALSE)
               {
               if (hDdeData != NULL)
                    GlobalFree (hDdeData) ;

               return FALSE ;
               }
          }

     return TRUE ;
     }


DEVCAPS.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP15\DEVCAPS.C

/*---------------------------------------------------------
   DEVCAPS.C -- Display routines for DEVCAPS1 and DEVCAPS2
                (c) Charles Petzold, 1990
  ---------------------------------------------------------*/

#include <windows.h>
#include <string.h>
#include <stdio.h>

typedef struct
     {
     short nMask ;
     char  *szMask ;
     char  *szDesc ;
     }
     BITS ;

void DoBasicInfo (HDC hdc, HDC hdcInfo, short cxChar, short cyChar)
     {
     static struct
          {
          short nIndex ;
          char  *szDesc ;
          }
          info [] =
          {
          HORZSIZE,      "HORZSIZE     Width in millimeters:",
          VERTSIZE,      "VERTSIZE     Height in millimeters:",
          HORZRES,       "HORZRES      Width in pixels:",
          VERTRES,       "VERTRES      Height in raster lines:",
          BITSPIXEL,     "BITSPIXEL    Color bits per pixel:",
          PLANES,        "PLANES       Number of color planes:",
          NUMBRUSHES,    "NUMBRUSHES   Number of device brushes:",
          NUMPENS,       "NUMPENS      Number of device pens:",
          NUMMARKERS,    "NUMMARKERS   Number of device markers:",
          NUMFONTS,      "NUMFONTS     Number of device fonts:",
          NUMCOLORS,     "NUMCOLORS    Number of device colors:",
          PDEVICESIZE,   "PDEVICESIZE  Size of device structure:",
          ASPECTX,       "ASPECTX      Relative width of pixel:",
          ASPECTY,       "ASPECTY      Relative height of pixel:",
          ASPECTXY,      "ASPECTXY     Relative diagonal of pixel:",
          LOGPIXELSX,    "LOGPIXELSX   Horizontal dots per inch:",
          LOGPIXELSY,    "LOGPIXELSY   Vertical dots per inch:",
          SIZEPALETTE,   "SIZEPALETTE  Number of palette entries:",
          NUMRESERVED,   "NUMRESERVED  Reserved palette entries:",
          COLORRES,      "COLORRES     Actual color resolution:"
          } ;
     char   szBuffer [80] ;
     short  i, nLine ;

     for (i = 0 ; i < sizeof info / sizeof info [0] ; i++)
          TextOut (hdc, cxChar, (i + 1) * cyChar, szBuffer,
               sprintf (szBuffer, "%-40s%8d", info[i].szDesc,
                    GetDeviceCaps (hdcInfo, info[i].nIndex))) ;
     }

void DoOtherInfo (HDC hdc, HDC hdcInfo, short cxChar, short cyChar)
     {
     static BITS clip [] =
          {
          CP_RECTANGLE,  "CP_RECTANGLE",     "Can Clip To Rectangle:"
          } ;

     static BITS raster [] =
          {
          RC_BITBLT,       "RC_BITBLT",       "Capable of simple BitBlt:",
          RC_BANDING,      "RC_BANDING",      "Requires banding support:",
          RC_SCALING,      "RC_SCALING",      "Requires scaling support:",
          RC_BITMAP64,     "RC_BITMAP64",     "Supports bitmaps >64K:",
          RC_GDI20_OUTPUT, "RC_GDI20_OUTPUT", "Has 2.0 output calls:",
          RC_DI_BITMAP,    "RC_DI_BITMAP",    "Supports DIB to memory:",
          RC_PALETTE,      "RC_PALETTE",      "Supports a palette:",
          RC_DIBTODEV,     "RC_DIBTODEV",     "Supports bitmap conversion:",
          RC_BIGFONT,      "RC_BIGFONT",      "Supports fonts >64K:",
          RC_STRETCHBLT,   "RC_STRETCHBLT",   "Supports StretchBlt:",
          RC_FLOODFILL,    "RC_FLOODFILL",    "Supports FloodFill:"
          } ;

     static char *szTech [] = { "DT_PLOTTER (Vector plotter)",
                                "DT_RASDISPLAY (Raster display)",
                                "DT_RASPRINTER (Raster printer)",
                                "DT_RASCAMERA (Raster camera)",
                                "DT_CHARSTREAM (Character-stream, PLP)",
                                "DT_METAFILE (Metafile, VDM)",
                                "DT_DISPFILE (Display-file)" } ;
     char        szBuffer [80] ;
     short       i ;

     TextOut (hdc, cxChar, cyChar, szBuffer,
          sprintf (szBuffer, "%-24s%04XH",
               "DRIVERVERSION:", GetDeviceCaps (hdcInfo, DRIVERVERSION))) ;

     TextOut (hdc, cxChar, 2 * cyChar, szBuffer,
          sprintf (szBuffer, "%-24s%-40s",
               "TECHNOLOGY:", szTech [GetDeviceCaps (hdcInfo, TECHNOLOGY)]))

     TextOut (hdc, cxChar, 4 * cyChar, szBuffer,
          sprintf (szBuffer, "CLIPCAPS (Clipping capabilities)")) ;

     for (i = 0 ; i < sizeof clip / sizeof clip [0] ; i++)
          TextOut (hdc, 9 * cxChar, (i + 6) * cyChar, szBuffer,
               sprintf (szBuffer, "%-16s%-28s %3s",
                    clip[i].szMask, clip[i].szDesc,
                    GetDeviceCaps (hdcInfo, CLIPCAPS) & clip[i].nMask ?
                         "Yes" : "No")) ;

     TextOut (hdc, cxChar, 8 * cyChar, szBuffer,
          sprintf (szBuffer, "RASTERCAPS (Raster capabilities)")) ;

     for (i = 0 ; i < sizeof raster / sizeof raster [0] ; i++)
          TextOut (hdc, 9 * cxChar, (i + 10) * cyChar, szBuffer,
               sprintf (szBuffer, "%-16s%-28s %3s",
                    raster[i].szMask, raster[i].szDesc,
                    GetDeviceCaps (hdcInfo, RASTERCAPS) & raster[i].nMask ?
                         "Yes" : "No")) ;
     }

void DoBitCodedCaps (HDC hdc, HDC hdcInfo, short cxChar, short cyChar,
                     short nType)
     {
     static BITS curves [] =
          {
          CC_CIRCLES,    "CC_CIRCLES",    "circles:",
          CC_PIE,        "CC_PIE",        "pie wedges:",
          CC_CHORD,      "CC_CHORD",      "chord arcs:",
          CC_ELLIPSES,   "CC_ELLIPSES",   "ellipses:",
          CC_WIDE,       "CC_WIDE",       "wide borders:",
          CC_STYLED,     "CC_STYLED",     "styled borders:",
          CC_WIDESTYLED, "CC_WIDESTYLED", "wide and styled borders:",
          CC_INTERIORS,  "CC_INTERIORS",  "interiors:"
          } ;

     static BITS lines [] =
          {
          LC_POLYLINE,   "LC_POLYLINE",   "polyline:",
          LC_MARKER,     "LC_MARKER",     "markers:",
          LC_POLYMARKER, "LC_POLYMARKER", "polymarkers",
          LC_WIDE,       "LC_WIDE",       "wide lines:",
          LC_STYLED,     "LC_STYLED",     "styled lines:",
          LC_WIDESTYLED, "LC_WIDESTYLED", "wide and styled lines:",
          LC_INTERIORS,  "LC_INTERIORS",  "interiors:"
          } ;

     static BITS poly [] =
          {
          PC_POLYGON,    "PC_POLYGON",    "alternate fill polygon:",
          PC_RECTANGLE,  "PC_RECTANGLE",  "rectangle:",
          PC_TRAPEZOID,  "PC_TRAPEZOID",  "winding number fill polygon:",
          PC_SCANLINE,   "PC_SCANLINE",   "scanlines:",
          PC_WIDE,       "PC_WIDE",       "wide borders:",
          PC_STYLED,     "PC_STYLED",     "styled borders:",
          PC_WIDESTYLED, "PC_WIDESTYLED", "wide and styled borders:",
          PC_INTERIORS,  "PC_INTERIORS",  "interiors:"
          } ;

     static BITS text [] =
          {
          TC_OP_CHARACTER, "TC_OP_CHARACTER", "character output precision:",
          TC_OP_STROKE,    "TC_OP_STROKE",    "stroke output precision:",
          TC_CP_STROKE,    "TC_CP_STROKE",    "stroke clip precision:",
          TC_CR_90,        "TC_CP_90",        "90 degree character rotation:"
          TC_CR_ANY,       "TC_CR_ANY",       "any character rotation:",
          TC_SF_X_YINDEP,  "TC_SF_X_YINDEP", "scaling independent of X and Y:
          TC_SA_DOUBLE,    "TC_SA_DOUBLE",   "doubled character for scaling:"
          TC_SA_INTEGER,   "TC_SA_INTEGER",  "integer multiples for scaling:"
          TC_SA_CONTIN,    "TC_SA_CONTIN",  "any multiples for exact scaling:
          TC_EA_DOUBLE,    "TC_EA_DOUBLE",  "double weight characters:",
          TC_IA_ABLE,      "TC_IA_ABLE",    "italicizing:",
          TC_UA_ABLE,      "TC_UA_ABLE",    "underlining:",
          TC_SO_ABLE,      "TC_SO_ABLE",    "strikeouts:",
          TC_RA_ABLE,      "TC_RA_ABLE",    "raster fonts:",
          TC_VA_ABLE,      "TC_VA_ABLE",    "vector fonts:"
          } ;

     static struct
          {
          short nIndex ;
          char  *szTitle ;
          BITS  (*pbits) [] ;
          short nSize ;
          }
          bitinfo [] =
          {
          CURVECAPS,  "CURVCAPS (Curve Capabilities)",
                      (BITS (*)[]) curves, sizeof curves / sizeof curves [0],
          LINECAPS,   "LINECAPS (Line Capabilities)",
                      (BITS (*)[]) lines, sizeof lines / sizeof lines [0],
          POLYGONALCAPS, "POLYGONALCAPS (Polygonal Capabilities)",
                      (BITS (*)[]) poly, sizeof poly / sizeof poly [0],
          TEXTCAPS,   "TEXTCAPS (Text Capabilities)",
                      (BITS (*)[]) text, sizeof text / sizeof text [0]
          } ;

     static char szBuffer [80] ;
     BITS        (*pbits) [] = bitinfo [nType].pbits ;
     short       nDevCaps = GetDeviceCaps (hdcInfo, bitinfo [nType].nIndex) ;
     short       i ;

     TextOut (hdc, cxChar, cyChar, bitinfo [nType].szTitle,
                    strlen (bitinfo [nType].szTitle)) ;

     for (i = 0 ; i < bitinfo [nType].nSize ; i++)
          TextOut (hdc, cxChar, (i + 3) * cyChar, szBuffer,
               sprintf (szBuffer, "%-16s %s %-32s %3s",
                    (*pbits)[i].szMask, "Can do", (*pbits)[i].szDesc,
                    nDevCaps & (*pbits)[i].nMask ? "Yes" : "No")) ;
     }


DEVCAPS.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP11\DEVCAPS.C

/*---------------------------------------------------------
   DEVCAPS.C -- Display routines for DEVCAPS1 and DEVCAPS2
                (c) Charles Petzold, 1990
  ---------------------------------------------------------*/

#include <windows.h>
#include <string.h>
#include <stdio.h>

typedef struct
     {
     short nMask ;
     char  *szMask ;
     char  *szDesc ;
     }
     BITS ;

void DoBasicInfo (HDC hdc, HDC hdcInfo, short cxChar, short cyChar)
     {
     static struct
          {
          short nIndex ;
          char  *szDesc ;
          }
          info [] =
          {
          HORZSIZE,      "HORZSIZE     Width in millimeters:",
          VERTSIZE,      "VERTSIZE     Height in millimeters:",
          HORZRES,       "HORZRES      Width in pixels:",
          VERTRES,       "VERTRES      Height in raster lines:",
          BITSPIXEL,     "BITSPIXEL    Color bits per pixel:",
          PLANES,        "PLANES       Number of color planes:",
          NUMBRUSHES,    "NUMBRUSHES   Number of device brushes:",
          NUMPENS,       "NUMPENS      Number of device pens:",
          NUMMARKERS,    "NUMMARKERS   Number of device markers:",
          NUMFONTS,      "NUMFONTS     Number of device fonts:",
          NUMCOLORS,     "NUMCOLORS    Number of device colors:",
          PDEVICESIZE,   "PDEVICESIZE  Size of device structure:",
          ASPECTX,       "ASPECTX      Relative width of pixel:",
          ASPECTY,       "ASPECTY      Relative height of pixel:",
          ASPECTXY,      "ASPECTXY     Relative diagonal of pixel:",
          LOGPIXELSX,    "LOGPIXELSX   Horizontal dots per inch:",
          LOGPIXELSY,    "LOGPIXELSY   Vertical dots per inch:",
          SIZEPALETTE,   "SIZEPALETTE  Number of palette entries:",
          NUMRESERVED,   "NUMRESERVED  Reserved palette entries:",
          COLORRES,      "COLORRES     Actual color resolution:"
          } ;
     char   szBuffer [80] ;
     short  i, nLine ;

     for (i = 0 ; i < sizeof info / sizeof info [0] ; i++)
          TextOut (hdc, cxChar, (i + 1) * cyChar, szBuffer,
               sprintf (szBuffer, "%-40s%8d", info[i].szDesc,
                    GetDeviceCaps (hdcInfo, info[i].nIndex))) ;
     }

void DoOtherInfo (HDC hdc, HDC hdcInfo, short cxChar, short cyChar)
     {
     static BITS clip [] =
          {
          CP_RECTANGLE,  "CP_RECTANGLE",     "Can Clip To Rectangle:"
          } ;

     static BITS raster [] =
          {
          RC_BITBLT,       "RC_BITBLT",       "Capable of simple BitBlt:",
          RC_BANDING,      "RC_BANDING",      "Requires banding support:",
          RC_SCALING,      "RC_SCALING",      "Requires scaling support:",
          RC_BITMAP64,     "RC_BITMAP64",     "Supports bitmaps >64K:",
          RC_GDI20_OUTPUT, "RC_GDI20_OUTPUT", "Has 2.0 output calls:",
          RC_DI_BITMAP,    "RC_DI_BITMAP",    "Supports DIB to memory:",
          RC_PALETTE,      "RC_PALETTE",      "Supports a palette:",
          RC_DIBTODEV,     "RC_DIBTODEV",     "Supports bitmap conversion:",
          RC_BIGFONT,      "RC_BIGFONT",      "Supports fonts >64K:",
          RC_STRETCHBLT,   "RC_STRETCHBLT",   "Supports StretchBlt:",
          RC_FLOODFILL,    "RC_FLOODFILL",    "Supports FloodFill:"
          } ;

     static char *szTech [] = { "DT_PLOTTER (Vector plotter)",
                                "DT_RASDISPLAY (Raster display)",
                                "DT_RASPRINTER (Raster printer)",
                                "DT_RASCAMERA (Raster camera)",
                                "DT_CHARSTREAM (Character-stream, PLP)",
                                "DT_METAFILE (Metafile, VDM)",
                                "DT_DISPFILE (Display-file)" } ;
     char        szBuffer [80] ;
     short       i ;

     TextOut (hdc, cxChar, cyChar, szBuffer,
          sprintf (szBuffer, "%-24s%04XH",
               "DRIVERVERSION:", GetDeviceCaps (hdcInfo, DRIVERVERSION))) ;

     TextOut (hdc, cxChar, 2 * cyChar, szBuffer,
          sprintf (szBuffer, "%-24s%-40s",
               "TECHNOLOGY:", szTech [GetDeviceCaps (hdcInfo, TECHNOLOGY)]))

     TextOut (hdc, cxChar, 4 * cyChar, szBuffer,
          sprintf (szBuffer, "CLIPCAPS (Clipping capabilities)")) ;

     for (i = 0 ; i < sizeof clip / sizeof clip [0] ; i++)
          TextOut (hdc, 9 * cxChar, (i + 6) * cyChar, szBuffer,
               sprintf (szBuffer, "%-16s%-28s %3s",
                    clip[i].szMask, clip[i].szDesc,
                    GetDeviceCaps (hdcInfo, CLIPCAPS) & clip[i].nMask ?
                         "Yes" : "No")) ;

     TextOut (hdc, cxChar, 8 * cyChar, szBuffer,
          sprintf (szBuffer, "RASTERCAPS (Raster capabilities)")) ;

     for (i = 0 ; i < sizeof raster / sizeof raster [0] ; i++)
          TextOut (hdc, 9 * cxChar, (i + 10) * cyChar, szBuffer,
               sprintf (szBuffer, "%-16s%-28s %3s",
                    raster[i].szMask, raster[i].szDesc,
                    GetDeviceCaps (hdcInfo, RASTERCAPS) & raster[i].nMask ?
                         "Yes" : "No")) ;
     }

void DoBitCodedCaps (HDC hdc, HDC hdcInfo, short cxChar, short cyChar,
                     short nType)
     {
     static BITS curves [] =
          {
          CC_CIRCLES,    "CC_CIRCLES",    "circles:",
          CC_PIE,        "CC_PIE",        "pie wedges:",
          CC_CHORD,      "CC_CHORD",      "chord arcs:",
          CC_ELLIPSES,   "CC_ELLIPSES",   "ellipses:",
          CC_WIDE,       "CC_WIDE",       "wide borders:",
          CC_STYLED,     "CC_STYLED",     "styled borders:",
          CC_WIDESTYLED, "CC_WIDESTYLED", "wide and styled borders:",
          CC_INTERIORS,  "CC_INTERIORS",  "interiors:"
          } ;

     static BITS lines [] =
          {
          LC_POLYLINE,   "LC_POLYLINE",   "polyline:",
          LC_MARKER,     "LC_MARKER",     "markers:",
          LC_POLYMARKER, "LC_POLYMARKER", "polymarkers",
          LC_WIDE,       "LC_WIDE",       "wide lines:",
          LC_STYLED,     "LC_STYLED",     "styled lines:",
          LC_WIDESTYLED, "LC_WIDESTYLED", "wide and styled lines:",
          LC_INTERIORS,  "LC_INTERIORS",  "interiors:"
          } ;

     static BITS poly [] =
          {
          PC_POLYGON,    "PC_POLYGON",    "alternate fill polygon:",
          PC_RECTANGLE,  "PC_RECTANGLE",  "rectangle:",
          PC_TRAPEZOID,  "PC_TRAPEZOID",  "winding number fill polygon:",
          PC_SCANLINE,   "PC_SCANLINE",   "scanlines:",
          PC_WIDE,       "PC_WIDE",       "wide borders:",
          PC_STYLED,     "PC_STYLED",     "styled borders:",
          PC_WIDESTYLED, "PC_WIDESTYLED", "wide and styled borders:",
          PC_INTERIORS,  "PC_INTERIORS",  "interiors:"
          } ;

     static BITS text [] =
          {
          TC_OP_CHARACTER, "TC_OP_CHARACTER", "character output precision:",
          TC_OP_STROKE,    "TC_OP_STROKE",    "stroke output precision:",
          TC_CP_STROKE,    "TC_CP_STROKE",    "stroke clip precision:",
          TC_CR_90,        "TC_CP_90",        "90 degree character rotation:"
          TC_CR_ANY,       "TC_CR_ANY",       "any character rotation:",
          TC_SF_X_YINDEP,  "TC_SF_X_YINDEP", "scaling independent of X and Y:
          TC_SA_DOUBLE,    "TC_SA_DOUBLE",   "doubled character for scaling:"
          TC_SA_INTEGER,   "TC_SA_INTEGER",  "integer multiples for scaling:"
          TC_SA_CONTIN,    "TC_SA_CONTIN",  "any multiples for exact scaling:
          TC_EA_DOUBLE,    "TC_EA_DOUBLE",  "double weight characters:",
          TC_IA_ABLE,      "TC_IA_ABLE",    "italicizing:",
          TC_UA_ABLE,      "TC_UA_ABLE",    "underlining:",
          TC_SO_ABLE,      "TC_SO_ABLE",    "strikeouts:",
          TC_RA_ABLE,      "TC_RA_ABLE",    "raster fonts:",
          TC_VA_ABLE,      "TC_VA_ABLE",    "vector fonts:"
          } ;

     static struct
          {
          short nIndex ;
          char  *szTitle ;
          BITS  (*pbits) [] ;
          short nSize ;
          }
          bitinfo [] =
          {
          CURVECAPS,  "CURVCAPS (Curve Capabilities)",
                      (BITS (*)[]) curves, sizeof curves / sizeof curves [0],
          LINECAPS,   "LINECAPS (Line Capabilities)",
                      (BITS (*)[]) lines, sizeof lines / sizeof lines [0],
          POLYGONALCAPS, "POLYGONALCAPS (Polygonal Capabilities)",
                      (BITS (*)[]) poly, sizeof poly / sizeof poly [0],
          TEXTCAPS,   "TEXTCAPS (Text Capabilities)",
                      (BITS (*)[]) text, sizeof text / sizeof text [0]
          } ;

     static char szBuffer [80] ;
     BITS        (*pbits) [] = bitinfo [nType].pbits ;
     short       nDevCaps = GetDeviceCaps (hdcInfo, bitinfo [nType].nIndex) ;
     short       i ;

     TextOut (hdc, cxChar, cyChar, bitinfo [nType].szTitle,
                    strlen (bitinfo [nType].szTitle)) ;

     for (i = 0 ; i < bitinfo [nType].nSize ; i++)
          TextOut (hdc, cxChar, (i + 3) * cyChar, szBuffer,
               sprintf (szBuffer, "%-16s %s %-32s %3s",
                    (*pbits)[i].szMask, "Can do", (*pbits)[i].szDesc,
                    nDevCaps & (*pbits)[i].nMask ? "Yes" : "No")) ;
     }


DEVCAPS1.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP11\DEVCAPS1.C

/*------------------------------------------------------
   DEVCAPS1.C -- Displays Device Capability Information
                 (c) Charles Petzold, 1990
  ------------------------------------------------------*/

#include <windows.h>
#include <string.h>
#include "devcaps1.h"

void DoBasicInfo (HDC, HDC, short, short) ;       // in DEVCAPS.C
void DoOtherInfo (HDC, HDC, short, short) ;
void DoBitCodedCaps (HDC, HDC, short, short, short) ;

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "DevCaps" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          !RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Device Capabilities",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

HDC GetPrinterIC ()
     {
     char szPrinter [64] ;
     char *szDevice, *szDriver, *szOutput ;

     GetProfileString ("windows", "device", "", szPrinter, 64) ;

     if ((szDevice = strtok (szPrinter, "," )) &&
         (szDriver = strtok (NULL,      ", ")) &&
         (szOutput = strtok (NULL,      ", ")))

               return CreateIC (szDriver, szDevice, szOutput, NULL) ;

     return NULL ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static short cxChar, cyChar, nCurrentDevice = IDM_SCREEN,
                                  nCurrentInfo   = IDM_BASIC ;
     HDC          hdc, hdcInfo ;
     HMENU        hMenu ;
     PAINTSTRUCT  ps ;
     TEXTMETRIC   tm ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

               GetTextMetrics (hdc, &tm) ;
               cxChar = tm.tmAveCharWidth ;
               cyChar = tm.tmHeight + tm.tmExternalLeading ;

               ReleaseDC (hwnd, hdc) ;
               return 0 ;

          case WM_COMMAND:
               hMenu = GetMenu (hwnd) ;

               switch (wParam)
                    {
                    case IDM_SCREEN:
                    case IDM_PRINTER:
                         CheckMenuItem (hMenu, nCurrentDevice, MF_UNCHECKED)
                         nCurrentDevice = wParam ;
                         CheckMenuItem (hMenu, nCurrentDevice, MF_CHECKED) ;
                         InvalidateRect (hwnd, NULL, TRUE) ;
                         return 0 ;

                    case IDM_BASIC:
                    case IDM_OTHER:
                    case IDM_CURVE:
                    case IDM_LINE:
                    case IDM_POLY:
                    case IDM_TEXT:
                         CheckMenuItem (hMenu, nCurrentInfo, MF_UNCHECKED) ;
                         nCurrentInfo = wParam ;
                         CheckMenuItem (hMenu, nCurrentInfo, MF_CHECKED) ;
                         InvalidateRect (hwnd, NULL, TRUE) ;
                         return 0 ;
                    }
               break ;

          case WM_DEVMODECHANGE:
               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

               if (nCurrentDevice == IDM_SCREEN)
                    hdcInfo = CreateIC ("DISPLAY", NULL, NULL, NULL) ;
               else
                    hdcInfo = GetPrinterIC () ;

               if (hdcInfo)
                    {
                    switch (nCurrentInfo)
                         {
                         case IDM_BASIC:
                              DoBasicInfo (hdc, hdcInfo, cxChar, cyChar) ;
                              break ;

                         case IDM_OTHER:
                              DoOtherInfo (hdc, hdcInfo, cxChar, cyChar) ;
                              break ;

                         case IDM_CURVE:
                         case IDM_LINE:
                         case IDM_POLY:
                         case IDM_TEXT:
                              DoBitCodedCaps (hdc, hdcInfo, cxChar, cyChar,
                                              nCurrentInfo - IDM_CURVE) ;
                              break ;
                         }
                    DeleteDC (hdcInfo) ;
                    }

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


DEVCAPS2.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP15\DEVCAPS2.C

/*------------------------------------------------------------------
   DEVCAPS2.C -- Displays Device Capability Information (Version 2)
                 (c) Charles Petzold, 1990
  ------------------------------------------------------------------*/

#include <windows.h>
#include <string.h>
#include "devcaps2.h"

void DoBasicInfo (HDC, HDC, short, short) ;            // in DEVCAPS.C
void DoOtherInfo (HDC, HDC, short, short) ;
void DoBitCodedCaps (HDC, HDC, short, short, short) ;

typedef VOID (FAR PASCAL *DEVMODEPROC) (HWND, HANDLE, LPSTR, LPSTR) ;

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "DevCaps2" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, NULL,
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

void DoEscSupport (HDC hdc, HDC hdcInfo, short cxChar, short cyChar)
     {
     static struct
          {
          char  *szEscCode ;
          short nEscCode ;
          }
          esc [] =
          {
          "NEWFRAME",          NEWFRAME,
          "ABORTDOC",          ABORTDOC,
          "NEXTBAND",          NEXTBAND,
          "SETCOLORTABLE",     SETCOLORTABLE,
          "GETCOLORTABLE",     GETCOLORTABLE,
          "FLUSHOUTPUT",       FLUSHOUTPUT,
          "DRAFTMODE",         DRAFTMODE,
          "QUERYESCSUPPORT",   QUERYESCSUPPORT,
          "SETABORTPROC",      SETABORTPROC,
          "STARTDOC",          STARTDOC,
          "ENDDOC",            ENDDOC,
          "GETPHYSPAGESIZE",   GETPHYSPAGESIZE,
          "GETPRINTINGOFFSET", GETPRINTINGOFFSET,
          "GETSCALINGFACTOR",  GETSCALINGFACTOR } ;

     static char *szYesNo [] = { "Yes", "No" } ;
     char        szBuffer [32] ;
     POINT       pt ;
     short       n, nReturn ;

     TextOut (hdc, cxChar, cyChar, "Escape Support", 14) ;

     for (n = 0 ; n < sizeof esc / sizeof esc [0] ; n++)
          {
          nReturn = Escape (hdcInfo, QUERYESCSUPPORT, 1,
                                   (LPSTR) & esc[n].nEscCode, NULL) ;
          TextOut (hdc, 6 * cxChar, (n + 3) * cyChar, szBuffer,
               wsprintf (szBuffer, "%-24s %3s", (LPSTR) esc[n].szEscCode,
                         (LPSTR) szYesNo [nReturn > 0 ? 0 : 1])) ;

          if (nReturn > 0 && esc[n].nEscCode >= GETPHYSPAGESIZE
                          && esc[n].nEscCode <= GETSCALINGFACTOR)
               {
               Escape (hdcInfo, esc[n].nEscCode, 0, NULL, (LPSTR) &pt) ;
               TextOut (hdc, 36 * cxChar, (n + 3) * cyChar, szBuffer,
                        wsprintf (szBuffer, "(%u,%u)", pt.x, pt.y)) ;
               }
          }
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static char   szAllDevices [4096], szDevice [32], szDriver [16],
                   szDriverFile [16], szWindowText [64] ;
     static HANDLE hLibrary ;
     static short  n, cxChar, cyChar, nCurrentDevice = IDM_SCREEN,
                                      nCurrentInfo   = IDM_BASIC ;
     char          *szOutput, *szPtr ;
     DEVMODEPROC   lpfnDM ;
     HDC           hdc, hdcInfo ;
     HMENU         hMenu ;
     PAINTSTRUCT   ps ;
     TEXTMETRIC    tm ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
               GetTextMetrics (hdc, &tm) ;
               cxChar = tm.tmAveCharWidth ;
               cyChar = tm.tmHeight + tm.tmExternalLeading ;
               ReleaseDC (hwnd, hdc) ;

               lParam = NULL ;
                                                  // fall through
          case WM_WININICHANGE:
               if (lParam != NULL && lstrcmp ((LPSTR) lParam, "devices") != 0
                    return 0 ;

               hMenu = GetSubMenu (GetMenu (hwnd), 0) ;

               while (GetMenuItemCount (hMenu) > 1)
                    DeleteMenu (hMenu, 1, MF_BYPOSITION) ;

               GetProfileString ("devices", NULL, "", szAllDevices,
                         sizeof szAllDevices) ;

               n = IDM_SCREEN + 1 ;
               szPtr = szAllDevices ;
               while (*szPtr)
                    {
                    AppendMenu (hMenu, n % 16 ? 0 : MF_MENUBARBREAK, n, szPtr
                    n++ ;
                    szPtr += strlen (szPtr) + 1 ;
                    }
               AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ;
               AppendMenu (hMenu, 0, IDM_DEVMODE, "Device Mode") ;

               wParam = IDM_SCREEN ;
                                                  // fall through
          case WM_COMMAND:
               hMenu = GetMenu (hwnd) ;

               if (wParam < IDM_DEVMODE)          // IDM_SCREEN & Printers
                    {
                    CheckMenuItem (hMenu, nCurrentDevice, MF_UNCHECKED) ;
                    nCurrentDevice = wParam ;
                    CheckMenuItem (hMenu, nCurrentDevice, MF_CHECKED) ;
                    }
               else if (wParam == IDM_DEVMODE)
                    {
                    GetMenuString (hMenu, nCurrentDevice, szDevice,
                                   sizeof szDevice, MF_BYCOMMAND) ;

                    GetProfileString ("devices", szDevice, "",
                                      szDriver, sizeof szDriver) ;

                    szOutput = strtok (szDriver, ", ") ;
                    strcat (strcpy (szDriverFile, szDriver), ".DRV") ;

                    if (hLibrary >= 32)
                         FreeLibrary (hLibrary) ;

                    hLibrary = LoadLibrary (szDriverFile) ;
                    if (hLibrary >= 32)
                         {
                         lpfnDM = GetProcAddress (hLibrary, "DEVICEMODE") ;
                         (*lpfnDM) (hwnd, hLibrary, (LPSTR) szDevice,
                                                    (LPSTR) szOutput) ;
                         }
                    }
               else                               // info menu items
                    {
                    CheckMenuItem (hMenu, nCurrentInfo, MF_UNCHECKED) ;
                    nCurrentInfo = wParam ;
                    CheckMenuItem (hMenu, nCurrentInfo, MF_CHECKED) ;
                    }
               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_INITMENUPOPUP:
               if (lParam == 0)
                    EnableMenuItem (GetMenu (hwnd), IDM_DEVMODE,
                         nCurrentDevice == IDM_SCREEN ?
                              MF_GRAYED : MF_ENABLED) ;
               return 0 ;

          case WM_PAINT:
               strcpy (szWindowText, "Device Capabilities: ") ;

               if (nCurrentDevice == IDM_SCREEN)
                    {
                    strcpy (szDriver, "DISPLAY") ;
                    strcat (szWindowText, szDriver) ;
                    hdcInfo = CreateIC (szDriver, NULL, NULL, NULL) ;
                    }
               else
                    {
                    hMenu = GetMenu (hwnd) ;

                    GetMenuString (hMenu, nCurrentDevice, szDevice,
                                   sizeof szDevice, MF_BYCOMMAND) ;

                    GetProfileString ("devices", szDevice, "", szDriver, 10)
                    szOutput = strtok (szDriver, ", ") ;
                    strcat (szWindowText, szDevice) ;

                    hdcInfo = CreateIC (szDriver, szDevice, szOutput, NULL) ;
                    }
               SetWindowText (hwnd, szWindowText) ;

               hdc = BeginPaint (hwnd, &ps) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

               if (hdcInfo)
                    {
                    switch (nCurrentInfo)
                         {
                         case IDM_BASIC:
                              DoBasicInfo (hdc, hdcInfo, cxChar, cyChar) ;
                              break ;

                         case IDM_OTHER:
                              DoOtherInfo (hdc, hdcInfo, cxChar, cyChar) ;
                              break ;

                         case IDM_CURVE:
                         case IDM_LINE:
                         case IDM_POLY:
                         case IDM_TEXT:
                              DoBitCodedCaps (hdc, hdcInfo, cxChar, cyChar,
                                   nCurrentInfo - IDM_CURVE) ;
                              break ;

                         case IDM_ESC:
                              DoEscSupport (hdc, hdcInfo, cxChar, cyChar) ;
                              break ;
                         }
                    DeleteDC (hdcInfo) ;
                    }

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               if (hLibrary >= 32)
                    FreeLibrary (hLibrary) ;

               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


DIGCLOCK.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP05\DIGCLOCK.C

/*-----------------------------------------
   DIGCLOCK.C -- Digital Clock Program
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>
#include <time.h>
#define ID_TIMER    1

#define YEAR  (datetime->tm_year % 100)
#define MONTH (datetime->tm_mon  + 1)
#define MDAY  (datetime->tm_mday)
#define WDAY  (datetime->tm_wday)
#define HOUR  (datetime->tm_hour)
#define MIN   (datetime->tm_min)
#define SEC   (datetime->tm_sec)

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG);
void SizeTheWindow (short *, short *, short *, short *) ;

char  sDate [2], sTime [2], sAMPM [2][5] ;
int   iDate, iTime ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "DigClock" ;
     HWND        hwnd;
     MSG         msg;
     short       xStart, yStart, xClient, yClient ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

    RegisterClass (&wndclass) ;
          }

     SizeTheWindow (&xStart, &yStart, &xClient, &yClient) ;

     hwnd = CreateWindow (szAppName, szAppName,
                          WS_POPUP | WS_DLGFRAME | WS_SYSMENU,
                          xStart,  yStart,
                          xClient, yClient,
                          NULL, NULL, hInstance, NULL) ;

     if (!SetTimer (hwnd, ID_TIMER, 1000, NULL))
          {
          MessageBox (hwnd, "Too many clocks or timers!", szAppName,
                      MB_ICONEXCLAMATION | MB_OK) ;
          return FALSE ;
          }

     ShowWindow (hwnd, SW_SHOWNOACTIVATE) ;
     UpdateWindow (hwnd);

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

void SizeTheWindow (short *pxStart,  short *pyStart,
                    short *pxClient, short *pyClient)
     {
     HDC        hdc ;
     TEXTMETRIC tm ;

     hdc = CreateIC ("DISPLAY", NULL, NULL, NULL) ;
     GetTextMetrics (hdc, &tm) ;
     DeleteDC (hdc) ;

     *pxClient = 2 * GetSystemMetrics (SM_CXDLGFRAME) + 16*tm.tmAveCharWidth
     *pxStart  =     GetSystemMetrics (SM_CXSCREEN)   - *pxClient ;
     *pyClient = 2 * GetSystemMetrics (SM_CYDLGFRAME) + 2*tm.tmHeight ;
     *pyStart  =     GetSystemMetrics (SM_CYSCREEN)   - *pyClient ;
     }

void SetInternational (void)
     {
     static char cName [] = "intl" ;

     iDate = GetProfileInt (cName, "iDate", 0) ;
     iTime = GetProfileInt (cName, "iTime", 0) ;

     GetProfileString (cName, "sDate",  "/", sDate,     2) ;
     GetProfileString (cName, "sTime",  ":", sTime,     2) ;
     GetProfileString (cName, "s1159", "AM", sAMPM [0], 5) ;
     GetProfileString (cName, "s2359", "PM", sAMPM [1], 5) ;
     }

void WndPaint (HWND hwnd, HDC hdc)
     {
     static char szWday[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat" ;
     char        cBuffer[40] ;
     long        lTime ;
     RECT        rect ;
     short       nLength ;
     struct tm   *datetime ;

     time (&lTime) ;
     datetime = localtime (&lTime) ;

     nLength = wsprintf (cBuffer, "  %s  %d%s%02d%s%02d  \r\n",
               (LPSTR) szWday + 4 * WDAY,
               iDate == 1 ? MDAY  : iDate == 2 ? YEAR  : MONTH, (LPSTR) sDate
               iDate == 1 ? MONTH : iDate == 2 ? MONTH : MDAY,  (LPSTR) sDate
               iDate == 1 ? YEAR  : iDate == 2 ? MDAY  : YEAR) ;

     if (iTime == 1)
          nLength += wsprintf (cBuffer + nLength, "  %02d%s%02d%s%02d  ",
                               HOUR, (LPSTR) sTime, MIN, (LPSTR) sTime, SEC)
     else
          nLength += wsprintf (cBuffer + nLength, "  %d%s%02d%s%02d %s  ",
                               (HOUR % 12) ? (HOUR % 12) : 12,
                               (LPSTR) sTime, MIN, (LPSTR) sTime, SEC,
                               (LPSTR) sAMPM [HOUR / 12]) ;

     GetClientRect (hwnd, &rect) ;
     DrawText (hdc, cBuffer, -1, &rect, DT_CENTER | DT_NOCLIP) ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     HDC         hdc ;
     PAINTSTRUCT ps;

     switch (message)
          {
          case WM_CREATE :
               SetInternational () ;
               return 0 ;

          case WM_TIMER :
               InvalidateRect (hwnd, NULL, FALSE) ;
               return 0 ;

          case WM_PAINT :
               hdc = BeginPaint (hwnd, &ps) ;
               WndPaint (hwnd, hdc) ;
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_WININICHANGE :
               SetInternational () ;
               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_DESTROY :
               KillTimer (hwnd, ID_TIMER) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


ENVIRON.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP06\ENVIRON.C

/*----------------------------------------
   ENVIRON.C -- Environment List Box
                (c) Charles Petzold, 1990
  ----------------------------------------*/

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#define  MAXENV  4096

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "Environ" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = COLOR_WINDOW + 1 ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Environment List Box",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static char szBuffer [MAXENV + 1] ;
     static HWND hwndList, hwndText ;
     HDC         hdc ;
     TEXTMETRIC  tm ;
     WORD        n ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;
               GetTextMetrics (hdc, &tm) ;
               ReleaseDC (hwnd, hdc) ;

               hwndList = CreateWindow ("listbox", NULL,
                              WS_CHILD | WS_VISIBLE | LBS_STANDARD,
                              tm.tmAveCharWidth, tm.tmHeight * 3,
                              tm.tmAveCharWidth * 16 +
                                   GetSystemMetrics (SM_CXVSCROLL),
                              tm.tmHeight * 5,
                              hwnd, 1,
                              GetWindowWord (hwnd, GWW_HINSTANCE), NULL) ;

               hwndText = CreateWindow ("static", NULL,
                              WS_CHILD | WS_VISIBLE | SS_LEFT,
                              tm.tmAveCharWidth,          tm.tmHeight,
                              tm.tmAveCharWidth * MAXENV, tm.tmHeight,
                              hwnd, 2,
                              GetWindowWord (hwnd, GWW_HINSTANCE), NULL) ;

               for (n = 0 ; environ[n] ; n++)
                    {
                    if (strlen (environ [n]) > MAXENV)
                         continue ;
                    *strchr (strcpy (szBuffer, environ [n]), '=') = '\0' ;
                    SendMessage (hwndList, LB_ADDSTRING, 0,
                                 (LONG) (LPSTR) szBuffer) ;
                    }
               return 0 ;

          case WM_SETFOCUS:
               SetFocus (hwndList) ;
               return 0 ;

          case WM_COMMAND:
               if (wParam == 1 && HIWORD (lParam) == LBN_SELCHANGE)
                    {
                    n = (WORD) SendMessage (hwndList, LB_GETCURSEL, 0, 0L) ;
                    n = (WORD) SendMessage (hwndList, LB_GETTEXT, n,
                                            (LONG) (LPSTR) szBuffer) ;

                    strcpy (szBuffer + n + 1, getenv (szBuffer)) ;
                    *(szBuffer + n) = '=' ;

                    SetWindowText (hwndText, szBuffer) ;
                    }
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


FILEDLG.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP10\FILEDLG.C

/*-----------------------------------------------
   FILEDLG.C -- Open and Close File Dialog Boxes
  -----------------------------------------------*/

#include <windows.h>
#include "filedlg.h"

BOOL FAR PASCAL FileOpenDlgProc (HWND, WORD, WORD, LONG) ;
BOOL FAR PASCAL FileSaveDlgProc (HWND, WORD, WORD, LONG) ;

LPSTR lstrchr  (LPSTR str, char ch) ;
LPSTR lstrrchr (LPSTR str, char ch) ;

static char      szDefExt   [5]  ;
static char      szFileName [96] ;
static char      szFileSpec [16] ;
static POFSTRUCT pof ;
static WORD      wFileAttr, wStatus ;

int DoFileOpenDlg (HANDLE hInst, HWND hwnd, char *szFileSpecIn,
                   char *szDefExtIn, WORD wFileAttrIn,
                   char *szFileNameOut, POFSTRUCT pofIn)
     {
     FARPROC lpfnFileOpenDlgProc ;
     int     iReturn ;

     lstrcpy (szFileSpec, szFileSpecIn) ;
     lstrcpy (szDefExt,   szDefExtIn) ;
     wFileAttr = wFileAttrIn ;
     pof = pofIn ;

     lpfnFileOpenDlgProc = MakeProcInstance (FileOpenDlgProc, hInst) ;

     iReturn = DialogBox (hInst, "FileOpen", hwnd, lpfnFileOpenDlgProc) ;

     FreeProcInstance (lpfnFileOpenDlgProc) ;

     lstrcpy (szFileNameOut, szFileName) ;
     return iReturn ;
     }

int DoFileSaveDlg (HANDLE hInst, HWND hwnd, char *szFileSpecIn,
                   char *szDefExtIn, WORD *pwStatusOut,
                   char *szFileNameOut, POFSTRUCT pofIn)
     {
     FARPROC lpfnFileSaveDlgProc ;
     int     iReturn ;

     lstrcpy (szFileSpec, szFileSpecIn) ;
     lstrcpy (szDefExt,   szDefExtIn) ;
     pof = pofIn ;

     lpfnFileSaveDlgProc = MakeProcInstance (FileSaveDlgProc, hInst) ;

     iReturn = DialogBox (hInst, "FileSave", hwnd, lpfnFileSaveDlgProc) ;

     FreeProcInstance (lpfnFileSaveDlgProc) ;

     lstrcpy (szFileNameOut, szFileName) ;
     *pwStatusOut = wStatus ;
     return iReturn ;
     }

BOOL FAR PASCAL FileOpenDlgProc (HWND hDlg, WORD message,
                                 WORD wParam, LONG lParam)
     {
     char  cLastChar ;
     short nEditLen ;

     switch (message)
        {
        case WM_INITDIALOG:
           SendDlgItemMessage (hDlg, IDD_FNAME, EM_LIMITTEXT, 80, 0L) ;
           DlgDirList (hDlg, szFileSpec, IDD_FLIST, IDD_FPATH, wFileAttr) ;
           SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
           return TRUE ;

        case WM_COMMAND:
           switch (wParam)
              {
              case IDD_FLIST:
                 switch (HIWORD (lParam))
                    {
                    case LBN_SELCHANGE:
                       if (DlgDirSelect (hDlg, szFileName, IDD_FLIST))
                          lstrcat (szFileName, szFileSpec) ;
                       SetDlgItemText (hDlg, IDD_FNAME, szFileName) ;
                       return TRUE ;

                    case LBN_DBLCLK:
                       if (DlgDirSelect (hDlg, szFileName, IDD_FLIST))
                          {
                          lstrcat (szFileName, szFileSpec) ;
                          DlgDirList (hDlg, szFileName, IDD_FLIST, IDD_FPATH,
                                                              wFileAttr) ;
                          SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
                          }
                       else
                          {
                          SetDlgItemText (hDlg, IDD_FNAME, szFileName) ;
                          SendMessage (hDlg, WM_COMMAND, IDOK, 0L) ;
                          }
                       return TRUE ;
                    }
                 break ;

              case IDD_FNAME:
                 if (HIWORD (lParam) == EN_CHANGE)
                    EnableWindow (GetDlgItem (hDlg, IDOK),
                       (BOOL) SendMessage (LOWORD (lParam),
                                             WM_GETTEXTLENGTH, 0, 0L)) ;
                 return TRUE ;

              case IDOK:
                 GetDlgItemText (hDlg, IDD_FNAME, szFileName, 80) ;

                 nEditLen  = lstrlen (szFileName) ;
                 cLastChar = *AnsiPrev (szFileName, szFileName + nEditLen) ;

                 if (cLastChar == '\\' || cLastChar == ':')
                    lstrcat (szFileName, szFileSpec) ;

                 if (lstrchr (szFileName, '*') || lstrchr (szFileName, '?'))
                    {
                    if (DlgDirList (hDlg, szFileName, IDD_FLIST,
                                          IDD_FPATH, wFileAttr))
                       {
                       lstrcpy (szFileSpec, szFileName) ;
                       SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
                       }
                    else
                       MessageBeep (0) ;

                    return TRUE ;
                    }

                 lstrcat (lstrcat (szFileName, "\\"), szFileSpec) ;

                 if (DlgDirList (hDlg, szFileName, IDD_FLIST,
                                                   IDD_FPATH, wFileAttr))
                    {
                    lstrcpy (szFileSpec, szFileName) ;
                    SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
                    return TRUE ;
                    }

                 szFileName [nEditLen] = '\0' ;

                 if (-1 == OpenFile (szFileName, pof, OF_READ | OF_EXIST))
                    {
                    lstrcat (szFileName, szDefExt) ;
                    if (-1 == OpenFile (szFileName, pof, OF_READ | OF_EXIST))
                       {
                       MessageBeep (0) ;
                       return TRUE ;
                       }
                    }
                 lstrcpy (szFileName,
                          AnsiNext (lstrrchr (pof->szPathName, '\\'))) ;

                 OemToAnsi (szFileName, szFileName) ;
                 EndDialog (hDlg, TRUE) ;
                 return TRUE ;

              case IDCANCEL:
                 EndDialog (hDlg, FALSE) ;
                 return TRUE ;
              }
        }
     return FALSE ;
     }

BOOL FAR PASCAL FileSaveDlgProc (HWND hDlg, WORD message,
                                 WORD wParam, LONG lParam)
     {
     switch (message)
        {
        case WM_INITDIALOG:
           SendDlgItemMessage (hDlg, IDD_FNAME, EM_LIMITTEXT, 80, 0L) ;
           DlgDirList (hDlg, szFileSpec, 0, IDD_FPATH, 0) ;
           SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
           return TRUE ;

        case WM_COMMAND:
           switch (wParam)
              {
              case IDD_FNAME:
                 if (HIWORD (lParam) == EN_CHANGE)
                    EnableWindow (GetDlgItem (hDlg, IDOK),
                       (BOOL) SendMessage (LOWORD (lParam),
                                             WM_GETTEXTLENGTH, 0, 0L)) ;
                 return TRUE ;

              case IDOK:
                 GetDlgItemText (hDlg, IDD_FNAME, szFileName, 80) ;

                 if (-1 == OpenFile (szFileName, pof, OF_PARSE))
                    {
                    MessageBeep (0) ;
                    return TRUE ;
                    }

                 if (!lstrchr (AnsiNext (lstrrchr (pof->szPathName, '\\')),
                               '.'))
                    lstrcat (szFileName, szDefExt) ;

                 if (-1 != OpenFile (szFileName, pof, OF_WRITE | OF_EXIST))
                    wStatus = 1 ;

                 else if (-1 != OpenFile (szFileName, pof,
                                                  OF_CREATE | OF_EXIST))
                    wStatus = 0 ;

                 else
                    {
                    MessageBeep (0) ;
                    return TRUE ;
                    }

                 lstrcpy (szFileName,
                          AnsiNext (lstrrchr (pof->szPathName, '\\'))) ;

                 OemToAnsi (szFileName, szFileName) ;
                 EndDialog (hDlg, TRUE) ;
                 return TRUE ;

              case IDCANCEL:
                 EndDialog (hDlg, FALSE) ;
                 return TRUE ;
              }
        }
     return FALSE ;
     }

LPSTR lstrchr (LPSTR str, char ch)
     {
     while (*str)
          {
          if (ch == *str)
               return str ;

          str = AnsiNext (str) ;
          }
     return NULL ;
     }

LPSTR lstrrchr (LPSTR str, char ch)
     {
     LPSTR strl = str + lstrlen (str) ;

     do
          {
          if (ch == *strl)
               return strl ;

          strl = AnsiPrev (str, strl) ;
          }
     while (strl > str) ;

     return NULL ;
     }


FILEDLG.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP15\FILEDLG.C

/*-----------------------------------------------
   FILEDLG.C -- Open and Close File Dialog Boxes
  -----------------------------------------------*/

#include <windows.h>
#include "filedlg.h"

BOOL FAR PASCAL FileOpenDlgProc (HWND, WORD, WORD, LONG) ;
BOOL FAR PASCAL FileSaveDlgProc (HWND, WORD, WORD, LONG) ;

LPSTR lstrchr  (LPSTR str, char ch) ;
LPSTR lstrrchr (LPSTR str, char ch) ;

static char      szDefExt   [5]  ;
static char      szFileName [96] ;
static char      szFileSpec [16] ;
static POFSTRUCT pof ;
static WORD      wFileAttr, wStatus ;

int DoFileOpenDlg (HANDLE hInst, HWND hwnd, char *szFileSpecIn,
                   char *szDefExtIn, WORD wFileAttrIn,
                   char *szFileNameOut, POFSTRUCT pofIn)
     {
     FARPROC lpfnFileOpenDlgProc ;
     int     iReturn ;

     lstrcpy (szFileSpec, szFileSpecIn) ;
     lstrcpy (szDefExt,   szDefExtIn) ;
     wFileAttr = wFileAttrIn ;
     pof = pofIn ;

     lpfnFileOpenDlgProc = MakeProcInstance (FileOpenDlgProc, hInst) ;

     iReturn = DialogBox (hInst, "FileOpen", hwnd, lpfnFileOpenDlgProc) ;

     FreeProcInstance (lpfnFileOpenDlgProc) ;

     lstrcpy (szFileNameOut, szFileName) ;
     return iReturn ;
     }

int DoFileSaveDlg (HANDLE hInst, HWND hwnd, char *szFileSpecIn,
                   char *szDefExtIn, WORD *pwStatusOut,
                   char *szFileNameOut, POFSTRUCT pofIn)
     {
     FARPROC lpfnFileSaveDlgProc ;
     int     iReturn ;

     lstrcpy (szFileSpec, szFileSpecIn) ;
     lstrcpy (szDefExt,   szDefExtIn) ;
     pof = pofIn ;

     lpfnFileSaveDlgProc = MakeProcInstance (FileSaveDlgProc, hInst) ;

     iReturn = DialogBox (hInst, "FileSave", hwnd, lpfnFileSaveDlgProc) ;

     FreeProcInstance (lpfnFileSaveDlgProc) ;

     lstrcpy (szFileNameOut, szFileName) ;
     *pwStatusOut = wStatus ;
     return iReturn ;
     }

BOOL FAR PASCAL FileOpenDlgProc (HWND hDlg, WORD message,
                                 WORD wParam, LONG lParam)
     {
     char  cLastChar ;
     short nEditLen ;

     switch (message)
        {
        case WM_INITDIALOG:
           SendDlgItemMessage (hDlg, IDD_FNAME, EM_LIMITTEXT, 80, 0L) ;
           DlgDirList (hDlg, szFileSpec, IDD_FLIST, IDD_FPATH, wFileAttr) ;
           SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
           return TRUE ;

        case WM_COMMAND:
           switch (wParam)
              {
              case IDD_FLIST:
                 switch (HIWORD (lParam))
                    {
                    case LBN_SELCHANGE:
                       if (DlgDirSelect (hDlg, szFileName, IDD_FLIST))
                          lstrcat (szFileName, szFileSpec) ;
                       SetDlgItemText (hDlg, IDD_FNAME, szFileName) ;
                       return TRUE ;

                    case LBN_DBLCLK:
                       if (DlgDirSelect (hDlg, szFileName, IDD_FLIST))
                          {
                          lstrcat (szFileName, szFileSpec) ;
                          DlgDirList (hDlg, szFileName, IDD_FLIST, IDD_FPATH,
                                                              wFileAttr) ;
                          SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
                          }
                       else
                          {
                          SetDlgItemText (hDlg, IDD_FNAME, szFileName) ;
                          SendMessage (hDlg, WM_COMMAND, IDOK, 0L) ;
                          }
                       return TRUE ;
                    }
                 break ;

              case IDD_FNAME:
                 if (HIWORD (lParam) == EN_CHANGE)
                    EnableWindow (GetDlgItem (hDlg, IDOK),
                       (BOOL) SendMessage (LOWORD (lParam),
                                             WM_GETTEXTLENGTH, 0, 0L)) ;
                 return TRUE ;

              case IDOK:
                 GetDlgItemText (hDlg, IDD_FNAME, szFileName, 80) ;

                 nEditLen  = lstrlen (szFileName) ;
                 cLastChar = *AnsiPrev (szFileName, szFileName + nEditLen) ;

                 if (cLastChar == '\\' || cLastChar == ':')
                    lstrcat (szFileName, szFileSpec) ;

                 if (lstrchr (szFileName, '*') || lstrchr (szFileName, '?'))
                    {
                    if (DlgDirList (hDlg, szFileName, IDD_FLIST,
                                          IDD_FPATH, wFileAttr))
                       {
                       lstrcpy (szFileSpec, szFileName) ;
                       SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
                       }
                    else
                       MessageBeep (0) ;

                    return TRUE ;
                    }

                 lstrcat (lstrcat (szFileName, "\\"), szFileSpec) ;

                 if (DlgDirList (hDlg, szFileName, IDD_FLIST,
                                                   IDD_FPATH, wFileAttr))
                    {
                    lstrcpy (szFileSpec, szFileName) ;
                    SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
                    return TRUE ;
                    }

                 szFileName [nEditLen] = '\0' ;

                 if (-1 == OpenFile (szFileName, pof, OF_READ | OF_EXIST))
                    {
                    lstrcat (szFileName, szDefExt) ;
                    if (-1 == OpenFile (szFileName, pof, OF_READ | OF_EXIST))
                       {
                       MessageBeep (0) ;
                       return TRUE ;
                       }
                    }
                    lstrcpy (szFileName,
                             AnsiNext (lstrrchr (pof->szPathName, '\\'))) ;

                 OemToAnsi (szFileName, szFileName) ;
                 EndDialog (hDlg, TRUE) ;
                 return TRUE ;

              case IDCANCEL:
                 EndDialog (hDlg, FALSE) ;
                 return TRUE ;
              }
        }
     return FALSE ;
     }

BOOL FAR PASCAL FileSaveDlgProc (HWND hDlg, WORD message,
                                 WORD wParam, LONG lParam)
     {
     switch (message)
        {
        case WM_INITDIALOG:
           SendDlgItemMessage (hDlg, IDD_FNAME, EM_LIMITTEXT, 80, 0L) ;
           DlgDirList (hDlg, szFileSpec, 0, IDD_FPATH, 0) ;
           SetDlgItemText (hDlg, IDD_FNAME, szFileSpec) ;
           return TRUE ;

        case WM_COMMAND:
           switch (wParam)
              {
              case IDD_FNAME:
                 if (HIWORD (lParam) == EN_CHANGE)
                    EnableWindow (GetDlgItem (hDlg, IDOK),
                       (BOOL) SendMessage (LOWORD (lParam),
                                             WM_GETTEXTLENGTH, 0, 0L)) ;
                 return TRUE ;

              case IDOK:
                 GetDlgItemText (hDlg, IDD_FNAME, szFileName, 80) ;

                 if (-1 == OpenFile (szFileName, pof, OF_PARSE))
                    {
                    MessageBeep (0) ;
                    return TRUE ;
                    }

                 if (!lstrchr (AnsiNext (lstrrchr (pof->szPathName, '\\')),
                               '.'))
                    lstrcat (szFileName, szDefExt) ;

                 if (-1 != OpenFile (szFileName, pof, OF_WRITE | OF_EXIST))
                    wStatus = 1 ;

                 else if (-1 != OpenFile (szFileName, pof,
                                                  OF_CREATE | OF_EXIST))
                    wStatus = 0 ;

                 else
                    {
                    MessageBeep (0) ;
                    return TRUE ;
                    }

                 lstrcpy (szFileName,
                          AnsiNext (lstrrchr (pof->szPathName, '\\'))) ;

                 OemToAnsi (szFileName, szFileName) ;
                 EndDialog (hDlg, TRUE) ;
                 return TRUE ;

              case IDCANCEL:
                 EndDialog (hDlg, FALSE) ;
                 return TRUE ;
              }
        }
     return FALSE ;
     }

LPSTR lstrchr (LPSTR str, char ch)
     {
     while (*str)
          {
          if (ch == *str)
               return str ;

          str = AnsiNext (str) ;
          }
     return NULL ;
     }

LPSTR lstrrchr (LPSTR str, char ch)
     {
     LPSTR strl = str + lstrlen (str) ;

     do
          {
          if (ch == *strl)
               return strl ;

          strl = AnsiPrev (str, strl) ;
          }
     while (strl > str) ;

     return NULL ;
     }


FONTLIST.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP14\FONTLIST.C

/*-----------------------------------------
   FONTLIST.C -- Font Enumeration Program
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>
#include <string.h>
#include "fontlist.h"

typedef struct
     {
     GLOBALHANDLE hGMem ;
     short        nCount ;
     }
     ENUMER ;

typedef struct
     {
     short        nFontType ;
     LOGFONT      lf ;
     TEXTMETRIC   tm ;
     }
     FONT ;

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
int  FAR PASCAL EnumAllFaces (LPLOGFONT, LPTEXTMETRIC, short, ENUMER FAR *) ;
int  FAR PASCAL EnumAllFonts (LPLOGFONT, LPTEXTMETRIC, short, ENUMER FAR *) ;

char szAppName[] = "FontList" ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }
     hwnd = CreateWindow (szAppName, "Font Enumeration",
                          WS_OVERLAPPEDWINDOW | WS_VSCROLL,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

int FAR PASCAL EnumAllFaces (LPLOGFONT lf, LPTEXTMETRIC tm,
                             short nFontType, ENUMER FAR *enumer)
     {
     LPSTR lpFaces ;

     if (NULL == GlobalReAlloc (enumer->hGMem,
                         (DWORD) LF_FACESIZE * (1 + enumer->nCount),
                         GMEM_MOVEABLE))
          return 0 ;

     lpFaces = GlobalLock (enumer->hGMem) ;
     lstrcpy (lpFaces + enumer->nCount * LF_FACESIZE, lf->lfFaceName) ;
     GlobalUnlock (enumer->hGMem) ;
     enumer->nCount ++ ;
     return 1 ;
     }

int FAR PASCAL EnumAllFonts (LPLOGFONT lf, LPTEXTMETRIC tm,
                             short nFontType, ENUMER FAR *enumer)
     {
     FONT FAR *font ;

     if (NULL == GlobalReAlloc (enumer->hGMem,
                         (DWORD) sizeof (FONT) * (1 + enumer->nCount),
                         GMEM_MOVEABLE))
          return 0 ;

     font = (FONT FAR *) GlobalLock (enumer->hGMem) + enumer->nCount ;
     font->nFontType = nFontType ;
     font->lf = *lf ;
     font->tm = *tm ;

     GlobalUnlock (enumer->hGMem) ;
     enumer->nCount ++ ;
     return 1 ;
     }

void Display (HDC hdc, short cxChar, short cyChar, FONT FAR *font)
     {
     static FONT f ;

     static char *szYN [] = { "No",         "Yes" } ;
     static char *szCS [] = { "ANSI",       "?????",   "Kanji",    "OEM" } ;
     static char *szOP [] = { "Default",    "String",  "Char",    "Stroke" }
     static char *szCP [] = { "Default",    "Char",    "Stroke",   "?????" }
     static char *szQU [] = { "Draft",      "Default", "Proof",    "?????" }
     static char *szP1 [] = { "Default",    "Fixed",   "Variable", "?????" }
     static char *szP2 [] = { "Fixed",      "Variable" } ;
     static char *szFA [] = { "Don't Care", "Roman",      "Swiss", "Modern",
                              "Script",     "Decorative", "?????", "?????" }
     static char *szVR [] = { "Stroke",     "Raster" } ;
     static char *szGD [] = { "GDI",        "Device" } ;

     static struct
          {
          short x ;
          short y ;
          char  *szFmt ;
          short *pData ;
          }
          shorts [] =
          {
           1,  1, "LOGFONT",            NULL,
           1,  2, "-------",            NULL,
           1,  3, "Height:      %10d",  &f.lf.lfHeight,
           1,  4, "Width:       %10d",  &f.lf.lfWidth,
           1,  5, "Escapement:  %10d",  &f.lf.lfEscapement,
           1,  6, "Orientation: %10d",  &f.lf.lfOrientation,
           1,  7, "Weight:      %10d",  &f.lf.lfWeight,
          28,  1, "TEXTMETRIC",         NULL,
          28,  2, "----------",         NULL,
          28,  3, "Height:       %5d",  &f.tm.tmHeight,
          28,  4, "Ascent:       %5d",  &f.tm.tmAscent,
          28,  5, "Descent:      %5d",  &f.tm.tmDescent,
          28,  6, "Int. Leading: %5d",  &f.tm.tmInternalLeading,
          28,  7, "Ext. Leading: %5d",  &f.tm.tmExternalLeading,
          28,  8, "Ave. Width:   %5d",  &f.tm.tmAveCharWidth,
          28,  9, "Max. Width:   %5d",  &f.tm.tmMaxCharWidth,
          28, 10, "Weight:       %5d",  &f.tm.tmWeight,
          51, 10, "Overhang:     %10d", &f.tm.tmOverhang,
          51, 11, "Digitized X:  %10d", &f.tm.tmDigitizedAspectX,
          51, 12, "Digitized Y:  %10d", &f.tm.tmDigitizedAspectY
          } ;

     static struct
          {
          short x ;
          short y ;
          char  *szFmt ;
          BYTE  *pData ;
          }
          bytes [] =
          {
          51,  3, "First Char:   %10d", &f.tm.tmFirstChar,
          51,  4, "Last Char:    %10d", &f.tm.tmLastChar,
          51,  5, "Default Char: %10d", &f.tm.tmDefaultChar,
          51,  6, "Break Char:   %10d", &f.tm.tmBreakChar
          } ;

     static struct
          {
          short x ;
          short y ;
          char  *szFmt ;
          BYTE  *pData ;
          char  **szArray ;
          short sAnd ;
          short sShift ;
          }
          strings [] =
          {
           1,  8, "Italic:      %10s",  &f.lf.lfItalic,         szYN, 1,    0
           1,  9, "Underline:   %10s",  &f.lf.lfUnderline,      szYN, 1,    0
           1, 10, "Strike-Out:  %10s",  &f.lf.lfStrikeOut,      szYN, 1,    0
           1, 11, "Char Set:    %10s",  &f.lf.lfCharSet,        szCS, 0xC0, 6
           1, 12, "Out  Prec:   %10s",  &f.lf.lfOutPrecision,   szOP, 3,    0
           1, 13, "Clip Prec:   %10s",  &f.lf.lfClipPrecision,  szCP, 3,    0
           1, 14, "Quality:     %10s",  &f.lf.lfQuality,        szQU, 3,    0
           1, 15, "Pitch:       %10s",  &f.lf.lfPitchAndFamily, szP1, 3,    0
           1, 16, "Family:      %10s",  &f.lf.lfPitchAndFamily, szFA, 0x70, 4
          28, 11, "Italic:       %5s",  &f.tm.tmItalic,         szYN, 1,    0
          28, 12, "Underline:    %5s",  &f.tm.tmUnderlined,     szYN, 1,    0
          28, 13, "Strike-Out:   %5s",  &f.tm.tmStruckOut,      szYN, 1,    0
          51,  7, "Pitch:        %10s", &f.tm.tmPitchAndFamily, szP2, 1,    0
          51,  8, "Family:       %10s", &f.tm.tmPitchAndFamily, szFA, 0x70, 4
          51,  9, "Char Set:     %10s", &f.tm.tmCharSet,        szCS, 0xC0, 6
          36, 15, "Font Type:  %6s",    (BYTE *) &f.nFontType,  szVR, 1,    0
          55, 15, "%s",                 (BYTE *) &f.nFontType,  szGD, 2,    1
          } ;

     char szBuffer [80] ;
     int  i ;

     f = *font ;

     for (i = 0 ; i < sizeof shorts / sizeof shorts [0] ; i++)
          TextOut (hdc, cxChar * shorts[i].x, cyChar * shorts[i].y, szBuffer,
                   wsprintf (szBuffer, shorts[i].szFmt,
                             *shorts[i].pData)) ;

     for (i = 0 ; i < sizeof bytes / sizeof bytes [0] ; i++)
          TextOut (hdc, cxChar * bytes[i].x, cyChar * bytes[i].y, szBuffer,
                   wsprintf (szBuffer, bytes[i].szFmt,
                             *bytes[i].pData)) ;

     for (i = 0 ; i < sizeof strings / sizeof strings [0] ; i++)
          TextOut (hdc, cxChar * strings[i].x, cyChar * strings[i].y, szBuffe
                   wsprintf (szBuffer, strings[i].szFmt,
                             (LPSTR) ((strings[i].szArray)
                                  [(*strings[i].pData & strings[i].sAnd) >>
                                        strings[i].sShift]))) ;

     TextOut (hdc, cxChar, cyChar * 17, szBuffer,
              wsprintf (szBuffer, "Face Name:   %10s",
                        (LPSTR) f.lf.lfFaceName)) ;
     }

HDC GetPrinterIC ()
     {
     char szPrinter [64] ;
     char *szDevice, *szDriver, *szOutput ;

     GetProfileString ("windows", "device", "", szPrinter, 64) ;

     if ((szDevice = strtok (szPrinter, "," )) &&
         (szDriver = strtok (NULL,      ", ")) &&
         (szOutput = strtok (NULL,      ", ")))

               return CreateIC (szDriver, szDevice, szOutput, NULL) ;

     return NULL ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL    bHaveInfo = FALSE ;
     static ENUMER  enumer1, enumer2 ;
     static FARPROC lpfnEnumAllFaces, lpfnEnumAllFonts ;
     static short   cxChar, cyChar, nCurrent ;
     static WORD    wCurrentDC = IDM_SCREEN ;
     HANDLE         hInstance ;
     HDC            hdc ;
     HFONT          hFont ;
     HMENU          hMenu ;
     FONT FAR       *font ;
     LPSTR          lpFaces ;
     PAINTSTRUCT    ps ;
     short          i ;
     TEXTMETRIC     tm ;

     switch (message)
          {
          case WM_CREATE:
               hInstance = ((LPCREATESTRUCT) lParam)-> hInstance ;
               lpfnEnumAllFaces = MakeProcInstance (EnumAllFaces, hInstance)
               lpfnEnumAllFonts = MakeProcInstance (EnumAllFonts, hInstance)

               hdc = GetDC (hwnd) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

               GetTextMetrics (hdc, (LPTEXTMETRIC) &tm) ;
               cxChar = tm.tmAveCharWidth ;
               cyChar = tm.tmHeight + tm.tmExternalLeading ;

               ReleaseDC (hwnd, hdc) ;
               return 0 ;

          case WM_COMMAND:
               if (wParam == IDM_EXIT)
                    {
                    SendMessage (hwnd, WM_CLOSE, 0, 0L) ;
                    return 0 ;
                    }
               else if (wParam == wCurrentDC)
                    return 0 ;

               hMenu = GetMenu (hwnd) ;
               CheckMenuItem (hMenu, wCurrentDC, MF_UNCHECKED) ;
               CheckMenuItem (hMenu, wCurrentDC = wParam, MF_CHECKED) ;

                                        // fall through

          case WM_DEVMODECHANGE:
          case WM_FONTCHANGE:
               bHaveInfo = FALSE ;
               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_PAINT:
               if (!bHaveInfo)
                    {
                    if (enumer2.hGMem)
                         GlobalFree (enumer2.hGMem) ;

                    enumer1.hGMem  = GlobalAlloc (GHND, 1L) ;
                    enumer1.nCount = 0 ;

                    enumer2.hGMem  = GlobalAlloc (GHND, 1L) ;
                    enumer2.nCount = 0 ;

                    if (NULL == enumer1.hGMem || NULL == enumer2.hGMem)
                         goto MEMORY_ERROR ;

                    if (wCurrentDC == IDM_SCREEN)
                         hdc = CreateIC ("DISPLAY", NULL, NULL, NULL) ;
                    else
                         hdc = GetPrinterIC () ;

                    if (hdc)
                         {
                         if (0 == EnumFonts (hdc, NULL, lpfnEnumAllFaces,
                                                  (LPSTR) &enumer1))
                              goto MEMORY_ERROR ;

                         lpFaces = GlobalLock (enumer1.hGMem) ;

                         for (i = 0 ; i < enumer1.nCount ; i++)
                              if (0 == EnumFonts (hdc,
                                             lpFaces + i * LF_FACESIZE,
                                             lpfnEnumAllFonts,
                                             (LPSTR) &enumer2))
                                   goto MEMORY_ERROR ;

                         GlobalUnlock (enumer1.hGMem) ;
                         enumer2.nCount-- ;

                         DeleteDC (hdc) ;
                         bHaveInfo = TRUE ;
                         }
                    GlobalFree (enumer1.hGMem) ;
                    SetScrollRange (hwnd, SB_VERT, 0, enumer2.nCount, FALSE)
                    SetScrollPos   (hwnd, SB_VERT, nCurrent = 0, TRUE) ;
                    }

               hdc = BeginPaint (hwnd, &ps) ;

               if (bHaveInfo)
                    {
                    SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

                    font = (FONT FAR *) GlobalLock (enumer2.hGMem) + nCurrent
                    Display (hdc, cxChar, cyChar, font) ;

                    hFont = SelectObject (hdc, CreateFontIndirect (&font->lf)

                    TextOut (hdc, 1 * cxChar, 19 * cyChar,
                        "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz
                         52) ;

                    GlobalUnlock (enumer2.hGMem) ;
                    DeleteObject (SelectObject (hdc, hFont)) ;
                    }

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_KEYDOWN:
               switch (wParam)
                    {
                    case VK_HOME:
                         SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0L) ;
                         break ;
                    case VK_END:
                         SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0L) ;
                         break ;
                    case VK_LEFT:
                    case VK_UP:
                    case VK_PRIOR:
                         SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0L) ;
                         break ;
                    case VK_RIGHT:
                    case VK_DOWN:
                    case VK_NEXT:
                         SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0L) ;
                         break ;
                    default:
                         return 0 ;
                    }
               return 0 ;

          case WM_VSCROLL:
               switch (wParam)
                    {
                    case SB_TOP:
                         nCurrent = 0 ;
                         break ;
                    case SB_BOTTOM:
                         nCurrent = enumer2.nCount ;
                         break ;
                    case SB_LINEUP:
                    case SB_PAGEUP:
                         nCurrent -- ;
                         break ;
                    case SB_LINEDOWN:
                    case SB_PAGEDOWN:
                         nCurrent ++ ;
                         break ;
                    case SB_THUMBPOSITION:
                         nCurrent = LOWORD (lParam) ;
                         break ;
                    default:
                         return 0 ;
                    }
               nCurrent = min (max (0, nCurrent), enumer2.nCount) ;
               SetScrollPos (hwnd, SB_VERT, nCurrent, TRUE) ;
               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          MEMORY_ERROR:
               MessageBox (hwnd, "Cannot allocate memory, must end.",
                    szAppName, MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL) ;

                                             // fall through
          case WM_CLOSE:
               DestroyWindow (hwnd) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


FORMFEED.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP15\FORMFEED.C

/*---------------------------------------------
   FORMFEED.C -- Advances printer to next page
                 (c) Charles Petzold, 1990
  ---------------------------------------------*/

#include <windows.h>
#include <string.h>

HDC  GetPrinterDC (void) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szMsg [] = "FormFeed" ;
     HDC         hdcPrint ;

     if (hdcPrint = GetPrinterDC ())
          {
          if (Escape (hdcPrint, STARTDOC, sizeof szMsg - 1, szMsg, NULL) > 0)
               if (Escape (hdcPrint, NEWFRAME, 0, NULL, NULL) > 0)
                    Escape (hdcPrint, ENDDOC, 0, NULL, NULL) ;

          DeleteDC (hdcPrint) ;
          }
     return FALSE ;
     }

HDC GetPrinterDC (void)
     {
     static char szPrinter [80] ;
     char        *szDevice, *szDriver, *szOutput ;

     GetProfileString ("windows", "device", ",,,", szPrinter, 80) ;

     if ((szDevice = strtok (szPrinter, "," )) &&
         (szDriver = strtok (NULL,      ", ")) &&
         (szOutput = strtok (NULL,      ", ")))

               return CreateDC (szDriver, szDevice, szOutput, NULL) ;

     return 0 ;
     }


FREEMEM.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP05\FREEMEM.C

/*------------------------------------------
   FREEMEM.C -- Free Memory Display Program
                (c) Charles Petzold, 1990
  ------------------------------------------*/

#include <windows.h>
#include <stdio.h>
#define ID_TIMER    1

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "FreeMem" ;
     HDC         hdc ;
     HWND        hwnd ;
     MSG         msg ;
     TEXTMETRIC  tm ;
     WNDCLASS    wndclass ;

     if (hPrevInstance)
          return FALSE ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = NULL ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     !RegisterClass (&wndclass) ;

     hwnd = CreateWindow (szAppName, "Free Memory",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     hdc = GetDC (hwnd) ;
     GetTextMetrics (hdc, &tm) ;
     ReleaseDC (hwnd, hdc) ;

     if (4 * tm.tmAveCharWidth > GetSystemMetrics (SM_CXICON) ||
               2 * tm.tmHeight > GetSystemMetrics (SM_CYICON))
          {
          MessageBox (hwnd, "Icon size too small for display!",
                      szAppName, MB_ICONEXCLAMATION | MB_OK) ;
          return FALSE ;
          }

     if (!SetTimer (hwnd, ID_TIMER, 1000, NULL))
          {
          MessageBox (hwnd, "Too many clocks or timers!",
                      szAppName, MB_ICONEXCLAMATION | MB_OK) ;
          return FALSE ;
          }

     ShowWindow (hwnd, SW_SHOWMINNOACTIVE) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static DWORD  dwFreeMem, dwPrevMem ;
     static RECT   rect ;
     char          cBuffer [20] ;
     HDC           hdc ;
     PAINTSTRUCT   ps ;

     switch (message)
          {
          case WM_TIMER:
               dwFreeMem = GetFreeSpace (0) ;

               if (dwFreeMem != dwPrevMem)
                    InvalidateRect (hwnd, NULL, TRUE) ;

               dwPrevMem = dwFreeMem ;
               return 0 ;

          case WM_SIZE:
               GetClientRect (hwnd, &rect) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               DrawText (hdc, cBuffer,
                         sprintf (cBuffer, "%.2f megs",
                                  dwFreeMem / 1024.0 / 1024.0),
                         &rect, DT_WORDBREAK) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_QUERYOPEN:
               return 0 ;

          case WM_DESTROY:
               KillTimer (hwnd, ID_TIMER) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


GRAFMENU.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP09\GRAFMENU.C

/*----------------------------------------------
   GRAFMENU.C -- Demonstrates Bitmap Menu Items
                 (c) Charles Petzold, 1990
  ----------------------------------------------*/

#include <windows.h>
#include <string.h>
#include "grafmenu.h"

long FAR PASCAL WndProc  (HWND, WORD, WORD, LONG) ;
HBITMAP StretchBitmap (HBITMAP) ;
HBITMAP GetBitmapFont (int) ;

char szAppName [] = "GrafMenu" ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HBITMAP  hBitmapHelp, hBitmapFile, hBitmapEdit,
              hBitmapFont, hBitmapPopFont [3] ;
     HMENU    hMenu, hMenuPopup ;
     HWND     hwnd ;
     int      i ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hMenu = CreateMenu () ;

     hMenuPopup = LoadMenu (hInstance, "MenuFile") ;
     hBitmapFile = StretchBitmap (LoadBitmap (hInstance, "BitmapFile")) ;
     AppendMenu (hMenu, MF_BITMAP | MF_POPUP, hMenuPopup,
                 (LPSTR) (LONG) hBitmapFile);

     hMenuPopup = LoadMenu (hInstance, "MenuEdit") ;
     hBitmapEdit = StretchBitmap (LoadBitmap (hInstance, "BitmapEdit")) ;
     AppendMenu (hMenu, MF_BITMAP | MF_POPUP, hMenuPopup,
                 (LPSTR) (LONG) hBitmapEdit);

     hMenuPopup = CreateMenu () ;

     for (i = 0 ; i < 3 ; i++)
          {
          hBitmapPopFont [i] = GetBitmapFont (i) ;
          AppendMenu (hMenuPopup, MF_BITMAP, IDM_COUR + i,
                      (LPSTR) (LONG) hBitmapPopFont [i]) ;
          }

     hBitmapFont = StretchBitmap (LoadBitmap (hInstance, "BitmapFont")) ;
     AppendMenu (hMenu, MF_BITMAP | MF_POPUP, hMenuPopup,
                 (LPSTR) (LONG) hBitmapFont);

     hwnd = CreateWindow (szAppName, "Bitmap Menu Demonstration",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, hMenu, hInstance, NULL) ;

     hMenu = GetSystemMenu (hwnd, FALSE);
     hBitmapHelp = StretchBitmap (LoadBitmap (hInstance, "BitmapHelp")) ;
     AppendMenu (hMenu, MF_SEPARATOR, NULL,     NULL) ;
     AppendMenu (hMenu, MF_BITMAP,    IDM_HELP, (LPSTR) (LONG) hBitmapHelp) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

     DeleteObject (hBitmapHelp) ;
     DeleteObject (hBitmapEdit) ;
     DeleteObject (hBitmapFile) ;
     DeleteObject (hBitmapFont) ;

     for (i = 0 ; i < 3 ; i++)
          DeleteObject (hBitmapPopFont [i]) ;

     return msg.wParam ;
     }

HBITMAP StretchBitmap (HBITMAP hBitmap1)
     {
     BITMAP     bm1, bm2 ;
     HBITMAP    hBitmap2 ;
     HDC        hdc, hdcMem1, hdcMem2 ;
     TEXTMETRIC tm ;

     hdc = CreateIC ("DISPLAY", NULL, NULL, NULL) ;
     GetTextMetrics (hdc, &tm) ;
     hdcMem1 = CreateCompatibleDC (hdc) ;
     hdcMem2 = CreateCompatibleDC (hdc) ;
     DeleteDC (hdc) ;

     GetObject (hBitmap1, sizeof (BITMAP), (LPSTR) &bm1) ;

     bm2 = bm1 ;
     bm2.bmWidth      = (tm.tmAveCharWidth * bm2.bmWidth)  / 4 ;
     bm2.bmHeight     = (tm.tmHeight       * bm2.bmHeight) / 8 ;
     bm2.bmWidthBytes = ((bm2.bmWidth + 15) / 16) * 2 ;

     hBitmap2 = CreateBitmapIndirect (&bm2) ;

     SelectObject (hdcMem1, hBitmap1) ;
     SelectObject (hdcMem2, hBitmap2) ;

     StretchBlt (hdcMem2, 0, 0, bm2.bmWidth, bm2.bmHeight,
                 hdcMem1, 0, 0, bm1.bmWidth, bm1.bmHeight, SRCCOPY) ;

     DeleteDC (hdcMem1) ;
     DeleteDC (hdcMem2) ;
     DeleteObject (hBitmap1) ;

     return hBitmap2 ;
     }

HBITMAP GetBitmapFont (int i)
     {
     static  struct
          {
          BYTE lfPitchAndFamily ;
          BYTE lfFaceName [LF_FACESIZE] ;
          char *szMenuText ;
          }
          lfSet [3] =
          {
          FIXED_PITCH    | FF_MODERN, "Courier",   "Courier",
          VARIABLE_PITCH | FF_SWISS,  "Helvetica", "Helvetica",
          VARIABLE_PITCH | FF_ROMAN,  "Tms Rmn",   "Times Roman"
          } ;
     DWORD   dwSize ;
     HBITMAP hBitmap ;
     HDC     hdc, hdcMem ;
     HFONT   hFont ;
     LOGFONT lf ;

     hFont = GetStockObject (SYSTEM_FONT) ;
     GetObject (hFont, sizeof (LOGFONT), (LPSTR) &lf) ;

     lf.lfHeight *= 2 ;
     lf.lfWidth  *= 2 ;
     lf.lfPitchAndFamily = lfSet[i].lfPitchAndFamily ;
     strcpy (lf.lfFaceName, lfSet[i].lfFaceName) ;

     hdc = CreateIC ("DISPLAY", NULL, NULL, NULL) ;
     hdcMem = CreateCompatibleDC (hdc) ;
     SelectObject (hdcMem, CreateFontIndirect (&lf)) ;
     dwSize = GetTextExtent (hdcMem, lfSet[i].szMenuText,
                             strlen (lfSet[i].szMenuText)) ;

     hBitmap = CreateBitmap (LOWORD (dwSize)-1, HIWORD (dwSize), 1, 1, NULL)
     SelectObject (hdcMem, hBitmap) ;


     TextOut (hdcMem, 0, 0, lfSet[i].szMenuText,
                            strlen (lfSet[i].szMenuText)) ;

     DeleteObject (SelectObject (hdcMem, hFont)) ;
     DeleteDC (hdcMem) ;
     DeleteDC (hdc) ;

     return hBitmap ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     HMENU  hMenu ;
     static short nCurrentFont = IDM_COUR ;

     switch (message)
          {
          case WM_CREATE:
               CheckMenuItem (GetMenu (hwnd), nCurrentFont, MF_CHECKED) ;
               return 0 ;

          case WM_SYSCOMMAND:
               switch (wParam)
                    {
                    case IDM_HELP:
                         MessageBox (hwnd, "Help not yet implemented.",
                                   szAppName, MB_OK | MB_ICONEXCLAMATION) ;
                         return 0 ;
                    }
               break ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_NEW:
                    case IDM_OPEN:
                    case IDM_SAVE:
                    case IDM_SAVEAS:
                    case IDM_UNDO:
                    case IDM_CUT:
                    case IDM_COPY:
                    case IDM_PASTE:
                    case IDM_CLEAR:
                         MessageBeep (0) ;
                         return 0 ;

                    case IDM_COUR:
                    case IDM_HELV:
                    case IDM_TMSRMN:
                         hMenu = GetMenu (hwnd) ;
                         CheckMenuItem (hMenu, nCurrentFont, MF_UNCHECKED) ;
                         nCurrentFont = wParam ;
                         CheckMenuItem (hMenu, nCurrentFont, MF_CHECKED) ;
                         return 0 ;
                    }
               break ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


HEAD.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP06\HEAD.C

/*---------------------------------------------
   HEAD.C -- Displays beginning (head) of file
             (c) Charles Petzold, 1990
  ---------------------------------------------*/

#include <windows.h>
#include <io.h>
#include <string.h>
#include <direct.h>

#define  MAXPATH     100
#define  MAXREAD    2048

long FAR PASCAL WndProc  (HWND, WORD, WORD, LONG) ;
long FAR PASCAL ListProc (HWND, WORD, WORD, LONG) ;

char    sReadBuffer [MAXREAD] ;
FARPROC lpfnOldList ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "Head" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = COLOR_WINDOW + 1 ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "File Head",
                          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL     bValidFile ;
     static char     szFile [16] ;
     static HWND     hwndList, hwndText ;
     static OFSTRUCT ofs ;
     static RECT     rect ;
     char            szBuffer [MAXPATH + 1] ;
     HDC             hdc ;
     int             iHandle, i, iCount ;
     PAINTSTRUCT     ps ;
     TEXTMETRIC      tm ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
               GetTextMetrics (hdc, &tm) ;
               ReleaseDC (hwnd, hdc) ;

               rect.left = 20 * tm.tmAveCharWidth ;
               rect.top  =  3 * tm.tmHeight ;

               hwndList = CreateWindow ("listbox", NULL,
                              WS_CHILDWINDOW | WS_VISIBLE | LBS_STANDARD,
                              tm.tmAveCharWidth, tm.tmHeight * 3,
                              tm.tmAveCharWidth * 13 +
                                   GetSystemMetrics (SM_CXVSCROLL),
                              tm.tmHeight * 10,
                              hwnd, 1,
                              GetWindowWord (hwnd, GWW_HINSTANCE), NULL) ;

               hwndText = CreateWindow ("static", getcwd (szBuffer, MAXPATH),
                              WS_CHILDWINDOW | WS_VISIBLE | SS_LEFT,
                              tm.tmAveCharWidth,           tm.tmHeight,
                              tm.tmAveCharWidth * MAXPATH, tm.tmHeight,
                              hwnd, 2,
                              GetWindowWord (hwnd, GWW_HINSTANCE), NULL) ;

               lpfnOldList = (FARPROC) GetWindowLong (hwndList, GWL_WNDPROC)

               SetWindowLong (hwndList, GWL_WNDPROC,
                         (LONG) MakeProcInstance ((FARPROC) ListProc,
                                   GetWindowWord (hwnd, GWW_HINSTANCE))) ;

               SendMessage (hwndList, LB_DIR, 0x37, (LONG) (LPSTR) "*.*") ;
               return 0 ;

          case WM_SIZE:
               rect.right  = LOWORD (lParam) ;
               rect.bottom = HIWORD (lParam) ;
               return 0 ;

          case WM_SETFOCUS:
               SetFocus (hwndList) ;
               return 0 ;

          case WM_COMMAND:
               if (wParam == 1 && HIWORD (lParam) == LBN_DBLCLK)
                    {
                    if (LB_ERR == (i = (WORD) SendMessage (hwndList,
                                                  LB_GETCURSEL, 0, 0L)))
                         break ;

                    SendMessage (hwndList, LB_GETTEXT, i,
                                        (LONG) (char far *) szBuffer) ;

                    if (-1 != OpenFile (szBuffer, &ofs, OF_EXIST | OF_READ))
                         {
                         bValidFile = TRUE ;
                         strcpy (szFile, szBuffer) ;
                         getcwd (szBuffer, MAXPATH) ;
                         if (szBuffer [strlen (szBuffer) - 1] != '\\')
                              strcat (szBuffer, "\\") ;
                         SetWindowText (hwndText, strcat (szBuffer, szFile))
                         }
                    else
                         {
                         bValidFile = FALSE ;
                         szBuffer [strlen (szBuffer) - 1] = '\0' ;
                         chdir (szBuffer + 1) ;
                         getcwd (szBuffer, MAXPATH) ;
                         SetWindowText (hwndText, szBuffer) ;
                         SendMessage (hwndList, LB_RESETCONTENT, 0, 0L) ;
                         SendMessage (hwndList, LB_DIR, 0x37,
                                      (LONG) (LPSTR) "*.*") ;
                         }
                    InvalidateRect (hwnd, NULL, TRUE) ;
                    }
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;
               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
               SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ;
               SetBkColor   (hdc, GetSysColor (COLOR_WINDOW)) ;

               if (bValidFile && -1 != (iHandle =
                         OpenFile (szFile, &ofs, OF_REOPEN | OF_READ)))
                    {
                    i = read (iHandle, sReadBuffer, MAXREAD) ;
                    close (iHandle) ;
                    DrawText (hdc, sReadBuffer, i, &rect, DT_WORDBREAK |
                                   DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX) ;
                    }
               else
                    bValidFile = FALSE ;

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

long FAR PASCAL ListProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     if (message == WM_KEYDOWN && wParam == VK_RETURN)

          SendMessage (GetParent (hwnd), WM_COMMAND, 1,
                         MAKELONG (hwnd, LBN_DBLCLK)) ;

     return CallWindowProc (lpfnOldList, hwnd, message, wParam, lParam) ;
     }


HELLOWIN.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP01\HELLOWIN.C

/*--------------------------------------------------------
   HELLOWIN.C -- Displays "Hello, Windows" in client area
                 (c) Charles Petzold, 1990
  --------------------------------------------------------*/

#include <windows.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdParam, int nCmdShow)
     {
     static char szAppName[] = "HelloWin" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
    }

     hwnd = CreateWindow (szAppName,         // window class name
        "The Hello Program",     // window caption
                    WS_OVERLAPPEDWINDOW,     // window style
                    CW_USEDEFAULT,           // initial x position
                    CW_USEDEFAULT,           // initial y position
                    CW_USEDEFAULT,           // initial x size
                    CW_USEDEFAULT,           // initial y size
                    NULL,                    // parent window handle
                    NULL,                    // window menu handle
                    hInstance,               // program instance handle
        NULL) ;         // creation parameters

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     HDC         hdc ;
     PAINTSTRUCT ps ;
     RECT   rect ;

     switch (message)
          {
          case WM_PAINT:
         hdc = BeginPaint (hwnd, &ps) ;

               GetClientRect (hwnd, &rect) ;

         DrawText (hdc, "Hello, Windows!", -1, &rect,
       DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;

         EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }

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


HEXCALC.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP10\HEXCALC.C

/*----------------------------------------
   HEXCALC.C -- Hexadecimal Calculator
                (c) Charles Petzold, 1990
  ----------------------------------------*/

#include <windows.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "HexCalc" ;
     HWND        hwnd ;
     MSG         msg;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style          = CS_HREDRAW | CS_VREDRAW;
          wndclass.lpfnWndProc    = WndProc ;
          wndclass.cbClsExtra     = 0 ;
          wndclass.cbWndExtra     = DLGWINDOWEXTRA ;
          wndclass.hInstance      = hInstance ;
          wndclass.hIcon          = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground  = COLOR_WINDOW + 1 ;
          wndclass.lpszMenuName   = NULL ;
          wndclass.lpszClassName  = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateDialog (hInstance, szAppName, 0, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;

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

void ShowNumber (HWND hwnd, DWORD dwNumber)
     {
     char szBuffer [20] ;

     SetDlgItemText (hwnd, VK_ESCAPE, strupr (ltoa (dwNumber, szBuffer, 16)))
     }

DWORD CalcIt (DWORD dwFirstNum, short nOperation, DWORD dwNum)
     {
     switch (nOperation)
          {
          case '=' : return dwNum ;
          case '+' : return dwFirstNum +  dwNum ;
          case '-' : return dwFirstNum -  dwNum ;
          case '*' : return dwFirstNum *  dwNum ;
          case '&' : return dwFirstNum &  dwNum ;
          case '|' : return dwFirstNum |  dwNum ;
          case '^' : return dwFirstNum ^  dwNum ;
          case '<' : return dwFirstNum << dwNum ;
          case '>' : return dwFirstNum >> dwNum ;
          case '/' : return dwNum ? dwFirstNum / dwNum : ULONG_MAX ;
          case '%' : return dwNum ? dwFirstNum % dwNum : ULONG_MAX ;
          default  : return 0L ;
          }
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL  bNewNumber = TRUE ;
     static DWORD dwNumber, dwFirstNum ;
     static short nOperation = '=' ;
     HWND         hButton ;

     switch (message)
          {
          case WM_KEYDOWN:                   // left arrow --> backspace
               if (wParam != VK_LEFT)
                    break ;
               wParam = VK_BACK ;
                                             // fall through
          case WM_CHAR:
               if ((wParam = toupper (wParam)) == VK_RETURN)
                    wParam = '=' ;

               if (hButton = GetDlgItem (hwnd, wParam))
                    {
                    SendMessage (hButton, BM_SETSTATE, 1, 0L) ;
                    SendMessage (hButton, BM_SETSTATE, 0, 0L) ;
                    }
               else
                    {
                    MessageBeep (0) ;
                    break ;
                    }
                                             // fall through
          case WM_COMMAND:
               SetFocus (hwnd) ;

               if (wParam == VK_BACK)                  // backspace
                    ShowNumber (hwnd, dwNumber /= 16) ;

               else if (wParam == VK_ESCAPE)           // escape
                    ShowNumber (hwnd, dwNumber = 0L) ;

               else if (isxdigit (wParam))             // hex digit
                    {
                    if (bNewNumber)
                         {
                         dwFirstNum = dwNumber ;
                         dwNumber = 0L ;
                         }
                    bNewNumber = FALSE ;

                    if (dwNumber <= ULONG_MAX >> 4)
                         ShowNumber (hwnd, dwNumber = 16 * dwNumber + wParam
                              (isdigit (wParam) ? '0' : 'A' - 10)) ;
                    else
                         MessageBeep (0) ;
                    }
               else                                    // operation
                    {
                    if (!bNewNumber)
                         ShowNumber (hwnd, dwNumber =
                              CalcIt (dwFirstNum, nOperation, dwNumber)) ;
                    bNewNumber = TRUE ;
                    nOperation = wParam ;
                    }
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


JUSTIFY.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP14\JUSTIFY.C

/*----------------------------------------
   JUSTIFY.C -- Justified Type Program
                (c) Charles Petzold, 1990
  ----------------------------------------*/

#include <windows.h>
#include <string.h>
#include "justify.h"

typedef struct
     {
     short nNumFaces ;
     char  szFaceNames [MAX_FACES] [LF_FACESIZE] ;
     }
     ENUMFACE ;

typedef struct
     {
     short      nNumSizes ;
     short      xLogPixPerInch ;
     short      yLogPixPerInch ;
     LOGFONT    lf [MAX_SIZES] ;
     TEXTMETRIC tm [MAX_SIZES] ;
     }
     ENUMSIZE ;

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
int  FAR PASCAL EnumAllFaces (LPLOGFONT, LPTEXTMETRIC, short, ENUMFACE FAR *)
int  FAR PASCAL EnumAllSizes (LPLOGFONT, LPTEXTMETRIC, short, ENUMSIZE FAR *)

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static   char szAppName[] = "Justify" ;
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }
     hwnd = CreateWindow (szAppName, "Justified Type",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

int FAR PASCAL EnumAllFaces (LPLOGFONT lplf, LPTEXTMETRIC lptm,
                             short nFontType, ENUMFACE FAR *lpef)
     {
     if (nFontType & RASTER_FONTTYPE)
          {
          lstrcpy (lpef->szFaceNames[lpef->nNumFaces], lplf->lfFaceName) ;
          if (++lpef->nNumFaces == MAX_FACES)
               return 0 ;
          }
     return 1 ;
     }

int FAR PASCAL EnumAllSizes (LPLOGFONT lplf, LPTEXTMETRIC lptm,
                             short nFontType, ENUMSIZE FAR *lpes)
     {
     if (lpes->xLogPixPerInch == lptm->tmDigitizedAspectX &&
         lpes->yLogPixPerInch == lptm->tmDigitizedAspectY)
          {
          lpes->lf [lpes->nNumSizes] = *lplf ;
          lpes->tm [lpes->nNumSizes] = *lptm ;
          if (++lpes->nNumSizes == MAX_SIZES)
               return 0 ;
          }
     return 1 ;
     }

short MakeSizeMenu (HWND hwnd, FARPROC lpfnEnumAllSizes,
                    ENUMSIZE *pes, char *szFaceName)
     {
     static LOGFONT lfBlank ;
     char           szBuffer[20] ;
     HDC            hdc ;
     HMENU          hPopup ;
     short          i ;

     hdc = GetDC (hwnd) ;
     hPopup = GetSubMenu (GetMenu (hwnd), SIZE_MENU) ;

     pes->nNumSizes = 0 ;
     EnumFonts (hdc, szFaceName, lpfnEnumAllSizes, (LPSTR) pes) ;
     ReleaseDC (hwnd, hdc) ;

     while (GetMenuItemCount (hPopup) > 0)
          DeleteMenu (hPopup, 0, MF_BYPOSITION) ;

     if (pes->nNumSizes)
          for (i = 0 ; i < pes->nNumSizes ; i++)
               {
               wsprintf (szBuffer, "%i  %2d / %2d", i + 1,
                    (pes->tm[i].tmHeight - pes->tm[i].tmInternalLeading + 10)
                                                                        / 20,
                    (pes->tm[i].tmHeight + 10) / 20) ;
               AppendMenu (hPopup, 0, IDM_ISIZE + i, szBuffer) ;
               }
     else           /* no fonts found that match aspect ratio of display */
          {
          pes->lf[0] = lfBlank ;
          strcpy (pes->lf[0].lfFaceName, szFaceName) ;
          AppendMenu (hPopup, 0, IDM_ISIZE, "Default") ;
          }

     CheckMenuItem (hPopup, IDM_ISIZE, MF_CHECKED) ;
     return 0 ;
     }

void DrawRuler (HDC hdc, POINT ptClient)
     {
     static short nRuleSize [16] = { 360, 72, 144, 72, 216, 72, 144, 72,
                                     288, 72, 144, 72, 216, 72, 144, 72 } ;
     short        i, j ;

     MoveTo (hdc, 0,          -360) ;
     LineTo (hdc, ptClient.x, -360) ;
     MoveTo (hdc, -360,          0) ;
     LineTo (hdc, -360, ptClient.y) ;

     for (i = 0, j = 0 ; i <= ptClient.x ; i += 1440 / 16, j++)
          {
          MoveTo (hdc, i, -360) ;
          LineTo (hdc, i, -360 - nRuleSize [j % 16]) ;
          }
     for (i = 0, j = 0 ; i <= ptClient.y ; i += 1440 / 16, j++)
          {
          MoveTo (hdc, -360, i) ;
          LineTo (hdc, -360 - nRuleSize [j % 16], i) ;
          }
     }

void Justify (HDC hdc, HANDLE hResource, POINT ptClient, short nCurAlign)
     {
     DWORD  dwExtent ;
     LPSTR  lpText, lpBegin, lpEnd ;
     short  i, xStart, yStart, nBreakCount ;

     lpText = LockResource (hResource) ;

     yStart = 0 ;
     do                            // for each text line
          {
          nBreakCount = 0 ;
          while (*lpText == ' ')   // skip over leading blanks
               lpText++ ;
          lpBegin = lpText ;

          do                       // until the line is known
               {
               lpEnd = lpText ;

               while (*lpText != '\0' && *lpText++ != ' ') ;
               if (*lpText == '\0')
                    break ;
                                   // for each space, calculate extents
               nBreakCount++ ;
               SetTextJustification (hdc, 0, 0) ;
               dwExtent = GetTextExtent (hdc, lpBegin, lpText - lpBegin - 1)
               }
          while (LOWORD (dwExtent) < ptClient.x) ;

          nBreakCount-- ;
          while (*(lpEnd - 1) == ' ')   // eliminate trailing blanks
               {
               lpEnd-- ;
               nBreakCount-- ;
               }

          if (*lpText == '\0' || nBreakCount <= 0)
               lpEnd = lpText ;

          SetTextJustification (hdc, 0, 0) ;
          dwExtent = GetTextExtent (hdc, lpBegin, lpEnd - lpBegin) ;

          if (nCurAlign == IDM_LEFT)         // use alignment for xStart
               xStart = 0 ;

          else if (nCurAlign == IDM_RIGHT)
               xStart = ptClient.x - LOWORD (dwExtent) ;

          else if (nCurAlign == IDM_CENTER)
               xStart = (ptClient.x - LOWORD (dwExtent)) / 2 ;

          else
               {
               if (*lpText != '\0' && nBreakCount > 0)
                    SetTextJustification (hdc, ptClient.x - LOWORD (dwExtent)
                                                  nBreakCount) ;
               xStart = 0 ;
               }

          TextOut (hdc, xStart, yStart, lpBegin, lpEnd - lpBegin) ;
          yStart += HIWORD (dwExtent) ;
          lpText = lpEnd ;
          }
     while (*lpText && yStart < ptClient.y) ;

     GlobalUnlock (hResource) ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static ENUMFACE ef ;
     static ENUMSIZE es ;
     static FARPROC  lpfnEnumAllFaces, lpfnEnumAllSizes ;
     static HANDLE   hResource ;
     static POINT    ptClient ;
     static short    nCurSize, nCurFace, nCurAttr, nCurAlign = IDM_LEFT ;
     HANDLE          hInstance ;
     HDC             hdc ;
     HFONT           hFont ;
     HMENU           hMenu, hPopup ;
     PAINTSTRUCT     ps ;
     short           i ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;
               es.xLogPixPerInch = GetDeviceCaps (hdc, LOGPIXELSX) ;
               es.yLogPixPerInch = GetDeviceCaps (hdc, LOGPIXELSY) ;

                              // Set Map Mode

               SetMapMode (hdc, MM_ANISOTROPIC) ;
               SetWindowExt (hdc, 1440, 1440) ;
               SetViewportExt (hdc, es.xLogPixPerInch, es.yLogPixPerInch) ;
               SetWindowOrg (hdc, -720, -720) ;

                              // MakeProcInstance for 2 routines

               hInstance = ((LPCREATESTRUCT) lParam)-> hInstance ;
               lpfnEnumAllFaces = MakeProcInstance (EnumAllFaces, hInstance)
               lpfnEnumAllSizes = MakeProcInstance (EnumAllSizes, hInstance)

                              // Enumerate the Font Faces

               EnumFonts (hdc, NULL, lpfnEnumAllFaces, (LPSTR) &ef) ;
               ReleaseDC (hwnd, hdc) ;

                              // Initialize the Menus

               hMenu  = GetMenu (hwnd) ;
               hPopup = CreateMenu () ;

               for (i = 0 ; i < ef.nNumFaces ; i++)
                    AppendMenu (hPopup, 0, IDM_IFACE + i, ef.szFaceNames[i])

               ModifyMenu (hMenu, IDM_FACE, MF_POPUP, hPopup, "&FaceName") ;
               CheckMenuItem (hMenu, IDM_IFACE, MF_CHECKED) ;

               nCurSize = MakeSizeMenu (hwnd, lpfnEnumAllSizes, &es,
                                   ef.szFaceNames [nCurFace]) ;

                              // Load the Text Resource

               hResource = LoadResource (hInstance,
                           FindResource (hInstance, "Ismael", "TEXT")) ;
               return 0 ;

          case WM_SIZE:
               hdc = GetDC (hwnd) ;
               ptClient = MAKEPOINT (lParam) ;
               DPtoLP (hdc, &ptClient, 1) ;
               ptClient.x -= 360 ;
               ReleaseDC (hwnd, hdc) ;
               return 0 ;

          case WM_COMMAND:
               hMenu = GetMenu (hwnd) ;

               if (wParam >= IDM_IFACE && wParam < IDM_IFACE + MAX_FACES)
                    {
                    CheckMenuItem (hMenu, nCurFace + IDM_IFACE, MF_UNCHECKED)
                    CheckMenuItem (hMenu, wParam, MF_CHECKED) ;
                    nCurFace = wParam - IDM_IFACE ;

                    nCurSize = MakeSizeMenu (hwnd, lpfnEnumAllSizes, &es,
                                        ef.szFaceNames [nCurFace]) ;
                    }

               else if (wParam >= IDM_ISIZE && wParam < IDM_ISIZE + MAX_SIZES
                    {
                    CheckMenuItem (hMenu, nCurSize + IDM_ISIZE, MF_UNCHECKED)
                    CheckMenuItem (hMenu, wParam, MF_CHECKED) ;
                    nCurSize = wParam - IDM_ISIZE ;
                    }

               else switch (wParam)
                    {
                    case IDM_BOLD:
                    case IDM_ITALIC:
                    case IDM_STRIKE:
                    case IDM_UNDER:
                         CheckMenuItem (hMenu, wParam, MF_CHECKED &
                              GetMenuState (hMenu, wParam, MF_BYCOMMAND) ?
                                   MF_UNCHECKED : MF_CHECKED) ;
                         nCurAttr ^= wParam ;
                         break ;

                    case IDM_NORM:
                         nCurAttr = 0 ;
                         CheckMenuItem (hMenu, IDM_BOLD,   MF_UNCHECKED) ;
                         CheckMenuItem (hMenu, IDM_ITALIC, MF_UNCHECKED) ;
                         CheckMenuItem (hMenu, IDM_STRIKE, MF_UNCHECKED) ;
                         CheckMenuItem (hMenu, IDM_UNDER,  MF_UNCHECKED) ;
                         break ;

                    case IDM_LEFT:
                    case IDM_RIGHT:
                    case IDM_CENTER:
                    case IDM_JUST:
                         CheckMenuItem (hMenu, nCurAlign, MF_UNCHECKED) ;
                         nCurAlign = wParam ;
                         CheckMenuItem (hMenu, nCurAlign, MF_CHECKED) ;
                         break ;
                    }
               InvalidateRect (hwnd, NULL, TRUE) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               es.lf[nCurSize].lfWeight    = nCurAttr & IDM_BOLD ? 700 : 400
               es.lf[nCurSize].lfItalic    = (BYTE) (nCurAttr & IDM_ITALIC) ;
               es.lf[nCurSize].lfUnderline = (BYTE) (nCurAttr & IDM_UNDER) ;
               es.lf[nCurSize].lfStrikeOut = (BYTE) (nCurAttr & IDM_STRIKE) ;

               hFont = CreateFontIndirect (&es.lf[nCurSize]) ;
               hFont = SelectObject (hdc, hFont) ;

               DrawRuler (hdc, ptClient) ;
               Justify (hdc, hResource, ptClient, nCurAlign) ;

               DeleteObject (SelectObject (hdc, hFont)) ;
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               FreeResource (hResource) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


KEYLOOK.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP03\KEYLOOK.C

/*-------------------------------------------------------
   KEYLOOK.C -- Displays Keyboard and Character Messages
                (c) Charles Petzold, 1990
  -------------------------------------------------------*/

#include <windows.h>
#include <stdio.h>

long  FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

RECT  rect ;
short cxChar, cyChar ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "KeyLook" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Keyboard Message Looker",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

void ShowKey (HWND hwnd, int iType, char *szMessage, WORD wParam, LONG lParam
     {
     static char *szFormat[2] = { "%-14s %3d    %c %6u %4d %3s %3s %4s %4s",
                                  "%-14s    %3d %c %6u %4d %3s %3s %4s %4s" }
     char        szBuffer[80] ;
     HDC         hdc ;

     ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ;
     hdc = GetDC (hwnd) ;

     SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

     TextOut (hdc, cxChar, rect.bottom - cyChar, szBuffer,
              wsprintf (szBuffer, szFormat [iType],
                        (LPSTR) szMessage, wParam,
                        (BYTE) (iType ? wParam : ' '),
                        LOWORD (lParam),
                        HIWORD (lParam) & 0xFF,
                        (LPSTR) (0x01000000 & lParam ? "Yes"  : "No"),
                        (LPSTR) (0x20000000 & lParam ? "Yes"  : "No"),
                        (LPSTR) (0x40000000 & lParam ? "Down" : "Up"),
                        (LPSTR) (0x80000000 & lParam ? "Up"   : "Down"))) ;

     ReleaseDC (hwnd, hdc) ;
     ValidateRect (hwnd, NULL) ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static char szTop[] =
                    "Message        Key Char Repeat Scan Ext ALT Prev Tran";
     static char szUnd[]=
                    "_______        ___ ____ ______ ____ ___ ___ ____ ____";
     HDC         hdc ;
     PAINTSTRUCT ps ;
     TEXTMETRIC  tm ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;

               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

               GetTextMetrics (hdc, &tm) ;
               cxChar = tm.tmAveCharWidth ;
               cyChar = tm.tmHeight ;

               ReleaseDC (hwnd, hdc) ;

               rect.top = 3 * cyChar / 2 ;
               return 0 ;

          case WM_SIZE:
               rect.right  = LOWORD (lParam) ;
               rect.bottom = HIWORD (lParam) ;
               UpdateWindow (hwnd) ;
               return 0 ;

          case WM_PAINT:
               InvalidateRect (hwnd, NULL, TRUE) ;
               hdc = BeginPaint (hwnd, &ps) ;

               SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

               SetBkMode (hdc, TRANSPARENT) ;
               TextOut (hdc, cxChar, cyChar / 2, szTop, (sizeof szTop) - 1) ;
               TextOut (hdc, cxChar, cyChar / 2, szUnd, (sizeof szUnd) - 1) ;
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_KEYDOWN:
               ShowKey (hwnd, 0, "WM_KEYDOWN", wParam, lParam) ;
               return 0 ;

          case WM_KEYUP:
               ShowKey (hwnd, 0, "WM_KEYUP", wParam, lParam) ;
               return 0 ;

          case WM_CHAR:
               ShowKey (hwnd, 1, "WM_CHAR", wParam, lParam) ;
               return 0 ;

          case WM_DEADCHAR:
               ShowKey (hwnd, 1, "WM_DEADCHAR", wParam, lParam) ;
               return 0 ;

          case WM_SYSKEYDOWN:
               ShowKey (hwnd, 0, "WM_SYSKEYDOWN", wParam, lParam) ;
               break ;        // ie, call DefWindowProc

          case WM_SYSKEYUP:
               ShowKey (hwnd, 0, "WM_SYSKEYUP", wParam, lParam) ;
               break ;        // ie, call DefWindowProc

          case WM_SYSCHAR:
               ShowKey (hwnd, 1, "WM_SYSCHAR", wParam, lParam) ;
               break ;        // ie, call DefWindowProc

          case WM_SYSDEADCHAR:
               ShowKey (hwnd, 1, "WM_SYSDEADCHAR", wParam, lParam) ;
               break ;        // ie, call DefWindowProc

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


LINEDDA.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP12\LINEDDA.C

/*----------------------------------------
   LINEDDA.C -- LineDDA Demonstration
                (c) Charles Petzold, 1990
  ----------------------------------------*/

#include <windows.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
void FAR PASCAL LineProc (short, short, LPSTR) ;

HANDLE hInst ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName[] = "LineDDA" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hInst = hInstance ;

     hwnd = CreateWindow (szAppName, "LineDDA Demonstration",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static FARPROC lpfnLineProc ;
     static short   cxClient, cyClient, xL, xR, yT, yB ;
     HDC            hdc ;
     PAINTSTRUCT    ps ;

     switch (message)
          {
          case WM_CREATE:
               lpfnLineProc = MakeProcInstance (LineProc, hInst) ;
               return 0 ;

          case WM_SIZE:
               xR = 3 * (xL = (cxClient = LOWORD (lParam)) / 4) ;
               yB = 3 * (yT = (cyClient = HIWORD (lParam)) / 4) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               LineDDA (xL, yT, xR, yT, lpfnLineProc, (LPSTR) &hdc) ;
               LineDDA (xR, yT, xR, yB, lpfnLineProc, (LPSTR) &hdc) ;
               LineDDA (xR, yB, xL, yB, lpfnLineProc, (LPSTR) &hdc) ;
               LineDDA (xL, yB, xL, yT, lpfnLineProc, (LPSTR) &hdc) ;

               LineDDA (0,       0,       xL, yT, lpfnLineProc, (LPSTR) &hdc)
               LineDDA (cxClient, 0,       xR, yT, lpfnLineProc, (LPSTR) &hdc
               LineDDA (cxClient, cyClient, xR, yB, lpfnLineProc, (LPSTR) &hd
               LineDDA (0,       cyClient, xL, yB, lpfnLineProc, (LPSTR) &hdc

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

void FAR PASCAL LineProc (short x, short y, LPSTR lpData)
     {
     static short nCounter = 0 ;

     if (nCounter == 2)
          Ellipse (* (HDC far *) lpData, x - 2, y - 2, x + 3, y + 3) ;

     nCounter = (nCounter + 1) % 4 ;
     }


MDIDEMO.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP18\MDIDEMO.C

/*--------------------------------------------------------
   MDIDEMO.C -- Multiple Document Interface Demonstration
                (c) Charles Petzold, 1990
  --------------------------------------------------------*/

#include <windows.h>
#include <stdlib.h>
#include "mdidemo.h"

long FAR PASCAL FrameWndProc  (HWND, WORD, WORD, LONG) ;
BOOL FAR PASCAL CloseEnumProc (HWND, LONG) ;
long FAR PASCAL HelloWndProc  (HWND, WORD, WORD, LONG) ;
long FAR PASCAL RectWndProc   (HWND, WORD, WORD, LONG) ;

          // structure for storing data unique to each Hello child window

typedef struct
     {
     short    nColor ;
     COLORREF clrText ;
     }
     HELLODATA ;

typedef HELLODATA NEAR *NPHELLODATA ;

          // structure for storing data unique to each Rect child window

typedef struct
     {
     short cxClient ;
     short cyClient ;
     }
     RECTDATA ;

typedef RECTDATA NEAR *NPRECTDATA ;

          // global variables

char   szFrameClass [] = "MdiFrame" ;
char   szHelloClass [] = "MdiHelloChild" ;
char   szRectClass  [] = "MdiRectChild" ;
HANDLE hInst ;
HMENU  hMenuInit, hMenuHello, hMenuRect ;
HMENU  hMenuInitWindow, hMenuHelloWindow, hMenuRectWindow ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HANDLE   hAccel ;
     HWND     hwndFrame, hwndClient ;
     MSG      msg ;
     WNDCLASS wndclass ;

     hInst = hInstance ;

     if (!hPrevInstance)
          {
                    // Register the frame window class

          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = FrameWndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = COLOR_APPWORKSPACE + 1 ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szFrameClass ;

          RegisterClass (&wndclass) ;

                    // Register the Hello child window class

          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = HelloWndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = sizeof (LOCALHANDLE) ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szHelloClass ;

          RegisterClass (&wndclass) ;

                    // Register the Rect child window class

          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = RectWndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = sizeof (LOCALHANDLE) ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szRectClass ;

          RegisterClass (&wndclass) ;
          }
               // Obtain handles to three possible menus & submenus

     hMenuInit  = LoadMenu (hInst, "MdiMenuInit") ;
     hMenuHello = LoadMenu (hInst, "MdiMenuHello") ;
     hMenuRect  = LoadMenu (hInst, "MdiMenuRect") ;

     hMenuInitWindow  = GetSubMenu (hMenuInit,   INIT_MENU_POS) ;
     hMenuHelloWindow = GetSubMenu (hMenuHello, HELLO_MENU_POS) ;
     hMenuRectWindow  = GetSubMenu (hMenuRect,   RECT_MENU_POS) ;

               // Load accelerator table

     hAccel = LoadAccelerators (hInst, "MdiAccel") ;

               // Create the frame window

     hwndFrame = CreateWindow (szFrameClass, "MDI Demonstration",
                               WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                               CW_USEDEFAULT, CW_USEDEFAULT,
                               CW_USEDEFAULT, CW_USEDEFAULT,
                               NULL, hMenuInit, hInstance, NULL) ;

     hwndClient = GetWindow (hwndFrame, GW_CHILD) ;

     ShowWindow (hwndFrame, nCmdShow) ;
     UpdateWindow (hwndFrame) ;

               // Enter the modified message loop

     while (GetMessage (&msg, NULL, 0, 0))
          {
          if (!TranslateMDISysAccel (hwndClient, &msg) &&
              !TranslateAccelerator (hwndFrame, hAccel, &msg))
               {
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
               }
          }
     return msg.wParam ;
     }

long FAR PASCAL FrameWndProc (HWND hwnd, WORD message, WORD wParam, LONG lPar
     {
     static HWND        hwndClient ;
     CLIENTCREATESTRUCT clientcreate ;
     FARPROC            lpfnEnum ;
     HWND               hwndChild, hwndNext ;
     MDICREATESTRUCT    mdicreate ;

     switch (message)
          {
          case WM_CREATE:          // Create the client window

               clientcreate.hWindowMenu  = hMenuInitWindow ;
               clientcreate.idFirstChild = IDM_FIRSTCHILD ;

               hwndClient = CreateWindow ("MDICLIENT", NULL,
                              WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
                              0, 0, 0, 0, hwnd, 1, hInst,
                              (LPSTR) &clientcreate) ;
               return 0 ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_NEWHELLO:       // Create a Hello child window

                         mdicreate.szClass = szHelloClass ;
                         mdicreate.szTitle = "Hello" ;
                         mdicreate.hOwner  = hInst ;
                         mdicreate.x       = CW_USEDEFAULT ;
                         mdicreate.y       = CW_USEDEFAULT ;
                         mdicreate.cx      = CW_USEDEFAULT ;
                         mdicreate.cy      = CW_USEDEFAULT ;
                         mdicreate.style   = 0 ;
                         mdicreate.lParam  = NULL ;

                         hwndChild = SendMessage (hwndClient, WM_MDICREATE, 0
                                        (LONG) (LPMDICREATESTRUCT) &mdicreate
                         return 0 ;

                    case IDM_NEWRECT:        // Create a Rect child window

                         mdicreate.szClass = szRectClass ;
                         mdicreate.szTitle = "Rectangles" ;
                         mdicreate.hOwner  = hInst ;
                         mdicreate.x       = CW_USEDEFAULT ;
                         mdicreate.y       = CW_USEDEFAULT ;
                         mdicreate.cx      = CW_USEDEFAULT ;
                         mdicreate.cy      = CW_USEDEFAULT ;
                         mdicreate.style   = 0 ;
                         mdicreate.lParam  = NULL ;

                         hwndChild = SendMessage (hwndClient,  WM_MDICREATE,
                                        (LONG) (LPMDICREATESTRUCT) &mdicreate
                         return 0 ;

                    case IDM_CLOSE:          // Close the active window

                         hwndChild = LOWORD (SendMessage (hwndClient,
                                                  WM_MDIGETACTIVE, 0, 0L)) ;

                         if (SendMessage (hwndChild, WM_QUERYENDSESSION, 0, 0
                              SendMessage (hwndClient, WM_MDIDESTROY,
                                           hwndChild, 0L) ;
                         return 0 ;

                    case IDM_EXIT:           // Exit the program

                         SendMessage (hwnd, WM_CLOSE, 0, 0L) ;
                         return 0 ;

                                   // Messages for arranging windows
                    case IDM_TILE:
                         SendMessage (hwndClient, WM_MDITILE, 0, 0L) ;
                         return 0 ;

                    case IDM_CASCADE:
                         SendMessage (hwndClient, WM_MDICASCADE, 0, 0L) ;
                         return 0 ;

                    case IDM_ARRANGE:
                         SendMessage (hwndClient, WM_MDIICONARRANGE, 0, 0L) ;
                         return 0 ;

                    case IDM_CLOSEALL:       // Attempt to close all children

                         lpfnEnum = MakeProcInstance (CloseEnumProc, hInst) ;
                         EnumChildWindows (hwndClient, lpfnEnum, 0L) ;
                         FreeProcInstance (lpfnEnum) ;
                         return 0 ;

                    default:            // Pass to active child

                         hwndChild = LOWORD (SendMessage (hwndClient,
                                                WM_MDIGETACTIVE, 0, 0L)) ;

                        if (IsWindow (hwndChild))
                             SendMessage (hwndChild, WM_COMMAND,
                                          wParam, lParam) ;

                        break ;        // and then to DefFrameProc
                    }
               break ;

          case WM_QUERYENDSESSION:
          case WM_CLOSE:                     // Attempt to close all children

               SendMessage (hwnd, WM_COMMAND, IDM_CLOSEALL, 0L) ;

               if (NULL != GetWindow (hwndClient, GW_CHILD))
                    return 0 ;

               break ;   // ie, call DefFrameProc ;

          case WM_DESTROY :
               PostQuitMessage (0) ;
               return 0 ;
          }
               // Pass unprocessed messages to DefFrameProc (not DefWindowPro

     return DefFrameProc (hwnd, hwndClient, message, wParam, lParam) ;
     }

BOOL FAR PASCAL CloseEnumProc (HWND hwnd, LONG lParam)
     {
     if (GetWindow (hwnd, GW_OWNER))         // check for icon title
          return 1 ;

     SendMessage (GetParent (hwnd), WM_MDIRESTORE, hwnd, 0L) ;

     if (!SendMessage (hwnd, WM_QUERYENDSESSION, 0, 0L))
          return 1 ;

     SendMessage (GetParent (hwnd), WM_MDIDESTROY, hwnd, 0L) ;
          return 1 ;
     }

long FAR PASCAL HelloWndProc (HWND hwnd, WORD message, WORD wParam, LONG lPar
     {
     static COLORREF clrTextArray [] = { RGB (0,   0, 0), RGB (255, 0,   0),
                                         RGB (0, 255, 0), RGB (  0, 0, 255),
                                         RGB (255, 255, 255) } ;
     static HWND     hwndClient, hwndFrame ;
     HDC             hdc ;
     HMENU           hMenu ;
     LOCALHANDLE     hHelloData ;
     NPHELLODATA     npHelloData ;
     PAINTSTRUCT     ps ;
     RECT            rect ;

     switch (message)
          {
          case WM_CREATE:
                         // Allocate memory for window private data

               hHelloData = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT,
                                        sizeof (HELLODATA)) ;

               npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;
               npHelloData->nColor  = IDM_BLACK ;
               npHelloData->clrText = RGB (0, 0, 0) ;
               LocalUnlock (hHelloData) ;
               SetWindowWord (hwnd, 0, hHelloData) ;

                         // Save some window handles

               hwndClient = GetParent (hwnd) ;
               hwndFrame  = GetParent (hwndClient) ;
               return 0 ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_BLACK:
                    case IDM_RED:
                    case IDM_GREEN:
                    case IDM_BLUE:
                    case IDM_WHITE:
                                   // Change the text color

                         hHelloData  = GetWindowWord (hwnd, 0) ;
                         npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;

                         hMenu = GetMenu (hwndFrame) ;

                         CheckMenuItem (hMenu, npHelloData->nColor,
                                               MF_UNCHECKED) ;
                         npHelloData->nColor = wParam ;
                         CheckMenuItem (hMenu, npHelloData->nColor,
                                               MF_CHECKED) ;

                         npHelloData->clrText =
                               clrTextArray [wParam - IDM_BLACK] ;

                         LocalUnlock (hHelloData) ;
                         InvalidateRect (hwnd, NULL, FALSE) ;
                    }
               return 0 ;

          case WM_PAINT:
                         // Paint the window

         hdc = BeginPaint (hwnd, &ps) ;

               hHelloData  = GetWindowWord (hwnd, 0) ;
               npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;
               SetTextColor (hdc, npHelloData->clrText) ;
               LocalUnlock (hHelloData) ;

               GetClientRect (hwnd, &rect) ;

               DrawText (hdc, "Hello, World!", -1, &rect,
                         DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_MDIACTIVATE:

                         // Set the Hello menu if gaining focus

               if (wParam == TRUE)
                    SendMessage (hwndClient, WM_MDISETMENU, 0,
                                 MAKELONG (hMenuHello, hMenuHelloWindow)) ;

                         // check or uncheck menu item

               hHelloData  = GetWindowWord (hwnd, 0) ;
               npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;
               CheckMenuItem (hMenuHello, npHelloData->nColor,
                              wParam ? MF_CHECKED : MF_UNCHECKED) ;
               LocalUnlock (hHelloData) ;

                         // Set the Init menu if losing focus

               if (wParam == FALSE)
                    SendMessage (hwndClient, WM_MDISETMENU, 0,
                                 MAKELONG (hMenuInit, hMenuInitWindow)) ;

               DrawMenuBar (hwndFrame) ;
               return 0 ;

          case WM_QUERYENDSESSION:
          case WM_CLOSE:
               if (IDOK != MessageBox (hwnd, "OK to close window?", "Hello",
                                       MB_ICONQUESTION | MB_OKCANCEL))
                    return 0 ;

               break ;   // ie, call DefMDIChildProc

          case WM_DESTROY:
               hHelloData = GetWindowWord (hwnd, 0) ;
               LocalFree (hHelloData) ;
               return 0 ;
          }
               // Pass unprocessed message to DefMDIChildProc

     return DefMDIChildProc (hwnd, message, wParam, lParam) ;
     }

long FAR PASCAL RectWndProc (HWND hwnd, WORD message, WORD wParam, LONG lPara
     {
     static HWND hwndClient, hwndFrame ;
     HPEN        hBrush ;
     HDC         hdc ;
     LOCALHANDLE hRectData ;
     NPRECTDATA  npRectData ;
     PAINTSTRUCT ps ;
     short       xLeft, xRight, yTop, yBottom, nRed, nGreen, nBlue ;

     switch (message)
          {
          case WM_CREATE:
                         // Allocate memory for window private data

               hRectData = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT,
                                       sizeof (RECTDATA)) ;

               SetWindowWord (hwnd, 0, hRectData) ;

                         // Start the timer going

               SetTimer (hwnd, 1, 250, NULL) ;

                         // Save some window handles

               hwndClient = GetParent (hwnd) ;
               hwndFrame  = GetParent (hwndClient) ;
               return 0 ;

          case WM_SIZE:            // Save the window size

               hRectData  = GetWindowWord (hwnd, 0) ;
               npRectData = (NPRECTDATA) LocalLock (hRectData) ;

               npRectData->cxClient = LOWORD (lParam) ;
               npRectData->cyClient = HIWORD (lParam) ;

               LocalUnlock (hRectData) ;

               break ;        // WM_SIZE must be processed by DefMDIChildProc

          case WM_TIMER:           // Display a random rectangle

               hRectData  = GetWindowWord (hwnd, 0) ;
               npRectData = (NPRECTDATA) LocalLock (hRectData) ;

               xLeft   = rand () % npRectData->cxClient ;
               xRight  = rand () % npRectData->cxClient ;
               yTop    = rand () % npRectData->cyClient ;
               yBottom = rand () % npRectData->cyClient ;
               nRed    = rand () & 255 ;
               nGreen  = rand () & 255 ;
               nBlue   = rand () & 255 ;

               hdc = GetDC (hwnd) ;
               hBrush = CreateSolidBrush (RGB (nRed, nGreen, nBlue)) ;
               SelectObject (hdc, hBrush) ;

               Rectangle (hdc, min (xLeft, xRight), min (yTop, yBottom),
                               max (xLeft, xRight), max (yTop, yBottom)) ;

               ReleaseDC (hwnd, hdc) ;
               DeleteObject (hBrush) ;
               LocalUnlock (hRectData) ;
               return 0 ;

          case WM_PAINT:           // Clear the window

               InvalidateRect (hwnd, NULL, TRUE) ;
               hdc = BeginPaint (hwnd, &ps) ;
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_MDIACTIVATE:     // Set the appropriate menu
               if (wParam == TRUE)
                    SendMessage (hwndClient, WM_MDISETMENU, 0,
                                 MAKELONG (hMenuRect, hMenuRectWindow)) ;
               else
                    SendMessage (hwndClient, WM_MDISETMENU, 0,
                                 MAKELONG (hMenuInit, hMenuInitWindow)) ;

               DrawMenuBar (hwndFrame) ;
               return 0 ;

          case WM_DESTROY:
               hRectData = GetWindowWord (hwnd, 0) ;
               LocalFree (hRectData) ;
               KillTimer (hwnd, 1) ;
               return 0 ;
          }
               // Pass unprocessed message to DefMDIChildProc

     return DefMDIChildProc (hwnd, message, wParam, lParam) ;
     }


MENUDEMO.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP09\MENUDEMO.C

/*-----------------------------------------
   MENUDEMO.C -- Menu Demonstration
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>
#include "menudemo.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

char szAppName [] = "MenuDemo" ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Menu Demonstration",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static int  wColorID [5] = { WHITE_BRUSH,  LTGRAY_BRUSH, GRAY_BRUSH,
                                  DKGRAY_BRUSH, BLACK_BRUSH } ;
     static WORD wSelection = IDM_WHITE ;
     HMENU       hMenu ;

     switch (message)
          {
          case WM_COMMAND:
               hMenu = GetMenu (hwnd) ;

               switch (wParam)
                    {
                    case IDM_NEW:
                    case IDM_OPEN:
                    case IDM_SAVE:
                    case IDM_SAVEAS:
                         MessageBeep (0) ;
                         return 0 ;

                    case IDM_EXIT:
                         SendMessage (hwnd, WM_CLOSE, 0, 0L) ;
                         return 0 ;

                    case IDM_UNDO:
                    case IDM_CUT:
                    case IDM_COPY:
                    case IDM_PASTE:
                    case IDM_CLEAR:
                         MessageBeep (0) ;
                         return 0 ;

                    case IDM_WHITE:          // Note: Logic below
                    case IDM_LTGRAY:         //   assumes that IDM_WHITE
                    case IDM_GRAY:           //   through IDM_BLACK are
                    case IDM_DKGRAY:         //   consecutive numbers in
                    case IDM_BLACK:          //   the order shown here.

                         CheckMenuItem (hMenu, wSelection, MF_UNCHECKED) ;
                         wSelection = wParam ;
                         CheckMenuItem (hMenu, wSelection, MF_CHECKED) ;

                         SetClassWord (hwnd, GCW_HBRBACKGROUND,
                              GetStockObject (wColorID [wParam - IDM_WHITE]))

                         InvalidateRect (hwnd, NULL, TRUE) ;
                         return 0 ;

                    case IDM_START:
                         if (SetTimer (hwnd, 1, 1000, NULL))
                              {
                              EnableMenuItem (hMenu, IDM_START, MF_GRAYED) ;
                              EnableMenuItem (hMenu, IDM_STOP,  MF_ENABLED) ;
                              }
                         return 0 ;

                    case IDM_STOP:
                         KillTimer (hwnd, 1) ;
                         EnableMenuItem (hMenu, IDM_START, MF_ENABLED) ;
                         EnableMenuItem (hMenu, IDM_STOP,  MF_GRAYED) ;
                         return 0 ;

                    case IDM_HELP:
                         MessageBox (hwnd, "Help not yet implemented.",
                                     szAppName, MB_ICONINFORMATION | MB_OK) ;
                         return 0 ;

                    case IDM_ABOUT:
                         MessageBox (hwnd, "Menu Demonstration Program.",
                                     szAppName, MB_ICONINFORMATION | MB_OK) ;
                         return 0 ;
                    }
               break ;

          case WM_TIMER:
               MessageBeep (0) ;
               return 0 ;

          case WM_DESTROY :
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


MFCREATE.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP13\MFCREATE.C

/*-----------------------------------------
   MFCREATE.C -- Metafile Creation Program
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HBRUSH hBrush  = CreateSolidBrush (RGB (0, 0, 255)) ;
     HDC    hdcMeta = CreateMetaFile ("MYLOGO.WMF") ;

     Rectangle (hdcMeta, 0, 0, 100, 100) ;
     MoveTo (hdcMeta, 0, 0) ;
     LineTo (hdcMeta, 100, 100) ;
     MoveTo (hdcMeta, 0, 100) ;
     LineTo (hdcMeta, 100, 0) ;
     SelectObject (hdcMeta, hBrush) ;
     Ellipse (hdcMeta, 20, 20, 80, 80) ;

     DeleteMetaFile (CloseMetaFile (hdcMeta)) ;
     DeleteObject (hBrush) ;

     MessageBeep (0) ;

     return FALSE ;
     }


MFRESORC.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP13\MFRESORC.C

/*-----------------------------------------
   MFRESORC.C -- Metafile Resource Program
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "MFResorc" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = NULL ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Metafile Resource Program",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HANDLE hmf ;
     static short  cxClient, cyClient ;
     HANDLE        hInstance, hResource ;
     HDC           hdc ;
     PAINTSTRUCT   ps ;
     short         x, y ;

     switch (message)
          {
          case WM_CREATE:
               hInstance = ((LPCREATESTRUCT) lParam) -> hInstance ;
               hResource = LoadResource (hInstance,
                           FindResource (hInstance, "MyLogo", "METAFILE")) ;

               LockResource (hResource) ;
               hmf = SetMetaFileBits (hResource) ;
               UnlockResource (hResource) ;
               return 0 ;

          case WM_SIZE:
               cxClient = LOWORD (lParam) ;
               cyClient = HIWORD (lParam) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               SetMapMode (hdc, MM_ANISOTROPIC) ;
               SetWindowExt (hdc, 1000, 1000) ;
               SetViewportExt (hdc, cxClient, cyClient) ;

               for (x = 0 ; x < 10 ; x++)
                    for (y = 0 ; y < 10 ; y++)
                         {
                         SetWindowOrg (hdc, -100 * x, -100 * y) ;
                         PlayMetaFile (hdc, hmf) ;
                         }

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               DeleteMetaFile (hmf) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


NOPOPUPS.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP09\NOPOPUPS.C

/*-------------------------------------------------
   NOPOPUPS.C -- Demonstrates No-Popup Nested Menu
                 (c) Charles Petzold, 1990
  -------------------------------------------------*/

#include <windows.h>
#include "nopopups.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     static char szAppName [] = "NoPopUps" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASS    wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "No-Popup Nested Menu Demonstration",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HMENU hMenuMain, hMenuEdit, hMenuFile ;
     HANDLE       hInstance ;

     switch (message)
          {
          case WM_CREATE:
               hInstance = GetWindowWord (hwnd, GWW_HINSTANCE) ;

               hMenuMain = LoadMenu (hInstance, "MenuMain") ;
               hMenuFile = LoadMenu (hInstance, "MenuFile") ;
               hMenuEdit = LoadMenu (hInstance, "MenuEdit") ;

               SetMenu (hwnd, hMenuMain) ;
               return 0 ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_MAIN:
                         SetMenu (hwnd, hMenuMain) ;
                         return 0 ;

                    case IDM_FILE:
                         SetMenu (hwnd, hMenuFile) ;
                         return 0 ;

                    case IDM_EDIT:
                         SetMenu (hwnd, hMenuEdit) ;
                         return 0 ;

                    case IDM_NEW:
                    case IDM_OPEN:
                    case IDM_SAVE:
                    case IDM_SAVEAS:
                    case IDM_UNDO:
                    case IDM_CUT:
                    case IDM_COPY:
                    case IDM_PASTE:
                    case IDM_CLEAR:
                         MessageBeep (0) ;
                         return 0 ;
                    }
               break ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


PICKFONT.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP14\PICKFONT.C

/*-----------------------------------------
   PICKFONT.C -- Font Picker Program
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>
#include "pickfont.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
BOOL FAR PASCAL DlgProc (HWND, WORD, WORD, LONG) ;

char    szAppName [] = "PickFont" ;
DWORD   dwAspectMatch = 0L ;
HWND    hDlg ;
LOGFONT lf ;
short   nMapMode = IDD_TEXT ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "Font Picker",
                          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

     while (GetMessage (&msg, NULL, 0, 0))
          {
          if (hDlg == 0 || !IsDialogMessage (hDlg, &msg))
               {
               TranslateMessage (&msg) ;
               DispatchMessage  (&msg) ;
               }
          }
     return msg.wParam ;
     }

void MySetMapMode (HDC hdc)
     {
     if (nMapMode == IDD_LTWPS)
          {
          SetMapMode (hdc, MM_ANISOTROPIC) ;
          SetWindowExt (hdc, 1440, 1440) ;
          SetViewportExt (hdc, GetDeviceCaps (hdc, LOGPIXELSX),
                               GetDeviceCaps (hdc, LOGPIXELSY)) ;
          }
     else
          SetMapMode (hdc, MM_TEXT + nMapMode - IDD_TEXT) ;
     }

void ShowMetrics (HWND hDlg)
     {
     static TEXTMETRIC tm ;
     static struct
          {
          short nDlgID ;
          short *pData ;
          }
          shorts [] =
          {
          TM_HEIGHT,     &tm.tmHeight,
          TM_ASCENT,     &tm.tmAscent,
          TM_DESCENT,    &tm.tmDescent,
          TM_INTLEAD,    &tm.tmInternalLeading,
          TM_EXTLEAD,    &tm.tmExternalLeading,
          TM_AVEWIDTH,   &tm.tmAveCharWidth,
          TM_MAXWIDTH,   &tm.tmMaxCharWidth,
          TM_WEIGHT,     &tm.tmWeight,
          TM_OVER,       &tm.tmOverhang,
          TM_DIGX,       &tm.tmDigitizedAspectX,
          TM_DIGY,       &tm.tmDigitizedAspectY
          } ;
     static char    *szFamily [] = { "Don't Care", "Roman",  "Swiss",
                                     "Modern",     "Script", "Decorative" } ;
     BOOL           bTrans ;
     char           szFaceName [LF_FACESIZE] ;
     HDC            hdc ;
     HFONT          hFont ;
     short          i ;

     lf.lfHeight    = GetDlgItemInt (hDlg, IDD_HEIGHT, &bTrans, TRUE) ;
     lf.lfWidth     = GetDlgItemInt (hDlg, IDD_WIDTH,  &bTrans, FALSE) ;
     lf.lfWeight    = GetDlgItemInt (hDlg, IDD_WEIGHT, &bTrans, FALSE) ;

     lf.lfItalic    = (BYTE) (IsDlgButtonChecked (hDlg, IDD_ITALIC) ? 1 : 0)
     lf.lfUnderline = (BYTE) (IsDlgButtonChecked (hDlg, IDD_UNDER)  ? 1 : 0)
     lf.lfStrikeOut = (BYTE) (IsDlgButtonChecked (hDlg, IDD_STRIKE) ? 1 : 0)

     GetDlgItemText (hDlg, IDD_FACE, lf.lfFaceName, LF_FACESIZE) ;

     dwAspectMatch = IsDlgButtonChecked (hDlg, IDD_ASPECT) ? 1L : 0L ;

     hdc = GetDC (hDlg) ;
     MySetMapMode (hdc) ;
     SetMapperFlags (hdc, dwAspectMatch) ;

     hFont = SelectObject (hdc, CreateFontIndirect (&lf)) ;
     GetTextMetrics (hdc, &tm) ;
     GetTextFace (hdc, sizeof szFaceName, szFaceName) ;

     DeleteObject (SelectObject (hdc, hFont)) ;
     ReleaseDC (hDlg, hdc) ;

     for (i = 0 ; i < sizeof shorts / sizeof shorts [0] ; i++)
          SetDlgItemInt (hDlg, shorts[i].nDlgID, *shorts[i].pData, TRUE) ;

     SetDlgItemText (hDlg, TM_PITCH, tm.tmPitchAndFamily & 1 ?
                                                      "VARIABLE":"FIXED") ;

     SetDlgItemText (hDlg, TM_FAMILY, szFamily [tm.tmPitchAndFamily >> 4]) ;
     SetDlgItemText (hDlg, TM_CHARSET, tm.tmCharSet ? "OEM" : "ANSI") ;
     SetDlgItemText (hDlg, TF_NAME, szFaceName) ;
     }

BOOL FAR PASCAL DlgProc (HWND hDlg, WORD message, WORD wParam, LONG lParam)
     {
     switch (message)
          {
          case WM_INITDIALOG:
               CheckRadioButton (hDlg, IDD_TEXT,   IDD_LTWPS,  IDD_TEXT) ;
               CheckRadioButton (hDlg, IDD_ANSI,   IDD_OEM,    IDD_ANSI) ;
               CheckRadioButton (hDlg, IDD_QDRAFT, IDD_QPROOF, IDD_QDRAFT) ;
               CheckRadioButton (hDlg, IDD_PDEF,   IDD_PVAR,   IDD_PDEF) ;
               CheckRadioButton (hDlg, IDD_DONT,   IDD_DEC,    IDD_DONT) ;

               lf.lfEscapement    = 0 ;
               lf.lfOrientation   = 0 ;
               lf.lfOutPrecision  = OUT_DEFAULT_PRECIS ;
               lf.lfClipPrecision = CLIP_DEFAULT_PRECIS ;

               ShowMetrics (hDlg) ;
                                        /* fall through */
          case WM_SETFOCUS:
               SetFocus (GetDlgItem (hDlg, IDD_HEIGHT)) ;
               return FALSE ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDD_TEXT:
                    case IDD_LOMET:
                    case IDD_HIMET:
                    case IDD_LOENG:
                    case IDD_HIENG:
                    case IDD_TWIPS:
                    case IDD_LTWPS:
                         CheckRadioButton (hDlg, IDD_TEXT, IDD_LTWPS, wParam)
                         nMapMode = wParam ;
                         break ;

                    case IDD_ASPECT:
                    case IDD_ITALIC:
                    case IDD_UNDER:
                    case IDD_STRIKE:
                         CheckDlgButton (hDlg, wParam,
                              IsDlgButtonChecked (hDlg, wParam) ? 0 : 1) ;
                         break ;

                    case IDD_ANSI:
                    case IDD_OEM:
                         CheckRadioButton (hDlg, IDD_ANSI, IDD_OEM, wParam) ;
                         lf.lfCharSet = (BYTE) (wParam == IDD_ANSI ? 0 : 255)
                         break ;

                    case IDD_QDRAFT:
                    case IDD_QDEF:
                    case IDD_QPROOF:
                         CheckRadioButton (hDlg, IDD_QDRAFT, IDD_QPROOF,
                                                                      wParam)
                         lf.lfQuality = (BYTE) (wParam - IDD_QDRAFT) ;
                         break ;

                    case IDD_PDEF:
                    case IDD_PFIXED:
                    case IDD_PVAR:
                         CheckRadioButton (hDlg, IDD_PDEF, IDD_PVAR, wParam)
                         lf.lfPitchAndFamily &= 0xF0 ;
                         lf.lfPitchAndFamily |= (BYTE) (wParam - IDD_PDEF) ;
                         break ;

                    case IDD_DONT:
                    case IDD_ROMAN:
                    case IDD_SWISS:
                    case IDD_MODERN:
                    case IDD_SCRIPT:
                    case IDD_DEC:
                         CheckRadioButton (hDlg, IDD_DONT, IDD_DEC, wParam) ;
                         lf.lfPitchAndFamily &= 0x0F ;
                         lf.lfPitchAndFamily |= (BYTE) (wParam-IDD_DONT << 4)
                         break ;

                    case IDD_OK:
                         ShowMetrics (hDlg) ;
                         InvalidateRect (GetParent (hDlg), NULL, TRUE) ;
                         break ;
                    }
               break ;

          default:
               return FALSE ;
          }
     return TRUE ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static char  szText [] =
                      "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPqQqRrSsTtUuVvWwXxYyZz"
     static short cxClient, cyClient ;
     HANDLE       hInstance ;
     HDC          hdc ;
     HFONT        hFont ;
     FARPROC      lpfnDlgProc ;
     PAINTSTRUCT  ps ;
     RECT         rect ;

     switch (message)
          {
          case WM_CREATE :
               hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
               lpfnDlgProc = MakeProcInstance (DlgProc, hInstance) ;
               hDlg = CreateDialog (hInstance, szAppName, hwnd, lpfnDlgProc)
               return 0 ;

          case WM_SETFOCUS:
               SetFocus (hDlg) ;
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;
               MySetMapMode (hdc) ;
               SetMapperFlags (hdc, dwAspectMatch) ;
               GetClientRect (hDlg, &rect) ;
               rect.bottom += 1 ;
               DPtoLP (hdc, (LPPOINT) &rect, 2) ;

               hFont = SelectObject (hdc, CreateFontIndirect (&lf)) ;

               TextOut (hdc, rect.left, rect.bottom, szText, 52) ;

               DeleteObject (SelectObject (hdc, hFont)) ;
               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


POEPOEM.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP08\POEPOEM.C

/*-------------------------------------------------
   POEPOEM.C -- Demonstrates User-Defined Resource
                (c) Charles Petzold, 1990
  -------------------------------------------------*/

#include <windows.h>
#include "poepoem.h"

long FAR PASCAL WndProc  (HWND, WORD, WORD, LONG) ;

char   szAppName [10] ;
char   szCaption [35] ;
HANDLE hInst ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          LoadString (hInstance, IDS_APPNAME, szAppName, sizeof szAppName) ;
          LoadString (hInstance, IDS_CAPTION, szCaption, sizeof szCaption) ;

          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }
     else
          {
          GetInstanceData (hPrevInstance, szAppName, sizeof szAppName) ;
          GetInstanceData (hPrevInstance, szCaption, sizeof szCaption) ;
          }

     hInst = hInstance ;

     hwnd = CreateWindow (szAppName, szCaption,
                          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HANDLE hResource ;
     static HWND   hScroll ;
     static short  nPosition, cxChar, cyChar, cyClient, nNumLines, xScroll ;
     char          szPoemRes [15] ;
     char far      *lpText ;
     HDC           hdc ;
     PAINTSTRUCT   ps ;
     RECT          rect ;
     TEXTMETRIC    tm ;

     switch (message)
          {
          case WM_CREATE:
               hdc = GetDC (hwnd) ;
               GetTextMetrics (hdc, &tm) ;
               cxChar = tm.tmAveCharWidth ;
               cyChar = tm.tmHeight + tm.tmExternalLeading ;
               ReleaseDC (hwnd, hdc) ;

               xScroll = GetSystemMetrics (SM_CXVSCROLL) ;

               hScroll = CreateWindow ("scrollbar", NULL,
                              WS_CHILD | WS_VISIBLE | SBS_VERT,
                              0, 0, 0, 0,
                              hwnd, 1, hInst, NULL) ;

               LoadString (hInst, IDS_POEMRES, szPoemRes, sizeof szPoemRes) ;
               hResource = LoadResource (hInst,
                           FindResource (hInst, szPoemRes, "TEXT")) ;

               lpText = LockResource (hResource) ;

               nNumLines = 0 ;

               while (*lpText != '\\' && *lpText != '\0')
                    {
                    if (*lpText == '\n')
                         nNumLines ++ ;
                    lpText = AnsiNext (lpText) ;
                    }
               *lpText = '\0' ;

               GlobalUnlock (hResource) ;

               SetScrollRange (hScroll, SB_CTL, 0, nNumLines, FALSE) ;
               SetScrollPos   (hScroll, SB_CTL, 0, FALSE) ;
               return 0 ;

          case WM_SIZE:
               MoveWindow (hScroll, LOWORD (lParam) - xScroll, 0,
                    xScroll, cyClient = HIWORD (lParam), TRUE) ;
               SetFocus (hwnd) ;
               return 0 ;

          case WM_SETFOCUS:
               SetFocus (hScroll) ;
               return 0 ;

          case WM_VSCROLL:
               switch (wParam)
                    {
                    case SB_TOP:
                         nPosition = 0 ;
                         break ;
                    case SB_BOTTOM:
                         nPosition = nNumLines ;
                         break ;
                    case SB_LINEUP:
                         nPosition -= 1 ;
                         break ;
                    case SB_LINEDOWN:
                         nPosition += 1 ;
                         break ;
                    case SB_PAGEUP:
                         nPosition -= cyClient / cyChar ;
                         break ;
                    case SB_PAGEDOWN:
                         nPosition += cyClient / cyChar ;
                         break ;
                    case SB_THUMBPOSITION:
                         nPosition = LOWORD (lParam) ;
                         break ;
                    }
               nPosition = max (0, min (nPosition, nNumLines)) ;

               if (nPosition != GetScrollPos (hScroll, SB_CTL))
                    {
                    SetScrollPos (hScroll, SB_CTL, nPosition, TRUE) ;
                    InvalidateRect (hwnd, NULL, TRUE) ;
                    }
               return 0 ;

          case WM_PAINT:
               hdc = BeginPaint (hwnd, &ps) ;

               lpText = LockResource (hResource) ;

               GetClientRect (hwnd, &rect) ;
               rect.left += cxChar ;
               rect.top  += cyChar * (1 - nPosition) ;
               DrawText (hdc, lpText, -1, &rect, DT_EXTERNALLEADING) ;

               GlobalUnlock (hResource) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY:
               FreeResource (hResource) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


POORMENU.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP09\POORMENU.C

/*-----------------------------------------
   POORMENU.C -- The Poor Person's Menu
                 (c) Charles Petzold, 1990
  -----------------------------------------*/

#include <windows.h>

#define IDM_ABOUT   1
#define IDM_HELP    2
#define IDM_REMOVE  3

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

static char szAppName [] = "PoorMenu" ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HMENU    hMenu ;
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, "The Poor-Person's Menu",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     hMenu = GetSystemMenu (hwnd, FALSE) ;

     AppendMenu (hMenu, MF_SEPARATOR, 0,          NULL) ;
     AppendMenu (hMenu, MF_STRING,    IDM_ABOUT,  "About...") ;
     AppendMenu (hMenu, MF_STRING,    IDM_HELP,   "Help...") ;
     AppendMenu (hMenu, MF_STRING,    IDM_REMOVE, "Remove Additions") ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     switch (message)
          {
          case WM_SYSCOMMAND:
               switch (wParam)
                    {
                    case IDM_ABOUT:
                         MessageBox (hwnd, "A Poor-Person's Menu Program.",
                                     szAppName, MB_OK | MB_ICONEXCLAMATION) ;
                         return 0 ;

                    case IDM_HELP:
                         MessageBox (hwnd, "Help not yet implemented.",
                                     szAppName, MB_OK | MB_ICONEXCLAMATION) ;
                         return 0 ;

                    case IDM_REMOVE:
                         GetSystemMenu (hwnd, TRUE) ;
                         return 0 ;
                    }
               break ;

          case WM_DESTROY:
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


POPMENU.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP09\POPMENU.C

/*----------------------------------------
   POPMENU.C -- Popup Menu Demonstration
                (c) Charles Petzold, 1990
  ----------------------------------------*/

#include <windows.h>
#include "popmenu.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

char   szAppName [] = "PopMenu" ;
HANDLE hInst ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hInst = hInstance ;

     hwnd = CreateWindow (szAppName, "Popup Menu Demonstration",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HMENU hMenu ;
     static int   wColorID [5] = { WHITE_BRUSH,  LTGRAY_BRUSH, GRAY_BRUSH,
                                   DKGRAY_BRUSH, BLACK_BRUSH } ;
     static WORD  wSelection = IDM_WHITE ;
     POINT        point ;

     switch (message)
          {
          case WM_CREATE:
               hMenu = LoadMenu (hInst, szAppName) ;
               hMenu = GetSubMenu (hMenu, 0) ;
               return 0 ;

          case WM_RBUTTONDOWN:
               point = MAKEPOINT (lParam) ;
               ClientToScreen (hwnd, &point) ;

               TrackPopupMenu (hMenu, 0, point.x, point.y, 0, hwnd, NULL) ;
               return 0 ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDM_NEW:
                    case IDM_OPEN:
                    case IDM_SAVE:
                    case IDM_SAVEAS:
                    case IDM_UNDO:
                    case IDM_CUT:
                    case IDM_COPY:
                    case IDM_PASTE:
                    case IDM_CLEAR:
                         MessageBeep (0) ;
                         return 0 ;

                    case IDM_WHITE:          // Note: Logic below
                    case IDM_LTGRAY:         //   assumes that IDM_WHITE
                    case IDM_GRAY:           //   through IDM_BLACK are
                    case IDM_DKGRAY:         //   consecutive numbers in
                    case IDM_BLACK:          //   the order shown here.

                         CheckMenuItem (hMenu, wSelection, MF_UNCHECKED) ;
                         wSelection = wParam ;
                         CheckMenuItem (hMenu, wSelection, MF_CHECKED) ;

                         SetClassWord (hwnd, GCW_HBRBACKGROUND,
                              GetStockObject (wColorID [wParam - IDM_WHITE]))

                         InvalidateRect (hwnd, NULL, TRUE) ;
                         return 0 ;

                    case IDM_ABOUT:
                         MessageBox (hwnd, "Popup Menu Demonstration Program.
                                     szAppName, MB_ICONINFORMATION | MB_OK) ;
                         return 0 ;

                    case IDM_EXIT:
                         SendMessage (hwnd, WM_CLOSE, 0, 0L) ;
                         return 0 ;

                    case IDM_HELP:
                         MessageBox (hwnd, "Help not yet implemented.",
                                     szAppName, MB_ICONINFORMATION | MB_OK) ;
                         return 0 ;
                    }
               break ;

          case WM_DESTROY :
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }


POPPAD.C
CD-ROM Disc Path:   \SAMPCODE\PROGWIN\CHAP10\POPPAD.C

/*---------------------------------------
   POPPAD.C -- Popup Editor
               (c) Charles Petzold, 1990
  ---------------------------------------*/

#include <windows.h>
#include "poppad.h"
#define  EDITID 1

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG);

BOOL ReadFile  (HANDLE, HWND, HWND, POFSTRUCT, char *, BOOL) ;
BOOL WriteFile (HANDLE, HWND, HWND, POFSTRUCT, char *, BOOL) ;
BOOL PrintFile (HANDLE, HWND, HWND, char *) ;

LPSTR lstrrchr (LPSTR, char) ;

char szAppName  [] = "PopPad" ;
char szFileSpec [] = "*.TXT"  ;
char szUntitled [] = "(untitled)" ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     MSG      msg;
     HWND     hwnd ;
     HANDLE   hAccel ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = szAppName ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hwnd = CreateWindow (szAppName, NULL,
                          WS_OVERLAPPEDWINDOW,
                          GetSystemMetrics (SM_CXSCREEN) / 4,
                          GetSystemMetrics (SM_CYSCREEN) / 4,
                          GetSystemMetrics (SM_CXSCREEN) / 2,
                          GetSystemMetrics (SM_CYSCREEN) / 2,
                          NULL, NULL, hInstance, lpszCmdLine) ;

     ShowWindow (hwnd, nCmdShow) ;

     UpdateWindow (hwnd);

     hAccel = LoadAccelerators (hInstance, szAppName) ;

     while (GetMessage (&msg, NULL, 0, 0))
          {
          if (!TranslateAccelerator (hwnd, hAccel, &msg))
               {
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
               }
          }
     return msg.wParam ;
     }

BOOL FAR PASCAL AboutDlgProc (HWND hDlg, WORD message, WORD wParam, LONG lPar
     {
     switch (message)
          {
          case WM_INITDIALOG:
               return TRUE ;

          case WM_COMMAND:
               switch (wParam)
                    {
                    case IDOK:
                         EndDialog (hDlg, 0) ;
                         return TRUE ;
                    }
               break ;
          }
     return FALSE ;
     }

void DoCaption (HWND hwnd, char *szFileName)
     {
     char szCaption [40] ;

     wsprintf (szCaption, "%s - %s", (LPSTR) szAppName,
               (LPSTR) (szFileName [0] ? szFileName : szUntitled)) ;

     SetWindowText (hwnd, szCaption) ;
     }

short AskAboutSave (HWND hwnd, char *szFileName)
     {
     char  szBuffer [40] ;
     short nReturn ;

     wsprintf (szBuffer, "Save current changes: %s",
               (LPSTR) (szFileName [0] ? szFileName : szUntitled)) ;

     if (IDYES == (nReturn = MessageBox (hwnd, szBuffer, szAppName,
                                    MB_YESNOCANCEL | MB_ICONQUESTION)))

          if (!SendMessage (hwnd, WM_COMMAND, IDM_SAVE, 0L))
               return IDCANCEL ;

     return nReturn ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static BOOL    bNeedSave = FALSE ;
     static char    szRealFileName [16] ;
     static FARPROC lpfnAboutDlgProc ;
     static HANDLE  hInst ;
     static HWND    hwndEdit ;
     char           szFileName [16] ;
     LONG           lSelect ;
     OFSTRUCT       of ;
     WORD           wEnable ;

     switch (message)
          {
          case WM_CREATE:
               hInst = ((LPCREATESTRUCT) lParam)->hInstance ;
               lpfnAboutDlgProc = MakeProcInstance (AboutDlgProc, hInst) ;

               hwndEdit = CreateWindow ("edit", NULL,
                         WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
                              WS_BORDER | ES_LEFT | ES_MULTILINE |
                              ES_AUTOHSCROLL | ES_AUTOVSCROLL,
                         0, 0, 0, 0,
                         hwnd, EDITID, hInst, NULL) ;

               SendMessage (hwndEdit, EM_LIMITTEXT, 32000, 0L) ;

               if (lstrlen (((LPCREATESTRUCT) lParam)->lpCreateParams))
                    {
                    OpenFile (((LPCREATESTRUCT) lParam)->lpCreateParams,
                                        &of, OF_PARSE) ;
                    lstrcpy (szFileName,
                              AnsiNext (lstrrchr (of.szPathName, '\\'))) ;

                    if (ReadFile (hInst, hwnd, hwndEdit, &of,
                              szFileName, FALSE))
                         lstrcpy (szRealFileName, szFileName) ;
                    }
               DoCaption (hwnd, szRealFileName) ;
               return 0 ;

          case WM_SETFOCUS:
               SetFocus (hwndEdit) ;
               return 0 ;

          case WM_SIZE:
               MoveWindow (hwndEdit, 0, 0, LOWORD (lParam),
                                           HIWORD (lParam), TRUE) ;
               return 0 ;

          case WM_INITMENUPOPUP:
               if (lParam == 1)
                    {
                    EnableMenuItem (wParam, IDM_UNDO,
                         SendMessage (hwndEdit, EM_CANUNDO, 0, 0L) ?
                              MF_ENABLED : MF_GRAYED) ;

                    EnableMenuItem (wParam, IDM_PASTE,
                         IsClipboardFormatAvailable (CF_TEXT) ?
                              MF_ENABLED : MF_GRAYED) ;

                    lSelect = SendMessage (hwndEdit, EM_GETSEL, 0, 0L) ;

                    if (HIWORD (lSelect) == LOWORD (lSelect))
                         wEnable = MF_GRAYED ;
                    else
                         wEnable = MF_ENABLED ;