README.TXT

The \Mssdk\Samples\WinUI directory contains samples
of Win32 user interface development, including:

* Base graphical user interface (GUI) capabilities
* Win32 console functions
* Win32 DLL templates
* Common dialog boxes, including File
* Common controls, including RichEdit, Rebar, and
  ListView
* Wheel mouse and associated APIs
* Combo boxes and Owner draw boxes
* Dynamic dialog box templates
* Icon manipulation
* Message tables

Samples related to the Windows Shell:
* Band objects
* Namespace extensions
* Drag-and-drop context menus
* File viewers
* File parsing
* Basic shell extensions, including context menu,
  property sheet, icon handler, and copy hook extensions

The build environment (headers, libraries, and minimum build
tools) for the Platform SDK must be installed in order to build
any of the samples in the \Mssdk\Samples\WinUI directory. The
most recent version of the build environment is available at
http://www.microsoft.com/msdn/sdk/platform.htm.
Some components may also require you to install runtimes, depending
on the platform on which you are trying to run the sample.
Redistributables are available in the \Mssdk\Redist subdirectory
on the Platform SDK compact disc.

Documentation for the components listed above can be installed
from the Platform SDK compact disc, or can be viewed online at
http://premium.microsoft.com/msdn/library/. This is a FREE service;
registration is necessary.

If you do not see any subdirectories in the \Mssdk\Samples\WinUI
node, you'll need to install the Windows Base Services Samples
component from the Platform SDK compact disc or download and
install them from http://www.microsoft.com/msdn/sdk/platform.htm.


CLIPTEXT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

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

    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 "cliptext.h"
#include <string.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 */

/* functions declared here, because of MIPS lack of passing C_DEFINES*/
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, INT);
LONG APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
BOOL APIENTRY About(HWND, UINT, UINT, LONG);
VOID OutOfMemory(VOID);

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

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

    PURPOSE: calls initialization function, processes message loop

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

int APIENTRY WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
    )
{
    MSG msg;

    UNREFERENCED_PARAMETER( lpCmdLine );

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

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

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

    /* 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(HANDLE hInstance)
{
    WNDCLASS  wc;

    wc.style = 0;
    wc.lpfnWndProc = (WNDPROC) 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(
    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);
    }

    strcpy(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 APIENTRY MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam)
{
    HDC hDC;
    PAINTSTRUCT ps;
    RECT rectClient;
    LPSTR lpszText;

    switch (message) {

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

            }
            return (TRUE);

        case WM_COMMAND:
                switch(LOWORD(wParam)) {
                case IDM_ABOUT:
    DialogBox(hInst, "AboutBox", hWnd, About);
                    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_DDESHARE, GlobalSize (hText)))) {
                            OutOfMemory();
                            return (TRUE);
                        }
                        if (!(lpData = GlobalLock(hData))) {
                            OutOfMemory();
                            return (TRUE);
                        }
                        if (!(lpszText = GlobalLock (hText))) {
                            OutOfMemory();
                            return (TRUE);
                        }
                                    strcpy(lpData, lpszText);
                        GlobalUnlock(hData);
                        GlobalUnlock (hText);

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

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

                                    if (LOWORD(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;
                        }
                        strcpy(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_WORDBREAK);
                    GlobalUnlock (hText);
                }
            }
                EndPaint (hWnd, &ps);
            break;

            case WM_DESTROY:
                PostQuitMessage(0);
            break;

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

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

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

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

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

BOOL APIENTRY About( HWND hDlg, UINT message, UINT wParam, LONG lParam)
{
    switch (message) {
            case WM_INITDIALOG:
                return (TRUE);

            case WM_COMMAND:
                if (LOWORD(wParam) == IDOK
                       || LOWORD(wParam) == IDCANCEL) {

                        EndDialog(hDlg, TRUE);
                        return (TRUE);
            }
                break;
    }
    return (FALSE);
        UNREFERENCED_PARAMETER(lParam);
}

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

    FUNCTION: OutOfMemory(void)

    PURPOSE:  Displays warning message

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


CLIPTEXT.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include "windows.h"

#define IDM_ABOUT 100

/* file menu items */

#define     IDM_NEW      101
#define     IDM_OPEN     102
#define     IDM_SAVE     103
#define     IDM_SAVEAS   104
#define     IDM_PRINT    105
#define     IDM_EXIT     106

/* edit menu items */

#define     IDM_UNDO     200
#define     IDM_CUT      201
#define     IDM_COPY     202
#define     IDM_PASTE    203
#define     IDM_CLEAR    204


README.TXT

Common Dialogs

SUMMARY
=======

The CDTest sample demonstrates how to use the common dialog boxes provided
in the Win32 API. In addition to displaying each default dialog box, the
sample also allows you to modify the parameters passed to the functions to
create variations of the common dialog boxes.

MORE INFORMATION
================

The CDTest sample dialog box makes use of the following controls:

  - The NULL structure check box.

    If you check this box, CDTest sends a NULL value for the common dialog
    box creation structure to the common dialog box function. The common
    dialog box should fail at startup. The common dialog box function
    should return FALSE and set the correct CommDlgExtendedError.

  - The Preloaded Template check box(es)

    If you check this box, CDTest preloads a custom template resource and
    puts the value of the handle to the resource in the hInstance member of
    the common dialog box structure. CDTest does not create a custom
    template dialog box with this preloaded custom template unless you
    also put the XXX_ENABLETEMPLATEHANDLE flag in the Flags box. For
    example, to create a GetOpenFileName dialog box with a multiple
    selection files list box and a preloaded custom dialog box template,
    put the following in the Flags box:

        OFN_ENABLETEMPLATEHANDLE | OFN_ALLOWMULTISELECT

    Check the Preloaded Template box.

  - The Reset button.

    The Reset button sets all the values for the common dialog box creation
    structure back to the default values.

  - The Add and Clear buttons.

    These buttons are used to edit arrays that might be needed in common
    dialog box creation. The Clear button clears the whole array. The Add
    button adds what you have typed into the edit control of the combo box
    to the left of these buttons.

  - The Multithread buttons.

    These buttons create two threads and two dialog boxes.

There is no way to create a NULL pointer as a value to be used for one of
the dialog box elements that require a string pointer, such as the
lpstrFile element of the GetOpenFileName structure. If you leave this field
blank, CDTest will call GetOpenFileName with a valid pointer to a string
containing a NULL character as its first value.

Notes:

  - ChooseFont dialog box.

    When you open the ChooseFont dialog box, you see a list of flags and
    fonts on the right of the vertical dividing line. These do not affect
    the ChooseFont dialog box creation. These only exist to allow the user
    to compare the fonts found by ChooseFont with a list of fonts found
    independently of ChooseFont. The lists should be identical when the
    same flags are used.

    If you want printer fonts to be listed in the ChooseFont dialog box, you
    have to mark the Printer radio button in the "HDC Type" group.

    The ChooseFont dialog box is designed to give you a "No fonts found"
    error if you don't include the CF_SCREENFONTS or CF_PRINTERFONTS flag
    as part of the value in the Flags box.

  - Open/Save dialog box.

    If you want to create a custom template dialog box with the
    OFN_ENABLETEMPLATE flag, you need to specify the name of the template
    in the lpTemplateName structure field. The names are:

      - opentemp1 (single selection files list box)
      - opentemp2 (multiple selection files list box)

  - Find/Replace dialog box.

    If you want to create a custom template dialog box with the
    FR_ENABLETEMPLATE flag, you need to specify the name of the template
    in the lpTemplateName structure field. The names are:

      - frtemp1 (find template)
      - frtemp2 (replace template)


CDTEST.C

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

  File: cdtest.c

  Purpose:  Contains the functions which handle the main window's functionality --
            creating the main window, handling menu commands, exiting the program.

  Functions:

           WinMain()           - Program's entry point

           InitApplication()   - Registers class and some user defined messages

           InitInstance()      - Creates main window

           MainWndProc()       - Window procedure for main window

           HandleTheCommand()  - Processes all WM_COMMAND messages

           InitGlobals()       - Initializes all global variables that need to
                                 be initialized at startup

           MyAtol()            - Converts an ASCII string in either hexi-
                                 decimal or decimal notation to a LONG.

           AboutProc()         - Callback function for CDTEST's about box.

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

#include <windows.h>
#include <commdlg.h>
#include <winnls.h>
#include "cdtest.h"
#include "colors.h"
#include "save.h"
#include "print.h"
#include "title.h"
#include "replace.h"
#include "open.h"
#include "font.h"
#include "find.h"

/* Some defines, global variables, and function declarations */

#define szClass TEXT("cdtestclass")
#define szIcon  TEXT("theicon")
#define szMenu  TEXT("themenu")

#ifdef UNICODE
  #define szTitle TEXT("Common Dialog Test App - Unicode Version")
#else
  #define szTitle TEXT("Common Dialog Test App - ANSI Version")
#endif

void InitGlobals(void) ;

UINT uMode = IDM_HEXMODE ;

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

  Function: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

  Purpose:

    - Contains standard windows entry point

    - Initializes the application

    - Contains the main message loop

  Returns: Final msg.wParam

  Comments:

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

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

  MSG msg;

  UNREFERENCED_PARAMETER( lpCmdLine );

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

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

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

  return (msg.wParam);
}

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

  Function: InitApplication(HANDLE)

  Purpose:

    - Fills in the WNDCLASS structure

    - Registers messages needed to communicate with the common dialogs

    - Registers the window class

  Returns: The return value of RegisterClass().  If this fails then the
           whole program fails.

  Comments:

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

BOOL InitApplication(HANDLE hInstance)
{
  WNDCLASS  wc;

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)MainWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(hInstance, szIcon) ;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = GetStockObject(WHITE_BRUSH);
  wc.lpszMenuName = szMenu ;
  wc.lpszClassName = szClass ;

  InitGlobals() ;

  /* Register any messages that the common dialogs will need to
     communicate with this app. */

  nFindMsg       = RegisterWindowMessage((LPTSTR) FINDMSGSTRING) ;
  nOpenShareVMsg = RegisterWindowMessage((LPTSTR) SHAREVISTRING) ;
  nHelpMessage   = RegisterWindowMessage((LPTSTR) HELPMSGSTRING) ;

  return (RegisterClass(&wc));
}

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

 Function: InitInstance(HANDLE, int)

 Purpose:

   - Creates the main window

   - Shows the main window

 Returns: FALSE if it cannot create the window, TRUE if it can.

 Comments:

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

BOOL InitInstance(HANDLE hInstance, int nCmdShow)
{
    HWND hWnd;
    DWORD dwFlags = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU ;

    /* save the instance handle in a global variable */

    hInst = hInstance;

    /* Create the main window */

    hWnd = CreateWindow(szClass, szTitle, dwFlags,
                        10, 10, 400, 200, NULL, NULL, hInstance, NULL) ;

    if (!hWnd)
        return (FALSE);

    ShowWindow(hWnd, SW_SHOWNORMAL) ;
    UpdateWindow(hWnd);

    return (TRUE);
}

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

  Function: MainWndProc(HWND, UINT, UINT, LONG)

  Purpose:

    - Is the callback function that handles all messages for the main window

  Returns:

    - Returns DefWindowProc() for any message it does not explicitly
      respond to.

  Comments:

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

LONG APIENTRY MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam)
{
    switch (message)
    {
      case WM_CREATE:
        CheckMenuItem(GetMenu(hWnd), IDM_HEXMODE, MF_CHECKED) ;
        break ;

      case WM_COMMAND:
        HandleTheCommand(hWnd, wParam, lParam) ;
        break ;

      case WM_DESTROY:
          PostQuitMessage(0);
          break;

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

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

  Function: HandleTheCommand(HWND, UINT, UINT)

  Purpose:

    - Handles all WM_COMMAND messages passed to the MainWndProc().  The
      menu or control ID value for Win32 WM_COMMAND messages is contained
      in the low word of the wParam parameter.

  Returns: Nothing.

  Comments:

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

void HandleTheCommand(HWND hWnd, UINT wParam, LONG lParam)
{

  switch (LOWORD(wParam))
  {
    case IDM_COLOR:                //For any of the dialog creation
      DoColorsDialog(hWnd) ;       //commands, call the appropriate
      break ;                      //function.  The function will
                                   //create the dialog...
    case IDM_FONT:
      DoFontDialog(hWnd) ;
      break ;

    case IDM_TITLE:
      DoTitleDialog(hWnd) ;
      break ;

    case IDM_FIND:
      DoFindDialog(hWnd) ;
      break ;

    case IDM_OPEN:
      DoOpenDialog(hWnd) ;
      break ;

    case IDM_SAVE:
      DoSaveDialog(hWnd) ;
      break ;

    case IDM_PRINT:
      DoPrintDialog(hWnd) ;
      break ;

    case IDM_REPLACE:
      DoReplaceDialog(hWnd) ;
      break ;

    case IDM_EXIT:
      PostQuitMessage(0) ;
      break ;

    case IDM_HEXMODE:
    case IDM_DECIMALMODE:

      /* We need to maintain a global variable that will indicate what
         kind of number processing we have to do.  First, check the
         menu item corresponding to the new mode... */

      CheckMenuItem(GetMenu(hWnd), uMode, MF_UNCHECKED) ;
      uMode = wParam ;
      CheckMenuItem(GetMenu(hWnd), uMode, MF_CHECKED) ;

      /* and then create an appropriate filter for wsprintf() type
         functions */

      if (uMode == IDM_HEXMODE)
      {
        lstrcpy(szShortFilter, TEXT("%x")) ;
        lstrcpy(szLongFilter, TEXT("%lx")) ;
      }
      if (uMode == IDM_DECIMALMODE)
      {
        lstrcpy(szShortFilter, TEXT("%d")) ;
        lstrcpy(szLongFilter, TEXT("%ld")) ;
      }

      break ;

    case IDM_ABOUT:
       DialogBox(hInst, TEXT("about"), hWnd, AboutProc) ;
       break ;

    default: break ;
  }

  return ;
}

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

  Function: InitGlobals(void)

  Purpose:

    - Any global variables can be initialized here since this function is
      called on app startup.

  Returns: Nothing

  Comments:

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

void InitGlobals(void)
{

  /* not really too much to do here.  Create a hex wsprintf() filter since
     the app starts off in Hex mode. */

  lstrcpy(szShortFilter, TEXT("%x")) ;
  lstrcpy(szLongFilter, TEXT("%lx")) ;

}

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

  Function: MyAtol(LPTSTR, BOOL, LPBOOL)

  Purpose:

    - This function will convert an ascii string to a LONG.

  Returns:

    - If it receives an invalid ascii character, it will return 0 and
      set the LPBOOL variable to false...

  Comments:

      Since the function may need to deal with either a hex number or a decimal
      number, it should use a variable as a multiplier.

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

LONG MyAtol(LPTSTR szString, BOOL bHex, LPBOOL bSuccess)
{
  LPTSTR p ;
  LONG l ;
  LONG lMultiplier ;
  BOOL bDigit ;

  if (bHex)
    lMultiplier = 16 ;
  else
    lMultiplier = 10 ;

  p = szString ;
  l = 0 ;

  while (*p)      //while chars
  {
     bDigit = FALSE ;  //set to false for each char that we look at

     if (*p >= (TCHAR) '0' && *p <= (TCHAR) '9')  //is it an ascii char ?
     {
       bDigit = TRUE ;
       l+=(*p - (TCHAR) '0') ;
     }

     if (bHex)
     {
       if (*p >= (TCHAR) 'A' && *p <= (TCHAR) 'F')  //or hex?
       {
         l+=(*p - (TCHAR) 'A' + 10) ;
         bDigit = TRUE ;
       }

       if (*p >= (TCHAR) 'a' && *p <= (TCHAR) 'f')
       {
         l+=(*p - (TCHAR) 'a' + 10) ;
         bDigit = TRUE ;
       }

     }

     if (bDigit == FALSE)
     {
       *bSuccess = FALSE ;
       return 0 ;
     }

     p++ ;               //get next char

     if (*p)             //if there is going to be at least one more char
       l*=lMultiplier ;  //then multiply what we have by the multiplier...
  }

  *bSuccess = TRUE ;

  return l ;             //success! return the value.
}

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

  Function: AboutProc(HWND, UINT, UINT, LONG)

  Purpose:  Callback function for the about dialog box.

  Returns:  BOOL - FALSE ...

  Comments:

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

BOOL APIENTRY AboutProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{

  if (msg == WM_INITDIALOG)
    return TRUE;
  if (
       (msg == WM_COMMAND) &&
       (LOWORD(wParam) == IDCANCEL)
     )

     EndDialog(hwnd, FALSE) ;

  else
    return FALSE ;
}


CDTEST.H

#define UMSG_DECREMENTDLGCOUNT (WM_USER+1)

HANDLE hInst ;

BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);
long FAR PASCAL MainWndProc(HWND, UINT, UINT, LONG);
void HandleTheCommand(HWND, UINT, LONG) ;
BOOL APIENTRY AboutProc(HWND, UINT, UINT, LONG) ;

TCHAR szTemp[100] ;

TCHAR szShortFilter[5] ;
TCHAR szLongFilter[5] ;

#define IDM_COLOR   100
#define IDM_FONT    101
#define IDM_FIND    102
#define IDM_TITLE   103
#define IDM_OPEN    104
#define IDM_SAVE    105
#define IDM_PRINT   106
#define IDM_REPLACE 107
#define IDM_EXIT    108
#define IDM_HEXMODE 200
#define IDM_DECIMALMODE 201
#define IDM_MULTITHREAD2 300
#define IDM_ABOUT   301

UINT nFindMsg ;
UINT nOpenShareVMsg ;
UINT nHelpMessage ;


COLORS.C

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

  File: colors.c

  Purpose:

    This file contains all the code necessary for the colors dialog.

  Functions:

    DoColorsDialog()          -- Starts off the creation of the main colors dlg

    ColorsProc()              -- Callback function for the colors dialog

    InitColorStruct()         -- Initializes default CHOOSECOLOR strucure

    FillColorDlg()            -- Fills the colors dialog with the current
                                 CHOOSECOLOR structure values.

    GetColorDlg()             -- Retrieves users edits and puts them in
                                 the appropriate CHOOSECOLOR structure.

    ColorsHookProc()          -- Callback hook function if the ChooseColor()
                                 function is called with CC_ENABLEHOOK

    GetColorsResHandle()      -- Loads a custom template from the EXE
                                 and returns a handle to it

    DoChooseColorStuff()      -- Calls the ChooseColor() function.

    ColorThreadProc1()        -- Starting address for the first thread

    ColorThreadProc2()        -- Starting address for the second thread

    MultiThreadColorDlg()     -- Creates two threads in this process
                                 which each create ChooseColor() dialogs.

    EnableColorButtons()      -- Enables or disables buttons in the colors
                                 dialog.  Necessary for the multithreading.

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

#include <windows.h>
#include <commdlg.h>
#include <stdlib.h>
#include <winnls.h>
#include "cdtest.h"
#include "colors.h"

/* Function prototypes */
void InitColorStruct(HWND hwnd, LPCHOOSECOLOR) ;
void FillColorDlg(HWND hwnd, LPCHOOSECOLOR) ;
void GetColorDlg(HWND hwnd, LPCHOOSECOLOR) ;
void DoChooseColorStuff(HWND, LPCHOOSECOLOR) ;
UINT APIENTRY ColorsHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam) ;

/* Variables and external declarations */

CHOOSECOLOR cc ;
DWORD dwCustColors[16] ;
DWORD dwColor ;
HANDLE hDialogColors, hResColors ;
HANDLE GetColorsResHandle(void) ;
TCHAR szTemplateNameC[50] ;
extern UINT uMode ;
extern LONG MyAtol(LPTSTR, BOOL, LPBOOL) ;

/* Multithreading stuff */

DWORD dwColorThreadID1 ;
DWORD dwColorThreadID2 ;
DWORD dwColorThreadParm1 ;
DWORD dwColorThreadParm2 ;
DWORD ColorThreadProc1(LPDWORD) ;
DWORD ColorThreadProc2(LPDWORD) ;
void MultiThreadColorDlg(void) ;
void EnableColorButtons(HWND, BOOL) ;
HANDLE hColorThread1 ;
HANDLE hColorThread2 ;
HWND hwndMainColor ;
int nOpenColorDlgCount ;
CHOOSECOLOR ccThread1 ;
CHOOSECOLOR ccThread2 ;

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

  Function: DoColorsDialog(HWND)

  Purpose: To create the ChooseColor() common dialog creation dialog.

  Returns: Nothing.

  Comments:

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

void DoColorsDialog(HWND hwnd)
{

  DialogBox(hInst, MAKEINTRESOURCE(ID_COLORSDIALOG),
            hwnd, ColorsProc) ;

}

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

  Function: ColorsProc(HWND, UINT, UINT, LONG)

  Purpose: The callback function for the main colors dialog.

  Returns: TRUE or FALSE depending on the situation.

  Comments:

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

BOOL APIENTRY ColorsProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  BOOL bRet = FALSE ;

  switch (msg)
  {

    case WM_INITDIALOG:

       InitColorStruct(hwnd, &cc) ;  //initialize the CHOOSECOLOR structure
       FillColorDlg(hwnd, &cc) ;  //and fill the colors dialog.

       nOpenColorDlgCount = 0 ;

       hwndMainColor = hwnd ;

       *(&ccThread1) = *(&ccThread2) = *(&cc) ;

       SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEC)) ;

       break ;

     case UMSG_DECREMENTDLGCOUNT:  //user defined message indicating
                                   //the closure of a multithreaded dialog

       /* Keep the main colors dialog buttons disabled until the last
          multithreaded dialog function has returned. */

       nOpenColorDlgCount-- ;

       if (nOpenColorDlgCount == 0)
         EnableColorButtons(hwnd, TRUE) ;

       break ;

     case WM_COMMAND:

        switch (LOWORD(wParam))
        {
          case IDOK:

            GetColorDlg(hwnd, &cc) ;            //get user's choices

            DoChooseColorStuff(hwnd, &cc) ;     //do the dialog

            break ;

          case ID_RESETC:

            SendDlgItemMessage(hwnd, ID_CUSTCOLORSC, CB_RESETCONTENT,
              (WPARAM) 0, (LPARAM) 0) ;

            InitColorStruct(hwnd, &cc) ;
            FillColorDlg(hwnd, &cc) ;

            SendDlgItemMessage(hwnd, ID_NULLSTRUCTCOLOR, BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;
            SendDlgItemMessage(hwnd, ID_PRELOADEDCOLORS, BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;

            SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEC)) ;

            break ;

          case IDCANCEL:
            EndDialog(hwnd, FALSE) ;
            break ;

          case ID_ADD1C:
            GetDlgItemText(hwnd, ID_CUSTCOLORSC, szTemp, 100) ;

            if (*szTemp)
              SendDlgItemMessage(hwnd, ID_CUSTCOLORSC, CB_ADDSTRING,
                                 (WPARAM) 0, (LPARAM) (LPTSTR) szTemp) ;
            break ;

          case ID_CLEAR1C:
            SendDlgItemMessage(hwnd, ID_CUSTCOLORSC, CB_RESETCONTENT,
                               (WPARAM) 0, (LPARAM) 0) ;
            break ;

          case ID_MULTITHREADCOLORS:

            /* Start the dialog count off at 2, disable the main dialog's
               buttons, and multithread */

            nOpenColorDlgCount = 2 ;

            EnableColorButtons(hwnd, FALSE) ;

            MultiThreadColorDlg() ;

            break ;

          default: break ;
        }

    default:

      /* If the help button is pressed in the ChooseColor()
         dialog, it will send a message Registered with RegisterWindowMessage()
         to the parent window.  The message nHelpMessage was registered
         at application startup */

      if (msg == nHelpMessage)
        MessageBox(GetForegroundWindow(), TEXT("Hello from the help button"),
                   TEXT("Colors Help Button"), MB_OK | MB_APPLMODAL) ;

      break ;
  }

  return FALSE ;
}

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

  Function: InitColorStruct(HWND, LPCHOOSECOLOR)

  Purpose: Fills the default CHOOSECOLOR structure with some default
           values.

  Returns: Nothing.

  Comments:

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

void InitColorStruct(HWND hwnd, LPCHOOSECOLOR pcc)
{
  int i ;

  pcc->lStructSize  = sizeof(CHOOSECOLOR) ;
  pcc->hwndOwner    = hwnd ;
  pcc->hInstance    = (HANDLE) hInst ;

  dwColor = RGB(0, 0, 0) ;
  pcc->rgbResult    = dwColor ;

  for (i=0; i<16; i++)
    dwCustColors[i] = (RGB(255-i*10, i, i*10)) ;

  pcc->lpCustColors = (LPDWORD) dwCustColors ;

  pcc->Flags     = CC_FULLOPEN | CC_SHOWHELP | CC_RGBINIT ;

  pcc->lCustData = 0 ;

  pcc->lpfnHook = ColorsHookProc ;

  lstrcpy(szTemplateNameC, TEXT("clrtemp")) ;         //name of custom template

  pcc->lpTemplateName = (LPTSTR) szTemplateNameC ;

  return ;
}

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

  Function: FillColorDlg(HWND, LPCHOOSECOLOR)

  Purpose: Fills the colors dialog with the current contents of the
           CHOOSECOLOR structure passed in as the second parameter.

  Returns: Nothing.

  Comments:

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

void FillColorDlg(HWND hwnd, LPCHOOSECOLOR pcc)
{
  int i ;

  wsprintf(szTemp, szLongFilter, pcc->lStructSize) ;
  SetDlgItemText(hwnd, ID_STRUCTSIZEC, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcc->hwndOwner) ;
  SetDlgItemText(hwnd, ID_HWNDOWNERC, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcc->hInstance) ;
  SetDlgItemText(hwnd, ID_HINSTANCEC, szTemp) ;

  wsprintf(szTemp, szLongFilter, pcc->rgbResult) ;
  SetDlgItemText(hwnd, ID_RGBRESULTC, szTemp) ;

  SendDlgItemMessage(hwnd, ID_CUSTCOLORSC, CB_RESETCONTENT,
                     (WPARAM) 0, (LPARAM) 0) ;

  for (i=0; i<16; i++)
  {
    wsprintf(szTemp, szLongFilter, dwCustColors[i]) ;
    SendDlgItemMessage(hwnd, ID_CUSTCOLORSC, CB_ADDSTRING,
                       (WPARAM) 0, (LPARAM) (LPTSTR) szTemp) ;
  }

  SendDlgItemMessage(hwnd, ID_CUSTCOLORSC, CB_SETCURSEL,
                     (WPARAM) 0 , (LPARAM) 0) ;

  wsprintf(szTemp, szLongFilter, pcc->Flags) ;
  SetDlgItemText(hwnd, ID_FLAGSC, szTemp) ;

  wsprintf(szTemp, szLongFilter, pcc->lCustData) ;
  SetDlgItemText(hwnd, ID_CUSTDATAC, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcc->lpfnHook) ;
  SetDlgItemText(hwnd, ID_HOOKC, szTemp) ;

  SetDlgItemText(hwnd, ID_TEMPLATEC, (LPTSTR) pcc->lpTemplateName) ;
}

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

  Function: GetColorDlg(HWND, LPCHOOSECOLOR)

  Purpose: Retrieves the users entries in the colors dialog edit boxes
           and puts them in the appropriate CHOOSECOLOR structure
           members.

  Returns: Nothing.

  Comments:

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

void GetColorDlg(HWND hwnd, LPCHOOSECOLOR pcc)
{
  int i ;
  BOOL b ;
  TCHAR szNum[30] ;

  #define WSIZECC 30

  GetDlgItemText(hwnd, ID_STRUCTSIZEC, szNum, WSIZECC) ;
  pcc->lStructSize = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HWNDOWNERC, szNum, WSIZECC) ;
  pcc->hwndOwner = (HWND) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HINSTANCEC, szNum, WSIZECC) ;
  pcc->hInstance = (HANDLE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_RGBRESULTC, szNum, WSIZECC) ;
  pcc->rgbResult = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  for (i=0; i<16; i++)
  {
    if (SendDlgItemMessage(hwnd, ID_CUSTCOLORSC, CB_GETLBTEXT,
                          (WPARAM) i, (LPARAM) (LPTSTR) szNum) != CB_ERR)

      dwCustColors[i] = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

    else
      dwCustColors[i] = (DWORD) 0 ;

    *(pcc->lpCustColors + i) = dwCustColors[i] ;
  }

  GetDlgItemText(hwnd, ID_FLAGSC, szNum, WSIZECC) ;
  pcc->Flags = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_CUSTDATAC, szNum, WSIZECC) ;
  pcc->lCustData = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HOOKC, szNum, WSIZECC) ;
  pcc->lpfnHook = (LPCCHOOKPROC) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_TEMPLATEC, (LPTSTR) pcc->lpTemplateName, 50) ;

  return ;
}

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

  Function: ColorsHookProc(HWND, UINT, UINT, LONG) ;

  Purpose:  This function will be called before the normal ChooseColor()
            logic is envoked.

  Returns: FALSE to instruct ChooseColor() to proceed with it's normal
           processing of the message.  TRUE to discard normal processing.

  Comments:

    To enable the hook, type the value for "CC_ENABLEHOOK" in the
    "Flags" edit box.

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

UINT APIENTRY ColorsHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  LPCHOOSECOLOR pCc ;
  TCHAR szMsg[50] ;

  switch(msg)
  {
    case WM_INITDIALOG:

      /* During initialization of the ChooseColor() dialog, if there is a
         hook proc the ChooseColor() function will send a pointer to the
         current CHOOSECOLOR structure in the wParam parameter */

      pCc = (LPCHOOSECOLOR) lParam ;

      if (pCc->lCustData != 0L)
      {
        wsprintf(szMsg, TEXT("CHOOSECOLOR->lCustData is: %ld"), pCc->lCustData) ;
        MessageBox(hwnd, szMsg, TEXT("lCustData Sent!"), MB_OK | MB_APPLMODAL) ;
      }

      SetWindowText(hwnd, TEXT("Color Hook Proc Dialog")) ;

      break ;

    default:
      break ;
  }

  return FALSE ;   //send msg to the common dialog code
}

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

  Function: GetColorsResHandle(void)

  Purpose: Creates a handle to the custom template and returns it to the
           caller.

  Returns: HANDLE to a preloaded custom template

  Comments:

    To use this preloaded template, mark the "Preloaded Template" checkbox
    and enter CC_ENABLETEMPLATEHANDLE in the "Flags" edit box.

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

HANDLE GetColorsResHandle(void)
{

  hResColors = FindResource(hInst, TEXT("clrtemp"), RT_DIALOG) ;

  hDialogColors = LoadResource(hInst, hResColors) ;

  return hDialogColors ;
}

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

  Function:DoChooseColorStuff(HWND, LPCHOOSECOLOR)

  Purpose: Does the actual calling of the ChooseColor() function.

  Returns: Nothing.

  Comments:

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

void DoChooseColorStuff(HWND hwnd, LPCHOOSECOLOR pcc)
{
  BOOL bRet ;

  /* If the "Preloaded template button is checked, load a handle for
     the custom template and put it in the hInstance member of the
     CHOOSECOLOR structure */

  if (IsDlgButtonChecked(hwnd, ID_PRELOADEDCOLORS) == 1)
    pcc->hInstance = GetColorsResHandle() ;

   /* If the "NULL Structure" box is checked, call ChooseColor() will
      a NULL pointer.  Otherwise call with the current CHOOSECOLOR pointer */

  if (IsDlgButtonChecked(hwnd, ID_NULLSTRUCTCOLOR) == 1)
    bRet = ChooseColor((LPCHOOSECOLOR) NULL) ;
  else
    bRet = ChooseColor(pcc) ;

  if (pcc->hInstance)
  {
    FreeResource(hDialogColors) ;
    hDialogColors = (HANDLE) 0 ;
    hResColors = (HANDLE) 0 ;
  }

  /* Fill the results into the main colors dialog */

  FillColorDlg(hwnd, pcc) ;

  wsprintf(szTemp, szShortFilter, bRet) ;
  SetDlgItemText(hwnd, ID_RETURNC, szTemp) ;

  wsprintf(szTemp, szLongFilter, CommDlgExtendedError()) ;
  SetDlgItemText(hwnd, ID_ERRORC, szTemp) ;

}

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

  Function:ColorThreadProc1(LPDWORD)

  Purpose: Starting address for first thread

  Returns: Any DWORD value.

  Comments:

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

DWORD ColorThreadProc1(LPDWORD pdw)
{
  GetColorDlg(hwndMainColor, &ccThread1) ;
  DoChooseColorStuff(hwndMainColor, &ccThread1) ;

  PostMessage(hwndMainColor, UMSG_DECREMENTDLGCOUNT, 0, 0L ) ;

  return 0L ;
}

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

  Function:ColorThreadProc2(LPDWORD)

  Purpose: Starting address for second thread

  Returns: Any DWORD value.

  Comments:

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

DWORD ColorThreadProc2(LPDWORD pdw)
{
  GetColorDlg(hwndMainColor, &ccThread2) ;
  DoChooseColorStuff(hwndMainColor, &ccThread2) ;

  PostMessage(hwndMainColor, UMSG_DECREMENTDLGCOUNT, 0, 0L ) ;

  return 0L ;
}

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

  Function:MultiThreadColorDlg(void)

  Purpose: Creates two threads which will each create a new ChooseColor()
           dialog.

  Returns: Nothing.

  Comments:

    Do not pass any parameters to this function that may be used by the
    ChooseColor dialog routines because this function will return before
    the new threads are finished.

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

void MultiThreadColorDlg(void)
{

  dwColorThreadParm1 = dwColorThreadParm2 = 0L ;

  if (!(hColorThread1 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                     (LPTHREAD_START_ROUTINE) ColorThreadProc1,
                                     &dwColorThreadParm1, CREATE_SUSPENDED, &dwColorThreadID1)))

  {
    MessageBox(GetForegroundWindow(), TEXT("Error creating thread 1"), NULL,
               MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;

    nOpenColorDlgCount = 0 ;

    EnableColorButtons(hwndMainColor, TRUE) ;

    return ;
  }

  if (!(hColorThread2 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                     (LPTHREAD_START_ROUTINE) ColorThreadProc2,
                                     &dwColorThreadParm2, CREATE_SUSPENDED, &dwColorThreadID2)))
  {
    MessageBox(GetForegroundWindow(), TEXT("Error creating thread 2"), NULL,
               MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;

    nOpenColorDlgCount = 0 ;

    EnableColorButtons(hwndMainColor, TRUE) ;

    return ;
  }

  ResumeThread(hColorThread1) ;

  ResumeThread(hColorThread2) ;

  return ;
}

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

  Function:EnableColorButtons(HWND, BOOL)

  Purpose: Enables or disables the buttons on the main colors dialog.
           Necessary when multithreading.

  Returns: Nothing.

  Comments:

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

void EnableColorButtons(HWND hwnd, BOOL bEnable)
{
  EnableWindow(GetDlgItem(hwnd, IDOK), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, IDCANCEL), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_RESETC), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_MULTITHREADCOLORS), bEnable) ;
}


COLORS.H

void DoColorsDialog(HWND) ;
BOOL APIENTRY ColorsProc(HWND, UINT, UINT, LONG) ;

#define ID_HWNDOWNERC               500
#define ID_STRUCTSIZEC              501
#define ID_HINSTANCEC               502
#define ID_CUSTCOLORSC              503
#define ID_CUSTDATAC                504
#define ID_HOOKC                    505
#define ID_TEMPLATEC                506
#define ID_RGBRESULTC               507
#define ID_ADD1C                    508
#define ID_CLEAR1C                  509
#define ID_ERRORC                   510
#define ID_RETURNC                  511

#define ID_COLORSDIALOG             602

#define ID_FLAGSC                   512
#define ID_RESETC                   603
#define ID_NULLSTRUCTCOLOR          604
#define ID_PRELOADEDCOLORS          605
#define ID_MULTITHREADCOLORS        606


DEVMODE.H

#define ID_DEVMODEDIALOG            710

#define ID_DMDRIVERDATA               118
#define ID_ADD1DM                     119
#define ID_CLEAR1DM                   120
#define ID_DMDEVNAME                  121
#define ID_DMSPECVER                  122
#define ID_DMSIZEDEV                  123
#define ID_DMDRIVERVER                124
#define ID_DMDRIVEREXTRA              125
#define ID_DMPAPERSIZE                128
#define ID_DMPAPERLENGTH              129
#define ID_DMPAPERWIDTH               130
#define ID_DMSCALE                    131
#define ID_DMCOPIES                   132
#define ID_DMDEFAULTSOURCE            133
#define ID_DMPRINTQUALITY             134
#define ID_DMCOLOR                    135
#define ID_DMDUPLEX                   136
#define ID_DMORIENTATION              137
#define ID_DMRESETDEV                 127
#define ID_DMFIELDS                   117
#define ID_DMCOLLATE                  138
#define ID_DMFORMNAME                 141
#define ID_DMBITSPERPEL               142
#define ID_DMPELSWIDTH                146
#define ID_DMPELSHEIGHT               147
#define ID_DMDISPLAYFREQ              148


DEVNAMES.H

#define ID_WDRIVEROFF               105
#define ID_WDEVICEOFF               106
#define ID_WOUTPUTOFF               107
#define ID_WDEFAULT                 108
#define ID_DEVNAMESDIALOG           700
#define ID_RESETDEVNAMES            109


FIND.C

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

  File: find.c

  Purpose:

     Manages CDTEST's find/replace dialog box.

  Functions:

    - lpfnFilterProc()      -- A callback function for a filter that must be
                               installed if a modeless dialog is created with
                               another dialog as its parent.

    - DoFindDialog()        -- Creates CDTEST's Open/Save dialog.

    - FindProc()            -- Callback function for CDTEST's Find/Replace dlg.

    - InitFindStruct()      -- Fills a FINDREPLACE structure with some defaults.

    - FillFindDlg()         -- Fills CDTESTs Find/Replace dialog with the contents
                               of a FINDREPLACE structure.

    - GetFindDlg()          -- Retrieves the users edit's from CDTEST's find/
                               replace dialog and puts them in a FINDREPLACE
                               structure.

    - FindReplaceHookProc() -- Callback function for FindText() or ReplaceText()
                               which will be called if either of these dialogs
                               is created with the FR_ENABLEHOOK flag.

    - GetFindDlgHandle()    -- Returns a handle to a preloaded FindText() template.

    - GetReplaceDlgHandle() -- Returns a handle to a preloaded ReplaceText() template.

    - DoFindRepStuff()      -- Calls FindText() or ReplaceText().

  NOTE: CDTEST does not multithread the FindText() or the ReplaceText()
        common dialogs.  The reason for this is that since these dialogs
        are modeless, their creation functions return immediately after the
        dialogs are created as opposed to other dialog functions that
        don't return until after the dialog has been destroyed by the user.

        As a result, any threads that create modeless dialogs will end
        immediately unless the threads themselves have separate message
        loops.  For the sake of clarity, this functionality has not been
        added to CDTEST.

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

#include <windows.h>
#include <commdlg.h>
#include <stdlib.h>
#include <winnls.h>
#include "cdtest.h"
#include "find.h"
#include "replace.h"

/* All functions used in this module + some exported ones */

void InitFindStruct(HWND, LPFINDREPLACE) ;
void FillFindDlg(HWND, LPFINDREPLACE) ;
void GetFindDlg(HWND, LPFINDREPLACE) ;
extern UINT uMode ;
extern LONG MyAtol(LPTSTR, BOOL, LPBOOL) ;
UINT APIENTRY FindReplaceHookProc(HWND, UINT, UINT, LONG) ;
void DoFindRepStuff(LPFINDREPLACE) ;

/* All global variables used in this module */

HWND hwndFind ;
HWND hwndMainDialog ;

FINDREPLACE fr ;
LPFINDREPLACE lpFr ;

TCHAR szFindWhat[100] ;
TCHAR szReplaceWith[100] ;
TCHAR szTemplate[40] ;

HANDLE hResFind, hDialogFind ;
HANDLE GetFindDlgHandle(void) ;
HANDLE GetReplaceDlgHandle(void) ;

HBRUSH hBrushDlg ;
HBRUSH hBrushEdit ;    //brush handles for new colors done with hook proc
HBRUSH hBrushButton ;

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

  Function: lpfnFilterProc(int, WPARAM, LAPRAM)

  Purpose: This is needed if a modeless dialog is created with its parent
           as another dialog box.

  Returns: TRUE if the message was handled and FALSE if not.

  Comments:

    The reason for this is that the DialogBox() procedure does not call
    the IsDialogMessage() function before it processes messages, so we
    need to install a hook function to do it for us.

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

LRESULT CALLBACK lpfnFilterProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  static bFirstTime = TRUE ;

  if (nCode < 0)
    return CallNextHookEx(hHook, nCode, wParam, lParam) ;

  if (nCode == MSGF_DIALOGBOX && bFirstTime)
  {
    bFirstTime = FALSE ;

    if (hwndFind && IsDialogMessage(hwndFind, (LPMSG) lParam))
    {
      bFirstTime = TRUE ;
      return 1L ;
    }

    else
    {
      bFirstTime = TRUE ;
      return 0L ;
    }
  }
  else return 0L ;
}

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

  Function: DoFindDialog(HWND)

  Purpose: This function installs the Hook function, creates the Find/
           Replace dialog, and un-installs the Hook.

  Returns: Nothing.

  Comments:

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

void DoFindDialog(HWND hwnd)
{
  bDoFindDlg = TRUE ;

  /* this is a little different than the others.  If the dialog is just
     created normally, it will make no IsDlgMessage() checks and the
     find/replace dialogs will have no keyboard input (i.e. tabbing and
     alt+key-ing from control to control.  To fix this, a message hook
     and message filter have to be installed

     It must be set to only look at the input for the current thread, or other
     programs will be interrupted by this hook also.
  */

  hHook = SetWindowsHookEx(WH_MSGFILTER, lpfnFilterProc,
                           hInst, GetCurrentThreadId()) ;

  if (!hHook) return ;

  DialogBox(hInst, MAKEINTRESOURCE(ID_FINDDIALOG), hwnd, FindProc) ;

  UnhookWindowsHookEx(hHook) ;

}

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

  Function: FindProc(HWND, UINT, UINT, LONG)

  Purpose: This is the callback function for the CDTEST's Find/Replace
           Dialog.

  Returns: TRUE or FALSE depending on the situation.

  Comments:

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

BOOL APIENTRY FindProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  switch (msg)
  {
    case WM_INITDIALOG:

        if (bDoFindDlg)
          SetWindowText(hwnd, TEXT("FindText()")) ;
        else
          SetWindowText(hwnd, TEXT("ReplaceText()")) ;

        InitFindStruct(hwnd, &fr) ;
        FillFindDlg(hwnd, &fr) ;

        hwndMainDialog = hwnd ;

        /* The find and replace dialogs are a lot harder to multithread because they
           are modeless.  Modeless dialog creation functions return right after the
           dialog is created.  Since ExitThread will be called at this point, it is
           probably not possible to multithread these dialogs without a separate
           GetMessage() loop.
        */

        EnableWindow(GetDlgItem(hwnd, ID_MULTITHREADFINDREP), FALSE) ;

        SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEFT)) ;

        break ;

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
          case IDOK:
            GetFindDlg(hwnd, &fr) ;
            DoFindRepStuff(&fr) ;
            break ;

          case ID_RESETFIND:
            SendDlgItemMessage(hwnd, ID_FRNULLSTRUCT, BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;
            SendDlgItemMessage(hwnd, ID_PRELOADEDFIND, BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;
            InitFindStruct(hwnd, &fr) ;
            FillFindDlg(hwnd, &fr) ;
            SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEFT)) ;
            break ;

          case IDCANCEL:
            EndDialog(hwnd, FALSE) ;

            break ;

          default: break ;
        }

    }

    default:

    /* nFindMsg is registered at program startup (see CDTEST.c).  The
       FindText() and ReplaceText() dialogs will communicate with the
       calling application via this message. */

    if (msg == nFindMsg)
    {
        lpFr = (LPFINDREPLACE) lParam ;

        if (lpFr->Flags & FR_DIALOGTERM)
        {
          PostMessage(hwnd, UMSG_DECREMENTDLGCOUNT, 0, 0L) ;

          if (hDialogFind)
          {
            FreeResource(hDialogFind) ;
            hDialogFind = (HANDLE) 0 ;
            hResFind = (HANDLE) 0 ;
          }

          hwndFind = (HWND) 0 ;
        }

        FillFindDlg(hwnd, &fr) ;

        wsprintf(szTemp, szLongFilter, CommDlgExtendedError()) ;
        SetDlgItemText(hwnd, ID_ERRORFT, szTemp) ;

        wsprintf(szTemp, szLongFilter, hwndFind) ;
        SetDlgItemText(hwnd, ID_RETURNFT, szTemp) ;
    }

    /* If the help button is pressed in the FindText() or ReplaceText()
       dialogs, it will send a message Registered with RegisterWindowMessage()
       to the parent window.  The message nHelpMessage was registered
       at application startup */

    if (msg == nHelpMessage)
      MessageBox(GetForegroundWindow(), TEXT("Hello from the help button"),
                 TEXT("Find Help Button"), MB_OK | MB_APPLMODAL) ;

    break ;
  }

  return FALSE ;
}

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

  Function: InitFindStruct(HWND, LPFINDREPLACE)

  Purpose: Fills a FINDREPLACE structure with some defaults.

  Returns: Nothing.

  Comments:

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

void InitFindStruct(HWND hwnd, LPFINDREPLACE pfr)
{
  pfr->lStructSize = (DWORD) sizeof(FINDREPLACE) ;
  pfr->hwndOwner = hwnd ;
  pfr->hInstance = (HANDLE) hInst ;
  pfr->Flags = FR_DOWN | FR_SHOWHELP  ;

  lstrcpy(szFindWhat, TEXT("Word to find")) ;
  pfr->lpstrFindWhat = szFindWhat ;
  pfr->wFindWhatLen = 100 ;

  lstrcpy(szReplaceWith, TEXT("Replace with word")) ;
  pfr->lpstrReplaceWith = szReplaceWith ;
  pfr->wReplaceWithLen = 100 ;

  pfr->lCustData = (DWORD) 0 ;

  pfr->lpfnHook = FindReplaceHookProc ;

  lstrcpy(szTemplate, TEXT("fttemp1")) ;

  pfr->lpTemplateName = szTemplate ;
}

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

  Function: FillFindDlg(HWND, LPFINDREPLACE)

  Purpose:  Fills CDTEST's Find/Replace dialog with the contents of a
            FINDREPLACE structure.

  Returns:  Nothing.

  Comments:

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

void FillFindDlg(HWND hwnd, LPFINDREPLACE pfr)
{
  wsprintf(szTemp, szLongFilter, pfr->lStructSize) ;
  SetDlgItemText(hwnd, ID_STRUCTSIZEFT, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pfr->hwndOwner) ;
  SetDlgItemText(hwnd, ID_HWNDOWNERFT, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pfr->hInstance) ;
  SetDlgItemText(hwnd, ID_HINSTANCEFT, szTemp) ;

  wsprintf(szTemp, szLongFilter, pfr->Flags) ;
  SetDlgItemText(hwnd, ID_FLAGSFT, szTemp) ;

  SetDlgItemText(hwnd, ID_FINDWHATFT, pfr->lpstrFindWhat) ;

  wsprintf(szTemp, szLongFilter, pfr->wFindWhatLen) ;
  SetDlgItemText(hwnd, ID_FINDWHATLENFT, szTemp) ;

  SetDlgItemText(hwnd, ID_REPLACEWITHFT, pfr->lpstrReplaceWith) ;

  wsprintf(szTemp, szLongFilter, pfr->wReplaceWithLen) ;
  SetDlgItemText(hwnd, ID_REPLACEWITHLENFT, szTemp) ;

  wsprintf(szTemp, szLongFilter, pfr->lCustData) ;
  SetDlgItemText(hwnd, ID_CUSTDATAFT, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pfr->lpfnHook) ;
  SetDlgItemText(hwnd, ID_HOOKFT, szTemp) ;

  SetDlgItemText(hwnd, ID_TEMPLATEFT, pfr->lpTemplateName) ;
}

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

  Function: GetFindDlg(HWND, LPFINDREPLACE)

  Purpose:  Fills a FINDREPLACE structure with the user's edits in CDTEST's
            Find/Replace dialog.

  Returns:  Nothing.

  Comments:

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

void GetFindDlg(HWND hwnd, LPFINDREPLACE pfr)
{
  TCHAR szNum[30] ;
  BOOL b ;

  #define WSIZEFR 30

  GetDlgItemText(hwnd, ID_STRUCTSIZEFT, szNum, WSIZEFR) ;
  pfr->lStructSize = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HWNDOWNERFT, szNum, WSIZEFR) ;
  pfr->hwndOwner = (HWND) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HINSTANCEFT, szNum, WSIZEFR) ;
  pfr->hInstance = (HANDLE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_FLAGSFT, szNum, WSIZEFR) ;
  pfr->Flags = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_FINDWHATFT, szFindWhat, 100) ;

  GetDlgItemText(hwnd, ID_REPLACEWITHFT, szReplaceWith, 100) ;

  GetDlgItemText(hwnd, ID_FINDWHATLENFT, szNum, WSIZEFR) ;
  pfr->wFindWhatLen = (WORD) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_REPLACEWITHLENFT, szNum, WSIZEFR) ;
  pfr->wReplaceWithLen = (WORD) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_CUSTDATAFT, szNum, WSIZEFR) ;
  pfr->lCustData = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HOOKFT, szNum, WSIZEFR) ;
  pfr->lpfnHook = (LPFRHOOKPROC) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_TEMPLATEFT, szTemplate, 40) ;
}

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

  Function: FindReplaceHookProc(HWND, UINT, UINT, LONG)

  Purpose:  Is the callback function that will be called by FindText()
            or ReplaceText() if the function is called with the
            FR_ENABLEHOOK flag.

  Returns:  TRUE to discard the message, and FALSE to instruct the common
            dialogs to process the message with the default logic.

  Comments:

     NOTE!

     If the application returns FALSE in response to the WM_INITDIALOG
     message, it is then responsible for displaying the dialog by
     calling ShowWindow() and UpdateWindow().

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

UINT APIENTRY FindReplaceHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  LPFINDREPLACE pFr ;
  TCHAR szMsg[75] ;
  TCHAR szTmp[20] ;
  int i ;
  TCHAR szDefString[] = TEXT("Default String ") ;

  switch(msg)
  {
    case WM_INITDIALOG:

      pFr = (LPFINDREPLACE) lParam ;

      if (pFr->lCustData != 0L)
      {
        wsprintf(szMsg, TEXT("FINDREPLACE->lCustData is: %ld"), pFr->lCustData) ;

        MessageBox(hwnd, szMsg, TEXT("lCustData Sent!"), MB_OK | MB_APPLMODAL) ;
      }

      SetWindowText(hwnd, TEXT("Find Hook Proc Dialog")) ;

      if (GetDlgItem(hwnd, ID_DEFSTRINGS))
      {
        for (i=0; i<5; i++)
        {
          lstrcpy(szMsg, szDefString) ;
          wsprintf(szTmp, TEXT("Number %d"), i+1) ;
          lstrcat(szMsg, szTmp) ;
          SendDlgItemMessage(hwnd, ID_DEFSTRINGS, LB_ADDSTRING, (WPARAM) 0,
                             (LONG) (LPTSTR) szMsg) ;
        }
      }

      return TRUE ;

      break ;

    /* use the WM_CTLCOLOR* messages to change the color of the Open
       dialog */

    case WM_CTLCOLORDLG:

        if (!hBrushDlg)
            hBrushDlg = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushDlg ;

        break ;

    case WM_CTLCOLORBTN:

        SetBkMode((HDC) wParam, TRANSPARENT) ;   //sets background color
                                                 //for push and check box
                                                 //buttons...

        if (!hBrushButton)
            hBrushButton = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushButton ;

        break ;

    case WM_CTLCOLORSTATIC:

        SetTextColor((HDC) wParam, RGB(0x00, 0xff, 0x00)) ;  //green
        SetBkMode((HDC) wParam, TRANSPARENT) ;               //transparent text

        if (!hBrushDlg)
            hBrushDlg = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushDlg ;

        break ;

    default:
      break ;
  }

  return FALSE ;   //send msg to the common dialog code
}

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

  Function: GetFindDlgHandle(void)

  Purpose:  Finds, loads, and returns a handle to the custom template
            for FindText() in CDTEST.EXE.

  Returns:  HANDLE to the dialog resource.

  Comments:

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

HANDLE GetFindDlgHandle(void)
{
  hResFind = FindResource(hInst, TEXT("fttemp1"), RT_DIALOG) ;

  hDialogFind = LoadResource(hInst, hResFind) ;

  return hDialogFind ;
}

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

  Function: GetReplaceDlgHandle(void)

  Purpose:  Finds, loads, and returns a handle to the custom template
            for ReplaceText() in CDTEST.EXE.

  Returns:  HANDLE to the dialog resource.

  Comments:

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

HANDLE GetReplaceDlgHandle(void)
{
  hResFind = FindResource(hInst, TEXT("fttemp2"), RT_DIALOG) ;

  hDialogFind = LoadResource(hInst, hResFind) ;

  return hDialogFind ;
}

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

  Function: DoFindReplaceStuff(LPFINDREPLACE)

  Purpose:  Calls FindText() or ReplaceText().

  Returns:  Nothing:

  Comments:

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

void DoFindRepStuff(LPFINDREPLACE pfr)
{
  if (IsDlgButtonChecked(hwndMainDialog, ID_PRELOADEDFIND) == 1)
  {
    if (bDoFindDlg)
      pfr->hInstance = GetFindDlgHandle() ;
    else
      pfr->hInstance = GetReplaceDlgHandle() ;
  }

  if (bDoFindDlg)
  {
    if (IsDlgButtonChecked(hwndMainDialog, ID_FRNULLSTRUCT) == 1)
    {
        hwndFind = FindText((LPFINDREPLACE) NULL) ;
    }
    else
    {
        hwndFind = FindText(pfr) ;
    }
  }
  else
  {
    if (IsDlgButtonChecked(hwndMainDialog, ID_FRNULLSTRUCT) == 1)
    {
        hwndFind = ReplaceText((LPFINDREPLACE) NULL) ;
    }
    else
    {
        hwndFind = ReplaceText(pfr) ;
    }

  }

  wsprintf(szTemp, szLongFilter, CommDlgExtendedError()) ;
  SetDlgItemText(hwndMainDialog, ID_ERRORFT, szTemp) ;

  wsprintf(szTemp, szLongFilter, hwndFind) ;
  SetDlgItemText(hwndMainDialog, ID_RETURNFT, szTemp) ;
}


FIND.H

BOOL bDoFindDlg ;

HHOOK hHook ;

LRESULT CALLBACK lpfnFilterProc(int, WPARAM, LPARAM) ;

void DoFindDialog(HWND) ;
BOOL APIENTRY FindProc(HWND, UINT, UINT, LONG) ;

#define ID_STRUCTSIZEFT             111
#define ID_HWNDOWNERFT              112
#define ID_FLAGSFT                  113
#define ID_FINDWHATFT               114
#define ID_REPLACEWITHFT            115
#define ID_FINDWHATLENFT            116
#define ID_REPLACEWITHLENFT         117
#define ID_CUSTDATAFT               118
#define ID_HOOKFT                   119
#define ID_TEMPLATEFT               120
#define ID_ERRORFT                  123
#define ID_RETURNFT                 124

#define ID_FINDDIALOG               600
#define ID_HINSTANCEFT              125
#define ID_RESETFIND                127
#define ID_FLAGSMSG                 128
#define ID_FRNULLSTRUCT             601
#define ID_PRELOADEDFIND            602
#define ID_MULTITHREADFINDREP       603

#define ID_FINDLEFT                 604
#define ID_FINDRIGHT                605
#define ID_DEFSTRINGS               101


FONT.C

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

  File: font.c

  Purpose:

    Contains all functions pertinent to CDTEST's Font dialog.

  Functions:

     DoFontDlg()          -- Creates CDTEST's font dialog.

     FontProc()           -- Callback for CDTEST's font dialog.

     InitFontStruct()     -- Fills a CHOOSEFONT structure with some defaults.

     FillFontDlg()        -- Fills CDTEST's font dialog with the contents
                             of a CHOOSEFONT structure.

     GetFontDlg()         -- Retrieves the contents of CDTEST's font dialog
                             and fills a CHOOSEFONT structure with them.

     GetHdc()             -- Creates and returns an HDC

     LogFontProc()        -- Callback function for the logfont dlg.

     InitLogFontStruct()  -- Fills a LOGFONT structure with some defaults.

     FillLogFontDlg()     -- Fills the LOGFONT dlg with the values in
                             a LOGFONT structure.

     GetLogFontDlg()      -- Retrieves the user's edits in the LogFont dlg
                             and puts them in a LOGFONT structure.

     FontHookProc()       -- Callback function for CHOOSEFONT->lpfnHook

     GetFontDlgHandle()   -- Creates a handle to the custom template and
                             returns a handle to it.

     EnumFontsProc()      -- Callback function for EnumFontFamilies()

     FillFontsBox()       -- Fills CDTEST's font list box with requested fonts

     ResetCheckBoxes()    -- handles the font choices check box manipulations

     HandleFontCheckBox() -- Handles the WM_COMMAND messages from the font
                             choices check boxes.

     DoChooseFontStuff()  -- Calls the ChooseFont() function.

     FontThreadProc1()    -- Starting address for the first thread.

     FontThreadProc2()    -- Starting address for the second thread.

     MultiThreadFontDlg() -- Creates two threads which each create a
                             ChooseFont() dialog.

     FontEnableButtons()  -- Enables or disables CDTEST's font dialog buttons.
                             Necessary for multithreading part of this app.

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

#include <windows.h>
#include <commdlg.h>
#include <stdlib.h>
#include <winnls.h>
#include "cdtest.h"
#include "font.h"
#include "logfont.h"

/* some definitions that will help us tell the difference between
   screen and printer fonts */

#define FONT_TYPE_WYSIWYG 1
BOOL bScreen ;

/* function prototypes and general variables */

void InitFontStruct(HWND, LPCHOOSEFONT) ;
void FillFontDlg(HWND, LPCHOOSEFONT) ;
void GetFontDlg(HWND, LPCHOOSEFONT) ;
HDC  GetHdc(HWND, int) ;
void InitLogFontStruct(HWND, LPLOGFONT) ;
void FillLogFontDlg(HWND, LPLOGFONT) ;
void GetLogFontDlg(HWND, LPLOGFONT) ;
BOOL APIENTRY LogFontProc(HWND, UINT, UINT, LONG) ;
UINT APIENTRY FontHookProc(HWND, UINT, UINT, LONG) ;
int  CALLBACK EnumFontsProc(LPLOGFONT, LPTEXTMETRIC, DWORD, LONG) ;
void FillFontsBox(HWND, DWORD) ;
void ResetCheckBoxes(HWND) ;
void HandleFontCheckBox(HWND, int) ;
void DoChooseFontStuff(HWND, LPCHOOSEFONT) ;

/* Global variables and some external variables and functions */

DWORD dwFontFlag ;
PRINTDLG pfd ;
BOOL bLogFontParam ;
LOGFONT lfWM_CF_LF ;                 //for the WM_CHOOSEFONT_GETLOGFONT message
extern UINT uMode ;
extern LONG MyAtol(LPTSTR, BOOL, LPBOOL) ;
HANDLE hResFont, hDialogFont ;
HANDLE GetFontDlgHandle(void) ;
CHOOSEFONT cf ;
LOGFONT lf ;
HDC hdc ;
TCHAR szFaceName[50] ;
TCHAR szFontTemplate[50] ;
TCHAR szFontStyle[50] ;
#define HDCSCREEN 1
#define HDCPRINTER 2
#define HDCNULL 3
#define HDCINVALID 4
INT nHdcType ;

/* Multithreading stuff */

HANDLE hFontThread1 ;
HANDLE hFontThread2 ;
DWORD  dwFontThreadID1 ;
DWORD  dwFontThreadID2 ;
DWORD  dwFontThreadParm1 ;
DWORD  dwFontThreadParm2 ;
DWORD  FontThreadProc1(LPDWORD) ;
DWORD  FontThreadProc2(LPDWORD) ;
HANDLE hwndMainFont ;
int    nOpenFontDialogCount ;
CHOOSEFONT cfThread1 ;
CHOOSEFONT cfThread2 ;
void MultiThreadFontDlg(void) ;
void EnableFontButtons(HWND, BOOL) ;

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

  Function: DoFontDlg(HWND)

  Purpose:

    To create the CDTEST's font dialog

  Returns: Nothing.

  Comments:

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

void DoFontDialog(HWND hwnd)
{

  DialogBox(hInst, MAKEINTRESOURCE(ID_FONTDIALOG),
            hwnd, FontProc) ;

}

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

  Function: FontProc(HWND, UINT, UINT, LONG)

  Purpose:

    Callback function for CDTEST's font dialog.

  Returns: TRUE or FALSE depending on the situation.

  Comments:

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

BOOL APIENTRY FontProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  BOOL b ;
  TCHAR szNum[30] ;

  switch (msg)
  {
    case WM_INITDIALOG:

        InitLogFontStruct(hwnd, &lf) ;   //fill (struct lf) so the choosefont
        InitFontStruct(hwnd, &cf) ;      //struct can use it
        FillFontDlg(hwnd, &cf) ;

        CheckRadioButton(hwnd, ID_HDCSCREEN, ID_HDCPRINTER, ID_HDCSCREEN) ;
        nHdcType = HDCSCREEN ;

        dwFontFlag = cf.Flags ;

        FillFontsBox(hwnd, dwFontFlag) ;

        ResetCheckBoxes(hwnd) ;

        *(&cfThread1) = *(&cfThread2) = *(&cf) ;

        hwndMainFont = hwnd ;
        nOpenFontDialogCount = 0 ;

        SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEF)) ;

        break ;

    case UMSG_DECREMENTDLGCOUNT:  // user defined message.  This is send by
                                  // the functions that are executing when
                                  // a new thread is created.  When these
                                  // Thread functions end, they should send
                                  // this message.

      nOpenFontDialogCount-- ;

      if (nOpenFontDialogCount == 0)
        EnableFontButtons(hwnd, TRUE) ;

      break ;

    case WM_CF_LF:

       /* If this message comes in, we know that the user clicked the
          button that tells the ChooseFont() dialog to tell the parent
          to send the WM_CHOOSEFONT_GETLOGFONT message.  So what we will
          do is send the message and then show the user what was in the
          logfont by calling creating the Logfont dialog box */

       SendMessage((HWND) lParam, WM_CHOOSEFONT_GETLOGFONT,
                   0, (LPARAM) &lfWM_CF_LF) ;

       bLogFontParam = TRUE ; //tells us if we are doing normal logfont
                              //processing or answering the WM_CF_LF message.

       DialogBoxParam(hInst, MAKEINTRESOURCE(ID_LOGFONTDIALOG),
                      (HWND) lParam, LogFontProc, (LPARAM) &lf) ;

       bLogFontParam = FALSE ;

       break ;

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
          case IDOK:
            DoChooseFontStuff(hwnd, &cf) ;  //create the dialog.
            break ;

          case IDCANCEL:
            EndDialog(hwnd, FALSE) ;
            break ;

          case ID_RESETFONT:

            InitLogFontStruct(hwnd, &lf) ;  //reset all the structures.
            InitFontStruct(hwnd, &cf) ;
            FillFontDlg(hwnd, &cf) ;        //refill the dialog.

            CheckRadioButton(hwnd, ID_HDCSCREEN, ID_HDCPRINTER, ID_HDCSCREEN) ;
            nHdcType = HDCSCREEN ;

            SendDlgItemMessage(hwnd, ID_NULLSTRUCTFONT, BM_SETCHECK, (WPARAM) 0, (LPARAM)0) ;
            SendDlgItemMessage(hwnd, ID_PRELOADEDFONT,  BM_SETCHECK, (WPARAM) 0, (LPARAM)0) ;

            dwFontFlag = cf.Flags ;
            FillFontsBox(hwnd, dwFontFlag) ;
            ResetCheckBoxes(hwnd) ;

            SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEF)) ;

            break ;

          case ID_HDCSCREEN:
            nHdcType = HDCSCREEN ;
            break ;

          case ID_HDCPRINTER:
            nHdcType = HDCPRINTER ;
            break ;

          case ID_HDCNULL:
            nHdcType = HDCNULL ;
            break ;

          case ID_HDCINVALID:
            nHdcType = HDCINVALID ;
            break ;

          case ID_EDITLOGFONT:

            /* Get the address of the logfont and then show the user what
               is in it with the Logfont dialog box */

            GetDlgItemText(hwnd, ID_LOGFONTF, szNum, 30) ;
            cf.lpLogFont = (LPLOGFONT) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

            DialogBox(hInst, MAKEINTRESOURCE(ID_LOGFONTDIALOG),
                      hwnd, LogFontProc) ;
            break ;

          case F_TTONLY:
          case F_ANSIONLY:
          case F_PRINTERFONTS:
          case F_SCREENFONTS:
          case F_FIXEDPITCHONLY:
          case F_NOOEMFONTS:
          case F_NOVECTORFONTS:
          case F_SCALABLEONLY:
          case F_WYSIWYG:
            HandleFontCheckBox(hwnd, LOWORD(wParam)) ;
            break ;

          case ID_MULTITHREADFONT:

            /* Set the dialog count to 2, disable the buttons in CDTEST's font
               dialog so the user can't do anything until last dialog has
               been terminated */

            nOpenFontDialogCount = 2 ;

            EnableFontButtons(hwndMainFont, FALSE) ;

            /* And then do the multithreading */

            MultiThreadFontDlg() ;

            break ;

          default: break ;
        }
    }

    default:

      /*
         If the help button is pressed in the ChooseFont()
         dialog, it will send a message Registered with RegisterWindowMessage()
         to the parent window.  The message nHelpMessage was registered
         at application startup

         It must be detected with an IF statement because the value
         returned by RegisterWindowMessage() is not a constant
      */

      if (msg == nHelpMessage)
        MessageBox(GetForegroundWindow(), TEXT("Hello from the help button"),
                   TEXT("Font Help Button"), MB_OK | MB_APPLMODAL) ;
      break ;
  }

  return FALSE ;
}

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

  Function: InitFontStruct(HWND, LPCHOOSEFONT)

  Purpose:

    Fill the CHOOSEFONT structure passed as the second parameter with
    some default values.

  Returns: Nothing.

  Comments:

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

void InitFontStruct(HWND hwnd, LPCHOOSEFONT pcf)
{
  pcf->lStructSize = sizeof(CHOOSEFONT) ;
  pcf->hwndOwner = hwnd ;
  pcf->hDC = (HDC) 0 ;
  pcf->lpLogFont = &lf ;
  pcf->iPointSize = 24 ;
  pcf->Flags = CF_EFFECTS | CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_SHOWHELP | CF_APPLY ;
  pcf->rgbColors = RGB(0, 255, 0) ;
  pcf->lCustData = 0L ;
  pcf->lpfnHook = FontHookProc ;

  lstrcpy(szFontTemplate, TEXT("fonttemp")) ;
  pcf->lpTemplateName = szFontTemplate ;

  pcf->hInstance = (HANDLE) hInst ;

  lstrcpy(szFontStyle, TEXT("Bold")) ;
  pcf->lpszStyle = szFontStyle ;

  pcf->nFontType = SCREEN_FONTTYPE ;

  pcf->nSizeMin = 8 ;
  pcf->nSizeMax = 50 ;
}

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

  Function: FillFontDlg(HWND, LPCHOOSEFONT)

  Purpose:

    Fill CDTEST's font dialog with the values in the CHOOSEFONT structure
    passed via the second parameter.

  Returns: Nothing.

  Comments:

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

void FillFontDlg(HWND hwnd, LPCHOOSEFONT pcf)
{
  wsprintf(szTemp, szLongFilter, pcf->lStructSize) ;
  SetDlgItemText(hwnd, ID_STRUCTSIZEF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->hwndOwner) ;
  SetDlgItemText(hwnd, ID_HWNDOWNERF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->hDC) ;
  SetDlgItemText(hwnd, ID_HDCF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->hDC) ;
  SetDlgItemText(hwnd, ID_HDCF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->lpLogFont) ;
  SetDlgItemText(hwnd, ID_LOGFONTF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->iPointSize) ;
  SetDlgItemText(hwnd, ID_POINTSIZEF, szTemp) ;

  wsprintf(szTemp, szLongFilter, pcf->Flags) ;
  SetDlgItemText(hwnd, ID_FLAGSF, szTemp) ;

  wsprintf(szTemp, szLongFilter, pcf->rgbColors) ;
  SetDlgItemText(hwnd, ID_RGBCOLORSF, szTemp) ;

  wsprintf(szTemp, szLongFilter, pcf->lCustData) ;
  SetDlgItemText(hwnd, ID_CUSTDATAF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->lpfnHook) ;
  SetDlgItemText(hwnd, ID_HOOKF, szTemp) ;

  SetDlgItemText(hwnd, ID_TEMPLATEF, pcf->lpTemplateName) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->hInstance) ;
  SetDlgItemText(hwnd, ID_HINSTANCEF, szTemp) ;

  SetDlgItemText(hwnd, ID_STYLEF, pcf->lpszStyle) ;

  wsprintf(szTemp, szLongFilter, (int) pcf->nFontType) ;
  SetDlgItemText(hwnd, ID_FONTTYPEF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (int) pcf->nSizeMin) ;
  SetDlgItemText(hwnd, ID_SIZEMINF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (int) pcf->nSizeMax) ;
  SetDlgItemText(hwnd, ID_SIZEMAXF, szTemp) ;

}

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

  Function: GetFontDlg(HWND, LPCHOOSEFONT)

  Purpose:

    Retrieve the users edits from CDTEST's font dialog and put them
    in the CHOOSEFONT structure passed in as the second parameter

  Returns: Nothing.

  Comments:

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

void GetFontDlg(HWND hwnd, LPCHOOSEFONT pcf)
{
  BOOL b ;
  TCHAR szNum[30] ;

#define WSIZEFO 30

  GetDlgItemText(hwnd, ID_STRUCTSIZEF, szNum, WSIZEFO) ;
  pcf->lStructSize = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HWNDOWNERF, szNum, WSIZEFO) ;
  pcf->hwndOwner = (HWND) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LOGFONTF, szNum, WSIZEFO) ;
  pcf->lpLogFont = (LPLOGFONT) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_POINTSIZEF, szNum, WSIZEFO) ;
  pcf->iPointSize = (int) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_FLAGSF, szNum, WSIZEFO) ;
  pcf->Flags = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_RGBCOLORSF, szNum, WSIZEFO) ;
  pcf->rgbColors = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_CUSTDATAF, szNum, WSIZEFO) ;
  pcf->lCustData = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HOOKF, szNum, WSIZEFO) ;
  pcf->lpfnHook = (LPCFHOOKPROC) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_TEMPLATEF, szFontTemplate, 50) ;

  GetDlgItemText(hwnd, ID_HINSTANCEF, szNum, WSIZEFO) ;
  pcf->hInstance = (HANDLE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_STYLEF, szFontStyle, 50) ;

  GetDlgItemText(hwnd, ID_FONTTYPEF, szNum, WSIZEFO) ;
  pcf->nFontType = (int) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_SIZEMINF, szNum, WSIZEFO) ;
  pcf->nSizeMin = (int) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_SIZEMAXF, szNum, WSIZEFO) ;
  pcf->nSizeMax = (int) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;
}

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

  Function: GetHdc(HWND, int)

  Purpose:

    Creates and returns an HDC of the type specified.

  Returns: The HDC that it creates.

  Comments:

    The NULL and INVALID HDCs are only useful to test if the common dialogs
    handle an HDC that is not useful, but this function is also necessary
    in that the HDC must be a printer HDC if ChooseFont() is called
    with the CF_PRINTERFONTS flag bit set.

    To tell ChooseFont() to list only printer fonts:

    1.  Set the HDC in CDTEST's font dialog to "Printer"

    2.  Set the "Flags" edit box CF_PRINTERFONTS

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

HDC GetHdc(HWND hwnd, int nType)
{
  switch (nType)
  {
    case HDCSCREEN:
        return GetDC(hwnd) ;
        break ;

    case HDCNULL:
        return (HDC) 0 ;
        break ;

    case HDCINVALID:
        return (HDC) 999 ;
        break ;

    case HDCPRINTER:

        /* To get the HDC of the current printer, fill out a PRINTDLG
           structure, and set it's flags to (PD_RETURNDC | PD_RETURNDEFAULT).
           This will tell PrintDlg() to create you an HDC but not show
           the Print dialog box. */

        pfd.lStructSize = sizeof(PRINTDLG) ;
        pfd.hwndOwner = hwnd ;
        pfd.hDevMode = (HANDLE) 0 ;
        pfd.hDevNames = (HANDLE) 0 ;
        pfd.Flags = PD_RETURNDC | PD_RETURNDEFAULT ;   //just get default printer
        pfd.nFromPage = 0 ;
        pfd.nToPage = 0 ;
        pfd.nMinPage = 0 ;
        pfd.nMaxPage = 0 ;
        pfd.nCopies = 0 ;
        pfd.hInstance = (HANDLE) hInst ;

        if (PrintDlg(&pfd) == 0)
            MessageBox(hwnd, TEXT("Error: Could not create a printer DC!"),
                       (LPTSTR) NULL, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;
        else
           return pfd.hDC ;

        break ;

    default:
        break ;
  }
  return (HDC) 0 ;
}

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

  Function: LogFontProc(HWND, int)

  Purpose:

    The callback proc for the LogFont dialog box

  Returns: BOOL -- depending on the situation.

  Comments:

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

BOOL APIENTRY LogFontProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  switch (msg)
  {
    case WM_INITDIALOG:
        FillLogFontDlg(hwnd, cf.lpLogFont) ;  //start off with the one that
                                              //is returned from ChooseFont()
        break ;

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
          case IDOK:

            GetLogFontDlg(hwnd, &lf) ;

            cf.lpLogFont = &lf ;

            EndDialog(hwnd, TRUE) ;

            break ;

          case ID_RESETLF:

            SetFocus(GetDlgItem(hwnd, ID_LFHEIGHT)) ;

            InitLogFontStruct(hwnd, &lf) ;

            FillLogFontDlg(hwnd, &lf) ;

            break ;

          case IDCANCEL:

            EndDialog(hwnd, FALSE) ;

            break ;

          default:
            break ;
        }

    default: break ;
  }

  return FALSE ;
}

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

  Function: InitLogFontStruct(HWND, LPLOGFONT)

  Purpose:

    Fills the LOGFONT structure passed in as the second parameter with
    some default values.

  Returns: Nothing.

  Comments:

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

void InitLogFontStruct(HWND hwnd, LPLOGFONT plf)
{
  plf->lfHeight = 24 ;
  plf->lfWidth  = 20 ;
  plf->lfEscapement = 0 ;
  plf->lfOrientation = 10 ;
  plf->lfWeight = 400 ;
  plf->lfItalic = FALSE ;
  plf->lfUnderline = FALSE ;
  plf->lfStrikeOut = FALSE ;
  plf->lfCharSet = ANSI_CHARSET ;
  plf->lfOutPrecision = OUT_DEFAULT_PRECIS ;
  plf->lfClipPrecision = CLIP_DEFAULT_PRECIS ;
  plf->lfQuality = DEFAULT_QUALITY ;
  plf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE ;

  szFaceName[0] = (TCHAR) 0 ;
  lstrcpy(plf->lfFaceName, szFaceName) ;
}

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

  Function: FillLogFontDlg(HWND, LPLOGFONT)

  Purpose:

    Fills CDTEST's logfont dialog with the values in the logfont that
    is passed in as the second parameter.

  Returns: Nothing.

  Comments:

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

void FillLogFontDlg(HWND hwnd, LPLOGFONT plf)
{
  TCHAR szTemp[50] ;

  LPLOGFONT pl = plf ;

  if (bLogFontParam)    //Are creating this dialog in response to the
    pl = &lfWM_CF_LF ;  //WM_CHOOSEFONT_GETLOGFONT message ?

  if (!pl)              //Avert a ghastly exception error
    return ;

  wsprintf(szTemp, szLongFilter, pl->lfHeight) ;
  SetDlgItemText(hwnd, ID_LFHEIGHT, szTemp) ;

  wsprintf(szTemp, szLongFilter, pl->lfWidth) ;
  SetDlgItemText(hwnd, ID_LFWIDTH, szTemp) ;

  wsprintf(szTemp, szLongFilter, pl->lfEscapement) ;
  SetDlgItemText(hwnd, ID_LFESCAPEMENT, szTemp) ;

  wsprintf(szTemp, szLongFilter, pl->lfOrientation) ;
  SetDlgItemText(hwnd, ID_LFORIENTATION, szTemp) ;

  wsprintf(szTemp, szLongFilter, pl->lfWeight) ;
  SetDlgItemText(hwnd, ID_LFWEIGHT, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfItalic) ;
  SetDlgItemText(hwnd, ID_LFITALIC, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfUnderline) ;
  SetDlgItemText(hwnd, ID_LFUNDERLINE, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfStrikeOut) ;
  SetDlgItemText(hwnd, ID_LFSTRIKEOUT, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfCharSet) ;
  SetDlgItemText(hwnd, ID_LFCHARSET, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfOutPrecision) ;
  SetDlgItemText(hwnd, ID_LFOUTP, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfClipPrecision) ;
  SetDlgItemText(hwnd, ID_LFCLIPP, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfQuality) ;
  SetDlgItemText(hwnd, ID_LFQUALITY, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfPitchAndFamily) ;
  SetDlgItemText(hwnd, ID_LFPITCHANDFAM, szTemp) ;

  SetDlgItemText(hwnd, ID_LFFACENAME, (LPTSTR) pl->lfFaceName) ;
}

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

  Function: GetLogFontDlg(HWND, LPLOGFONT)

  Purpose:

    Retrieves the users edits in CDTEST's Logfont dialog and puts them
    into the LOGFONT struct that is passed as the second parameter.

  Returns: Nothing.

  Comments:

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

void GetLogFontDlg(HWND hwnd, LPLOGFONT plf)
{
  BOOL b ;
  TCHAR szNum[30] ;

  if (bLogFontParam)  //if we are just viewing the logfont because we sent a
    return ;          //WM_CHOOSEFONT_GETLOGFONT message, don't change the Logfont Struct
                      //that ChooseFont() gives us...

  #define LFSIZE 30

  GetDlgItemText(hwnd, ID_LFHEIGHT, szNum, LFSIZE) ;
  plf->lfHeight = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFWIDTH, szNum, LFSIZE) ;
  plf->lfWidth = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFESCAPEMENT, szNum, LFSIZE) ;
  plf->lfEscapement = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFORIENTATION, szNum, LFSIZE) ;
  plf->lfOrientation = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFWEIGHT, szNum, LFSIZE) ;
  plf->lfWeight = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFITALIC, szNum, LFSIZE) ;
  plf->lfItalic = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFUNDERLINE, szNum, LFSIZE) ;
  plf->lfUnderline = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFSTRIKEOUT, szNum, LFSIZE) ;
  plf->lfStrikeOut = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFCHARSET, szNum, LFSIZE) ;
  plf->lfCharSet = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFOUTP, szNum, LFSIZE) ;
  plf->lfOutPrecision = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFCLIPP, szNum, LFSIZE) ;
  plf->lfClipPrecision = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFQUALITY, szNum, LFSIZE) ;
  plf->lfQuality = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFPITCHANDFAM, szNum, LFSIZE) ;
  plf->lfPitchAndFamily = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFFACENAME, (LPTSTR) plf->lfFaceName, 32) ;
}

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

  Function: FontHookProc(HWND, UINT, UINT, LONG)

  Purpose: The callback function that acts as the hook proc for
           the ChooseFont() dialog.

  Returns: TRUE to discard the message.  FALSE to send the message on
           to the normal dialog processing done by the ChooseFont()
           function.

  Comments:

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

UINT APIENTRY FontHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  LPCHOOSEFONT pCf ;
  TCHAR szMsg[50] ;

  switch(msg)
  {
    case WM_INITDIALOG:

      pCf = (LPCHOOSEFONT) lParam ;

      if (pCf->lCustData != 0L)
      {
        wsprintf(szMsg, TEXT("CHOOSEFONT->lCustData is: %ld"), pCf->lCustData) ;
        MessageBox(hwnd, szMsg, TEXT("lCustData Sent!"), MB_OK) ;
      }

      SetWindowText(hwnd, TEXT("Font Hook Proc Dialog")) ;

      break ;

    case WM_COMMAND:

        /* NOTE: This button will only be available if the user creates the
           ChooseFont() dialog with the CF_ENABLETEMPLATE or CF_ENABLETEMPLATEHANDLE
           flag.

           This button will only work if the ChooseFont() dialog is created
           with the CF_ENABLEHOOK flag.  So, to get full functionality from
           this button:

           1.  Enter (CF_ENABLEHOOK | CF_ENABLETEMPLATE | CF_SCREENFONTS)
               in the "Flags" edit box of CDTEST'S Font dialog.

           2.  Click OK
        */

        /* The parent will send the WM_CHOOSEFONT_GETLOGFONT message and
           display it's findings in a LogFont dialog if it gets this message */

        if (LOWORD(wParam) == ID_SEND_WM_CF_LF_MSG)
          SendMessage(GetParent(hwnd), WM_CF_LF, 0, (LPARAM) hwnd) ;

        break ;

    default:
      break ;
  }

  return FALSE ;   //send msg to the common dialog code
}

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

  Function: GetFontDlgHandle(void)

  Purpose:  Finds and loads the custom template resource and returns a
            handle to it.

  Returns: A handle to the custom template resource.

  Comments:

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

HANDLE GetFontDlgHandle(void)
{

  hResFont = FindResource(hInst, TEXT("fonttemp"), RT_DIALOG) ;

  hDialogFont = LoadResource(hInst, hResFont) ;

  return hDialogFont ;
}

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

  Function: EnumFontsProc(LPLOGFONT, LPTEXTMETRIC, DWORD, LONG)

  Purpose: Acts as a callback function for the EnumFontFamilies()
           function.

  Returns: 0 if the font enumeration should stop.  1 to ask for the
           next font.

  Comments:

    EnumFontFamilies will find the fonts on the system and call this
    function each time it finds a font.  Pointers to the LOGFONT and
    TEXTMETRIC structures the describe the font are passed as the first
    and second arguments, so we can determine if the fonts we get
    meet the specs we want.  If they do, we can add them to the font
    list box in CDTEST's Font dialog.

    NOTE:

    None of this affects the performance of ChooseFont().  This serves
    only to illustrate how ChooseFont() finds the fonts you request based
    on the flags you pass to the ChooseFont() function.

    The fonts found with this function should be the same as the ones
    found by ChooseFont() for any flag combination.

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

int CALLBACK EnumFontsProc(LPLOGFONT lplf, LPTEXTMETRIC lptm,
                           DWORD dwStyle, LONG lParam)
{
  UINT i ;

  /* check to see if the font is already there right off the bat
     and just continue the enumeration if it is...        */

  i = SendDlgItemMessage((HWND) lParam, ID_FONTLIST, LB_FINDSTRINGEXACT, (WPARAM) 0,
                         (LONG) (LPTSTR) lplf->lfFaceName) ;

  if (i != LB_ERR)
  {
    if (bScreen)     //if we are adding it for the first time
      return 1 ;

    //else, we are adding printer fonts, so if we find one that is already there

//it must be available for both the printer and the screen...

    else
    {
      SendDlgItemMessage((HWND) lParam, ID_FONTLIST, LB_SETITEMDATA, i,
                          FONT_TYPE_WYSIWYG) ;
      return 1 ;
    }
  }

  //Get rid of any fonts that we don't want...

  if ((dwFontFlag & CF_TTONLY) && (!(dwStyle & TRUETYPE_FONTTYPE)))
    return 1 ;

  if ((dwFontFlag & CF_SCALABLEONLY) && (dwStyle & RASTER_FONTTYPE))
    return 1 ;

  if ((dwFontFlag & CF_ANSIONLY) && (lplf->lfCharSet != ANSI_CHARSET))
    return 1 ;

  if ((dwFontFlag & CF_FIXEDPITCHONLY) && (lplf->lfPitchAndFamily & VARIABLE_PITCH))
    return 1 ;

  if ((dwFontFlag & CF_NOVECTORFONTS) && (lplf->lfCharSet == OEM_CHARSET))
    return 1 ;

  //if there is a font to be added, add it.

  if (*(lplf->lfFaceName))
    SendDlgItemMessage((HWND) lParam, ID_FONTLIST, LB_ADDSTRING,
                     (WPARAM) 0, (LONG) (LPTSTR) lplf->lfFaceName) ;

  return 1 ;
}

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

  Function: FillFontsBox(HWND, DWORD)

  Purpose: Enumerates all fonts on the system and sends the ones that
           meet the criteria of the dwFlags parameter (CF_SCREENFONTS,
           CF_SCALABLEONLY, etc).

  Returns: Nothing.

  Comments:

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

void FillFontsBox(HWND hwnd, DWORD dwFlags)
{
  HDC hdc ;
  HWND hwndControl ;
  DWORD dwData ;
  int nItemCount ;

  /* Empty the list box, and turn of the redraw because we may need
     to remove some fonts after they are added if the user calls this
     function with the CF_WYSIWYG flag bit set. */

  SendDlgItemMessage(hwnd, ID_FONTLIST, LB_RESETCONTENT, (WPARAM)0, (LPARAM) 0) ;

  hwndControl = GetDlgItem(hwnd, ID_FONTLIST) ;

  SendMessage(hwndControl, WM_SETREDRAW, FALSE, 0L) ;

  /* Now call EnumFontFamilies() for each type of HDC requested */

  if (dwFlags & CF_SCREENFONTS)
  {
    bScreen = TRUE ;
    hdc = GetHdc(hwnd, HDCSCREEN) ;
    EnumFontFamilies(hdc, (LPTSTR) NULL, (FONTENUMPROC) EnumFontsProc, (LONG) hwnd) ;
    ReleaseDC(hwnd, hdc) ;
  }

  if (dwFlags & CF_PRINTERFONTS)
  {
    bScreen = FALSE ;
    hdc = GetHdc(hwnd, HDCPRINTER) ;
    EnumFontFamilies(hdc, (LPTSTR) NULL, (FONTENUMPROC) EnumFontsProc, (LONG) hwnd) ;
    ReleaseDC(hwnd, hdc) ;
  }

  /* Special case:  If the CF_WYSIWYG flag is used, we have to enumerate
     the fonts in both HDCs and then remove all the ones that are not
     both printer fonts and screen fonts. */

  if (dwFontFlag & CF_WYSIWYG)
  {
    nItemCount = SendDlgItemMessage(hwnd, ID_FONTLIST, LB_GETCOUNT, 0, 0L) ;

    nItemCount-- ;   //the list is zero based

    while (nItemCount >= 0)
    {
      dwData = SendDlgItemMessage(hwnd, ID_FONTLIST, LB_GETITEMDATA, nItemCount, 0L) ;

      if (dwData != FONT_TYPE_WYSIWYG)
      {
        SendDlgItemMessage(hwnd, ID_FONTLIST, LB_DELETESTRING, nItemCount, 0L) ;
      }

      nItemCount-- ;
    }
  }

  /* Now redraw the font list */

  SendMessage(hwndControl, WM_SETREDRAW, TRUE, 0L) ;
  InvalidateRect(hwndControl, NULL, FALSE) ;

  return ;
}

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

  Function: ResetCheckBoxes(HWND)

  Purpose: Checks the checkboxes that control what fonts are listed in
           CDTEST's font list box.

  Returns: Nothing.

  Comments:

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

void ResetCheckBoxes(HWND hwnd)
{

  SendDlgItemMessage(hwnd, F_TTONLY,          BM_SETCHECK, dwFontFlag & CF_TTONLY         ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_ANSIONLY,        BM_SETCHECK, dwFontFlag & CF_ANSIONLY       ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_SCREENFONTS,     BM_SETCHECK, dwFontFlag & CF_SCREENFONTS    ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_PRINTERFONTS,    BM_SETCHECK, dwFontFlag & CF_PRINTERFONTS   ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_NOOEMFONTS,      BM_SETCHECK, dwFontFlag & CF_NOOEMFONTS     ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_NOVECTORFONTS,   BM_SETCHECK, dwFontFlag & CF_NOVECTORFONTS  ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_SCALABLEONLY,    BM_SETCHECK, dwFontFlag & CF_SCALABLEONLY   ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_WYSIWYG,         BM_SETCHECK, dwFontFlag & CF_WYSIWYG        ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_FIXEDPITCHONLY,  BM_SETCHECK, dwFontFlag & CF_FIXEDPITCHONLY ? 1 : 0, (LPARAM)0) ;

}

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

  Function: HandleFontCheckBox(HWND)

  Purpose: If the user clicks one of the fonts in the CDTEST's list of
           valid font types, this function will check the correct boxes
           and set the correct values in the flag which keeps track of
           which fonts are to be enumerated and added to CDTEST's font
           list box.

  Returns: Nothing.

  Comments:

    CF_NOOEMFONTS and CF_NOVECTORFONTS are identical.

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

void HandleFontCheckBox(HWND hwnd, int nID)
{
  DWORD dwTemp ;

  dwTemp = dwFontFlag ;

  switch (nID)
  {
    case F_TTONLY:

      /* TTONLY is a special case.  If we only want Truetype fonts,
         turn off irrelevant flags */

      if (!(dwFontFlag & CF_TTONLY)) //if we're turning this flag on...
      {
        dwFontFlag = 0L ;  //turn everyone off but the following:

        dwFontFlag |= CF_TTONLY ;

        if (dwTemp & CF_ANSIONLY)
          dwFontFlag |= CF_ANSIONLY ;

        if (dwTemp & CF_FIXEDPITCHONLY)
          dwFontFlag |= CF_FIXEDPITCHONLY ;

        if (dwTemp & CF_SCREENFONTS)
          dwFontFlag |= CF_SCREENFONTS ;

        if (dwTemp & CF_PRINTERFONTS)
          dwFontFlag |= CF_PRINTERFONTS ;
      }
      else
      {
        dwFontFlag ^= CF_TTONLY ;
      }

      break ;

    case F_ANSIONLY:
      dwFontFlag ^= CF_ANSIONLY ;        //otherwise toggle the bit.
      dwFontFlag &= ~CF_WYSIWYG ;

      break ;

    case F_PRINTERFONTS:
      dwFontFlag ^= CF_PRINTERFONTS ;

      break ;

    case F_SCREENFONTS:
      dwFontFlag ^= CF_SCREENFONTS ;

      break ;

    case F_FIXEDPITCHONLY:
      dwFontFlag ^= CF_FIXEDPITCHONLY ;

      break ;

    case F_NOOEMFONTS:
      dwFontFlag ^= CF_NOOEMFONTS ;

      break ;

    case F_NOVECTORFONTS:
      dwFontFlag ^= CF_NOVECTORFONTS ;

      break ;

    case F_SCALABLEONLY:
      dwFontFlag ^= CF_SCALABLEONLY ;

      break ;

    case F_WYSIWYG:                        //We want fonts for both the
                                           //screen and the printer...
      dwFontFlag ^= CF_WYSIWYG ;

      if (dwFontFlag & CF_WYSIWYG)
        dwFontFlag |= (CF_SCREENFONTS | CF_PRINTERFONTS | CF_SCALABLEONLY) ;

      break ;

    default: break ;
  }

  ResetCheckBoxes(hwnd) ;
  FillFontsBox(hwnd, dwFontFlag) ;
}

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

  Function: DoChooseFontStuff(HWND, LPCHOOSEFONT)

  Purpose:

    Calls the ChooseFont() function.

  Returns: Nothing.

  Comments:

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

void DoChooseFontStuff(HWND hwnd, LPCHOOSEFONT pcf)
{
  BOOL bRet = FALSE ;

  if (IsDlgButtonChecked(hwnd, ID_PRELOADEDFONT) == 1)
  {
    pcf->hInstance = GetFontDlgHandle() ;
    wsprintf(szTemp, szLongFilter, pcf->hInstance) ;
    SetDlgItemText(hwnd, ID_HINSTANCEF, szTemp) ;
  }

  pcf->hDC = GetHdc(hwnd, nHdcType) ;

  wsprintf(szTemp, szLongFilter, pcf->hDC) ;
  SetDlgItemText(hwnd, ID_HDCF, szTemp) ;

  GetFontDlg(hwnd, pcf) ;

  if (IsDlgButtonChecked(hwnd, ID_NULLSTRUCTFONT) == 1)
    bRet = ChooseFont((LPCHOOSEFONT)NULL) ;
  else
    bRet = ChooseFont(pcf) ;

  wsprintf(szTemp, szLongFilter, CommDlgExtendedError()) ;
  SetDlgItemText(hwnd, ID_ERRORF, szTemp) ;

  if (pcf->hDC)
  {
    ReleaseDC(hwnd, pcf->hDC) ;   //free the HDC that we used if we called
    pcf->hDC = (HDC) 0 ;          //the function with a valid HDC.
  }

  if (hDialogFont)
  {
    FreeResource(hDialogFont) ;
    hDialogFont = (HANDLE) 0 ;
    hResFont = (HANDLE) 0 ;
  }

  wsprintf(szTemp, szShortFilter, bRet) ;     //fill results into the Font dlg
  SetDlgItemText(hwnd, ID_RETURNF, szTemp) ;

  FillFontDlg(hwnd, pcf) ;
}

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

  Function: FontThreadProc1(LPDWORD)

  Purpose:

    Acts as the starting address for thread 1

  Returns: Any DWORD value.

  Comments:

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

DWORD FontThreadProc1(LPDWORD pdw)
{
  DoChooseFontStuff(hwndMainFont, &cfThread1) ;

  PostMessage(hwndMainFont, UMSG_DECREMENTDLGCOUNT, 0, 0L ) ;

  return 0L ;
}

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

  Function: FontThreadProc2(LPDWORD)

  Purpose:

    Acts as the starting address for thread 2

  Returns: Any DWORD value.

  Comments:

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

DWORD FontThreadProc2(LPDWORD pdw)
{
  DoChooseFontStuff(hwndMainFont, &cfThread2) ;

  PostMessage(hwndMainFont, UMSG_DECREMENTDLGCOUNT, 0, 0L ) ;

  return 0L ;
}

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

  Function: MultiThreadFontDlg(void)

  Purpose:

    Create the two threads that will in turn create two ChooseFont()
    dialogs.

  Returns: Nothing.

  Comments:

    Multithreading note:

    This function will return before the common dialog functions return.
    Therefore, do not pass any parameters to this function that will be
    referenced by the common dialogs because as soon as this function
    ends those parameters will be gone.

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

void MultiThreadFontDlg(void)
{

  dwFontThreadParm1 = dwFontThreadParm2 = 0L ;

  if (!(hFontThread1 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                     (LPTHREAD_START_ROUTINE) FontThreadProc1,
                                     &dwFontThreadParm1, CREATE_SUSPENDED, &dwFontThreadID1)))

  {
    MessageBox(GetForegroundWindow(), TEXT("Error creating thread 1"), NULL,
               MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;

    nOpenFontDialogCount = 0 ;

    EnableFontButtons(hwndMainFont, TRUE) ;

    return ;
  }

  if (!(hFontThread2 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                     (LPTHREAD_START_ROUTINE) FontThreadProc2,
                                     &dwFontThreadParm2, CREATE_SUSPENDED, &dwFontThreadID2)))
  {
    MessageBox(GetForegroundWindow(), TEXT("Error creating thread 2"), NULL,
               MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;

    nOpenFontDialogCount = 0 ;

    EnableFontButtons(hwndMainFont, TRUE) ;

    return ;
  }

  ResumeThread(hFontThread1) ;
  ResumeThread(hFontThread2) ;

  return ;
}

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

  Function: EnableFontButtons(HWND, BOOL)

  Purpose:

    Enables or disables CDTEST's font dialogs buttons based on the
    status of the second parameter.

  Returns: Nothing.

  Comments:

    This is necessary when multithreading in the case of this application.

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

void EnableFontButtons(HWND hwnd, BOOL bEnable)
{
  EnableWindow(GetDlgItem(hwnd, IDOK), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, IDCANCEL), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_RESETFONT), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_MULTITHREADFONT), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_EDITLOGFONT), bEnable) ;
}


FONT.H

void DoFontDialog(HWND) ;
BOOL APIENTRY FontProc(HWND, UINT, UINT, LONG) ;

#define ID_FONTDIALOG               607
#define WM_CF_LF                    (WM_USER+2)

#define ID_STRUCTSIZEF              116
#define ID_HWNDOWNERF               117
#define ID_HDCF                     118
#define ID_LOGFONTF                 119
#define ID_POINTSIZEF               120
#define ID_FLAGSF                   121
#define ID_RGBCOLORSF               122
#define ID_CUSTDATAF                123
#define ID_HOOKF                    124
#define ID_TEMPLATEF                125
#define ID_HINSTANCEF               126
#define ID_STYLEF                   127
#define ID_FONTTYPEF                128
#define ID_SIZEMINF                 129
#define ID_SIZEMAXF                 130
#define ID_ERRORF                   135
#define ID_RETURNF                  136
#define ID_HDCSCREEN                137
#define ID_HDCPRINTER               138
#define ID_HDCNULL                  131
#define ID_EDITLOGFONT              132
#define ID_RESETFONT                139
#define ID_HDCINVALID               609
#define ID_NULLSTRUCTFONT           608
#define ID_PRELOADEDFONT            610
#define ID_FONTLIST                 611
#define F_ANSIONLY                  614
#define F_TTONLY                    615
#define F_SCREENFONTS               616
#define F_PRINTERFONTS              617
#define F_FIXEDPITCHONLY            618
#define F_NOOEMFONTS                619
#define F_NOSIMULATIONS             620
#define F_NOVECTORFONTS             621
#define F_SCALABLEONLY              622
#define F_WYSIWYG                   623
#define ID_MULTITHREADFONT          624
#define ID_SEND_WM_CF_LF_MSG        625


LOGFONT.H

#define ID_LFHEIGHT                 115
#define ID_LFWIDTH                  116
#define ID_LFESCAPEMENT             117
#define ID_LFORIENTATION            118
#define ID_LFWEIGHT                 119
#define ID_LFITALIC                 120
#define ID_LFUNDERLINE              121
#define ID_LFSTRIKEOUT              122
#define ID_LFCHARSET                123
#define ID_LFOUTP                   124
#define ID_LFCLIPP                  125
#define ID_LFQUALITY                126
#define ID_LFPITCHANDFAM            127
#define ID_LFFACENAME               128

#define ID_LOGFONTDIALOG            608
#define ID_RESETLF                  129


OPEN.C

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

  File: open.c

  Purpose:

    This file contains the routines to control the CDTEST.EXE "Open" dialog
    box.  The "Open" dialog box allows the user to enter values into an
    OPENFILENAME structure and then create GetOpenFileName() dialog boxes
    on the fly.

  Functions:

    DoOpenDialog()                   -- starts off the main dialog for "open"

    OpenFunc()                       -- Callback function for main dialog

    InitOpenStruct()                 -- Fills initial OPENFILENAME structure.

    FillOpenDlg()                    -- Fills the dialog with the values from
                                        the OPENFILENAME structure.

    GetOpenDlg()                     -- Retrieves the users entries from the
                                        main dialog and puts them in the
                                        OPENFILENAME structure.

    InitFilterString()               -- Creates filter string

    InitCustFiltString()             -- Creates custom filter string

    InterpretCustomFilterString()    -- Parses custom filter string returned
                                        from GetOpenFileName()

    GetCorrectResourceHandle()       -- Loads custom templates from file
                                        as resource handles

    OpenSaveHookProc()               -- The hook function that will be
                                        called if GetOpen/Save is called
                                        with the OFN_ENABLEHOOK flag set.

    MultiThreadOpenSave()            -- Creates two Open/Save dialogs that
                                        the user can simultaneously access

    OpenSaveThread1Proc()            -- The starting address of thread 1

    OpenSaveThread2Proc()            -- The starting address of thread 2

    DoOpenSaveStuff()                -- Does the actuall calling of
                                        GetOpen/SaveFileName()

    OpenMultiThreadEnableButtons()   -- Enables and disables buttons in
                                        main dialog.  Needed when multi-
                                        threading.

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

#include <windows.h>
#include <commdlg.h>
#include <stdlib.h>
#include <winnls.h>
#include "cdtest.h"
#include "open.h"
#include "save.h"
#include "dlgs.h"     //include file that contains all #defines for the
                      //commdlg dialog templates.

/* All functions defined in this file + 1 external function
   and one external variable */

extern UINT uMode ;                               //see cdtest.c
extern LONG MyAtol(LPTSTR, BOOL, LPBOOL) ;
void InterpretCustomFilterString(void) ;
HANDLE GetCorrectResourceHandle(void) ;
void DoOpenSaveStuff(LPOPENFILENAME) ;
UINT APIENTRY OpenSaveHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam) ;
DWORD OpenSaveThread1Proc(LPDWORD) ;
DWORD OpenSaveThread2Proc(LPDWORD) ;
void MultiThreadOpenSave(void) ;
void OpenMultiThreadEnableButtons(BOOL, HWND) ;

/* All global variables defined in this file */

HWND hwndMainDialog ;   //global handle for open dialog.

HANDLE hRes ;           //handles to the resource and dialog for
HANDLE hDialog ;        //ofn_enabletemplatehandle

HBRUSH hBrushDlg ;
HBRUSH hBrushEdit ;     //brush handles for new colors done with hook proc
HBRUSH hBrushButton ;

HANDLE hOpenSaveThread1, hOpenSaveThread2 ;   //variables for the
DWORD dwThreadID1, dwThreadID2 ;              //multithreading part
DWORD dwThreadParm1, dwThreadParm2 ;
OPENFILENAME ofnThread1, ofnThread2 ;
int nOpenDialogCount ;

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

  Function: DoOpenDialog(HWND)

  Purpose: To create the GetOpenFileName() and GetSaveFileName()
           creation dialog.

  Returns: Nothing.

  Comments:

    GetOpenFileName() and GetSaveFileName() are similiar enough so that
    the same dialog can be used to edit their creation structure elements,
    so a global variable "bDoOpenDlg" keeps track of which one to create
    when the user clicks the OK or Multithread buttons...

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

void DoOpenDialog(HWND hwnd)
{

  bDoOpenDlg = TRUE ;

  DialogBox(hInst, MAKEINTRESOURCE(ID_OPENDIALOG), hwnd, OpenFunc) ;

}

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

  Function: OpenFunc(HWND, UINT, UINT, LONG)

  Purpose:

    This is the callback function for the dialog box containing the
    GetOpenFileName() and the GetSaveFileName() creation options.

    This function will handle the messages for this dialog and create
    either a GetOpenFileName() dialog or a GetSaveFileName() dialog
    depending on the state of the bDoOpenDlg variable.

  Returns: TRUE or FALSE depending on the situation.

  Comments:

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

BOOL APIENTRY OpenFunc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  switch (msg)
  {

    case WM_INITDIALOG:

      if (bDoOpenDlg)
        SetWindowText(hwnd, TEXT("GetOpenFileName()")) ;
      else
        SetWindowText(hwnd, TEXT("GetSaveFileName()")) ;

      /* initialize the OPENFILENAME structure members */

      InitOpenStruct(hwnd, &ofn) ;

      /* Fill these values into the creation dialog */

      FillOpenDlg(hwnd, &ofn) ;

      /* There are three separate OPENFILENAME structures.  One for
         the main Open/Save dialog and one for each multithreaded dialog.
         Set them equal to begin with */

      *(&ofnThread1) = *(&ofnThread2) = *(&ofn) ;

      hwndMainDialog = hwnd ;

      SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEO)) ;

      break ;

    case UMSG_DECREMENTDLGCOUNT:  //user defined message indicating
                                  //the closure of a multithreaded dialog

      /* When we are multithreading, there is nothing to prevent the
         user from interacting with the creation dialog once the first
         GetOpen(Save) file name dialog has returned.  So, in order
         to prevent the Multithread" button from being pressed again
         before the previous two multithreaded dialogs have been canceled,
         disable the controls until we get a message from each thread
         that the dialog has ended */

      nOpenDialogCount-- ;

      if (nOpenDialogCount == 0)
        OpenMultiThreadEnableButtons(TRUE, hwnd) ;

      break ;

    case WM_COMMAND:
    {
      switch (LOWORD(wParam))
      {

        case IDOK:
          GetOpenDlg(hwnd, &ofn) ;          //get the user's input
          DoOpenSaveStuff(&ofn) ;           //do the dialog
          break ;

        case IDCANCEL:
          EndDialog(hwnd, FALSE) ;
          break ;

        case ID_RESETOPEN:

          SendDlgItemMessage(hwnd, ID_FILTERO, CB_RESETCONTENT,
            (WPARAM) 0, (LPARAM) 0) ;

          InitOpenStruct(hwnd, &ofn) ;

          FillOpenDlg(hwnd, &ofn) ;

          SendDlgItemMessage(hwnd, ID_NULLSTRUCTO, BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;
          SendDlgItemMessage(hwnd, ID_USEHINSTO, BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;

          *(&ofnThread1) = *(&ofnThread2) = *(&ofn) ;

          SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEO)) ;

          break ;

        case ID_ADD1O:

          GetDlgItemText(hwnd, ID_FILTERO, szTemp, 100) ;

          if (*szTemp)
          {
            SendDlgItemMessage(hwnd, ID_FILTERO, CB_ADDSTRING, (WPARAM) 0,
                              (LPARAM) (LPTSTR) szTemp) ;
            SetWindowText(GetDlgItem(hwnd, ID_FILTERO), TEXT("")) ;
          }
          break ;

        case ID_ADD2O:

          GetDlgItemText(hwnd, ID_CUSTFILTO, szTemp, 100) ;

          if (*szTemp)
          {
            SendDlgItemMessage(hwnd, ID_CUSTFILTO, CB_ADDSTRING, (WPARAM) 0,
                              (LPARAM) (LPTSTR) szTemp) ;
            SetWindowText(GetDlgItem(hwnd, ID_CUSTFILTO), TEXT("")) ;
          }
          break ;

        case ID_CLEAR1O:
          SendDlgItemMessage(hwnd, ID_FILTERO, CB_RESETCONTENT,
                             (WPARAM) 0, (LPARAM) 0) ;
          break ;

        case ID_CLEAR2O:
          SendDlgItemMessage(hwnd, ID_CUSTFILTO, CB_RESETCONTENT,
                             (WPARAM) 0, (LPARAM) 0) ;
          break ;

        case ID_MULTIOPEN:

        /* First, disable the OK, Cancel, and MultiThread buttons */

          OpenMultiThreadEnableButtons(FALSE, hwnd) ;

        /* Then multithread the dialogs */

          nOpenDialogCount = 2 ;

          MultiThreadOpenSave() ;

          break ;

        default:   //end WM_COMMAND case
          break ;
      }
    }

    default:

      /* If the help button is pressed in the GetOpen/SaveFileName()
         dialogs, it will send a message Registered with RegisterWindowMessage()
         to the parent window.  The message nHelpMessage was registered
         at application startup */

      if (msg == nHelpMessage)
        MessageBox(GetForegroundWindow(),
                   TEXT("Hello from the help button"),
                   TEXT("Open Help Button"), MB_OK | MB_APPLMODAL) ;

      break ;

  }

  return FALSE ;
}

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

  Function: InitOpenStruct(HWND, LPOPENFILENAME)

  Purpose:

    Initializes the OPENFILENAME structure.  The structure is referenced
    via a pointer passed in as the second parameter so that we can pass
    any of the three OPENFILENAME structures into this function and
    Initialize them.

  Returns: Nothing.

  Comments:

    The szFilterInits and szCustFiltInits arrays are initialized to
    contain some default strings.  Eventually the strings in
    these arrays must be arranged one after the other with a null
    character between them and two null characters at the end:

    "Text files\0*.txt\0All files\0*.*\0\0"

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

void InitOpenStruct(HWND hwnd, LPOPENFILENAME po)
{
   int i = 0 ;
   szFileName[0] = 0 ;
   szFileTitle[0] = 0 ;

   dwFlags = OFN_READONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_SHOWHELP  ;

   if (bDoOpenDlg)
     lstrcpy(szDlgTitle, TEXT("Open Dialog Title")) ;
   else
     lstrcpy(szDlgTitle, TEXT("Save Dialog Title")) ;

   lstrcpy(szDefExt, TEXT("rat")) ;
   lstrcpy(szInitialDir, TEXT("c:\\")) ;
   lstrcpy(szTempName, TEXT("opentemp1")) ;

   lstrcpy(&szFilterInits[0][0], TEXT("All Files (*.*)")) ;
   lstrcpy(&szFilterInits[1][0], TEXT("*.*")) ;
   lstrcpy(&szFilterInits[2][0], TEXT("Fat Files (*.fat)")) ;
   lstrcpy(&szFilterInits[3][0], TEXT("*.fat")) ;
   szFilterInits[4][0] = (TCHAR) 0 ;

   lstrcpy(&szCustFiltInits[0][0], TEXT("Last Filter Used")) ;
   lstrcpy(&szCustFiltInits[1][0], TEXT("*.lst")) ;
   szCustFiltInits[2][0] = (TCHAR) 0 ;

   /*

      These two functions will create "strings" in the applications
      data area that are in the form

      "Filter Description"\0
      "Filter"\0
      "Filter Description"\0
      "Filter"\0
      ..
      ..
      \0\0

      The filters must be in this form in order that the common dialogs
      interpret it correctly...
   */

   InitFilterString() ;

   InitCustFilterString() ;

   po->lStructSize          = sizeof(OPENFILENAME) ;
   po->hwndOwner            = hwnd ;
   po->hInstance            = hInst ;
   (LPTSTR) po->lpstrFilter = lpszFilterString ;
   po->lpstrCustomFilter    = lpszCustFilterString ;
   po->nMaxCustFilter       = MAXCUSTFILTER ;
   po->nFilterIndex         = 1L ;
   po->lpstrFile            = szFileName ;
   po->nMaxFile             = FILENAMESIZE ;
   po->lpstrFileTitle       = szFileTitle ;
   po->nMaxFileTitle        = FILETITLESIZE ;
   po->lpstrInitialDir      = szInitialDir ;
   (LPTSTR) po->lpstrTitle  = szDlgTitle ;
   po->Flags                = dwFlags ;
   po->nFileOffset          = 0 ;
   po->nFileExtension       = 0 ;
   (LPTSTR) po->lpstrDefExt = szDefExt;
   po->lCustData            = 0L ;
   po->lpfnHook             = OpenSaveHookProc ;
   (LPTSTR) po->lpTemplateName  = szTempName ;

   return ;
}

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

  Function: FillOpenDlg(HWND, LPOPENFILENAME)

  Purpose:

    - This function will fill in the edit boxes that correspond to each
      of the fields in the OPENFILENAME structure.  The user can accept
      these values to create the common dialog or edit them.

  Returns: Nothing.

  Comments:

      The contents of the strings "szShortFilter" and "szLongFilter"
      determine how a WORD or a DWORD value is represented in the edit boxes

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

void FillOpenDlg(HWND hwnd, LPOPENFILENAME po)
{
   int i = 0 ;

   wsprintf(szTemp, szShortFilter, (int) po->lStructSize) ;
   SetDlgItemText(hwnd, ID_STRUCTSIZEO, szTemp) ;

   wsprintf(szTemp, szLongFilter, (LONG) po->hwndOwner) ;
   SetDlgItemText(hwnd, ID_HWNDOWNERO, szTemp) ;

   wsprintf(szTemp, szLongFilter, (LONG) po->hInstance) ;
   SetDlgItemText(hwnd, ID_HINSTANCEO, szTemp) ;

   SendDlgItemMessage(hwnd, ID_FILTERO, CB_RESETCONTENT,
                      (WPARAM) 0, (LPARAM) 0) ;

   while (szFilterInits[i][0] != (TCHAR) 0)
   {
      SendDlgItemMessage(hwnd, ID_FILTERO, CB_ADDSTRING, (WPARAM) 0,
                        (LPARAM) (LPTSTR) &szFilterInits[i][0]) ;
      i++ ;
   }

   SendDlgItemMessage(hwnd, ID_FILTERO, CB_SETCURSEL,
                     (WPARAM) 0, (LPARAM) 0 ) ;

   SendDlgItemMessage(hwnd, ID_CUSTFILTO, CB_RESETCONTENT,
                      (WPARAM) 0, (LPARAM) 0) ;

   for (i=0; i<2; i++)
     SendDlgItemMessage(hwnd, ID_CUSTFILTO, CB_ADDSTRING, (WPARAM) 0,
                        (LPARAM) (LPTSTR) &szCustFiltInits[i][0]) ;

   SendDlgItemMessage(hwnd, ID_CUSTFILTO, CB_SETCURSEL,
                     (WPARAM) 0, (LPARAM) 0) ;

   wsprintf(szTemp, szShortFilter, (int) po->nMaxCustFilter) ;
   SetDlgItemText(hwnd, ID_MAXCUSTFILTO, szTemp) ;

   wsprintf(szTemp, szShortFilter, (int) po->nFilterIndex) ;
   SetDlgItemText(hwnd, ID_FILTINDEXO, szTemp) ;

   SetDlgItemText(hwnd, ID_SZFILEO, po->lpstrFile) ;

   wsprintf(szTemp, szShortFilter, (int) po->nMaxFile) ;
   SetDlgItemText(hwnd, ID_MAXSZFILEO, szTemp) ;

   SetDlgItemText(hwnd, ID_SZFILETITLEO, po->lpstrFileTitle) ;

   wsprintf(szTemp, szShortFilter, (int) po->nMaxFileTitle) ;
   SetDlgItemText(hwnd, ID_MAXSZFILETITLEO, szTemp) ;

   SetDlgItemText(hwnd, ID_SZINITDIRO, po->lpstrInitialDir) ;

   SetDlgItemText(hwnd, ID_SZTITLEO, po->lpstrTitle) ;

   wsprintf(szTemp, szLongFilter, po->Flags) ;
   SetDlgItemText(hwnd, ID_FLAGSO, szTemp) ;

   wsprintf(szTemp, szShortFilter, po->nFileOffset) ;
   SetDlgItemText(hwnd, ID_FILEOFFO, szTemp) ;

   wsprintf(szTemp, szShortFilter, po->nFileExtension) ;
   SetDlgItemText(hwnd, ID_FILEEXTO, szTemp) ;

   SetDlgItemText(hwnd, ID_SZDEFEXTO, po->lpstrDefExt) ;

   wsprintf(szTemp, szLongFilter, po->lCustData) ;
   SetDlgItemText(hwnd, ID_CUSTDATAO, szTemp) ;

   wsprintf(szTemp, szLongFilter, po->lpfnHook) ;
   SetDlgItemText(hwnd, ID_HOOKO, szTemp) ;

   SetDlgItemText(hwnd, ID_TEMPLATEO, po->lpTemplateName) ;

   return ;
}

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

  Function: GetOpenDlg(HWND, LPOPENFILENAME)

  Purpose:

      This function will retrieve the contents of each edit box corresponding
      with each field in the OPENFILENAME structure, and fill in the
      OPENFILENAME structure with these values.

  Returns: Nothing.

  Comments:

    if (uMode == IDM_HEXMODE), then the numbers should be interpreted as
    hexidecimal and the MyAtol() function is called with its "bHex"
    parameter set to true.

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

void GetOpenDlg(HWND hwnd, LPOPENFILENAME po)
{
   int i ;
   BOOL b ;
   TCHAR szNum[20] ;

   GetDlgItemText(hwnd, ID_STRUCTSIZEO, szNum, 20) ;
   po->lStructSize = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_HWNDOWNERO, szNum, 20) ;
   po->hwndOwner = (HWND) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_HINSTANCEO, szNum, 20) ;
   po->hInstance = (HANDLE) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   /* these are just strings, no conversion necessary */

   i = 0 ;
   while (SendDlgItemMessage(hwnd, ID_FILTERO, CB_GETLBTEXT, (WPARAM) i,
                          (LPARAM) (LPTSTR) szFilterInits[i]) != CB_ERR)
   { i++ ; }

   /* create the filter string */

   InitFilterString() ;

   i = 0 ;

   while (SendDlgItemMessage(hwnd, ID_CUSTFILTO, CB_GETLBTEXT, (WPARAM) i,
                        (LPARAM) (LPTSTR) szCustFiltInits[i]) != CB_ERR)
   { i++ ; }

   InitCustFilterString() ;

   GetDlgItemText(hwnd, ID_MAXCUSTFILTO, szNum, 20) ;
   po->nMaxCustFilter = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_FILTINDEXO, szNum, 20) ;
   po->nFilterIndex = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_SZFILEO, po->lpstrFile, MAXBUF) ;

   GetDlgItemText(hwnd, ID_MAXSZFILEO, szNum, 20) ;
   po->nMaxFile = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_SZFILETITLEO, po->lpstrFileTitle, MAXBUF) ;

   GetDlgItemText(hwnd, ID_MAXSZFILETITLEO, szNum, MAXBUF) ;
   po->nMaxFileTitle = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_SZINITDIRO, (LPTSTR) po->lpstrInitialDir, MAXBUF) ;

   GetDlgItemText(hwnd, ID_SZTITLEO, (LPTSTR) po->lpstrTitle, MAXBUF) ;

   GetDlgItemText(hwnd, ID_FLAGSO, szNum, 20) ;
   po->Flags = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_FILEOFFO, szNum, 20) ;
   po->nFileOffset = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_FILEEXTO, szNum, 20) ;
   po->nFileExtension = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_SZDEFEXTO, (LPTSTR) po->lpstrDefExt, DEFEXTSIZE) ;

   GetDlgItemText(hwnd, ID_CUSTDATAO, szNum, 20) ;
   po->lCustData = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_HOOKO, szNum, 20) ;
   po->lpfnHook = (LPOFNHOOKPROC) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

   GetDlgItemText(hwnd, ID_TEMPLATEO, (LPTSTR) po->lpTemplateName, TEMPNAMESIZE) ;

   /*  if we are supposed to use a preloaded resource handle, load it and put it in
       OPENFILENAME.hInstance... */

   if (IsDlgButtonChecked(hwnd, ID_USEHINSTO) == 1)
     po->hInstance = GetCorrectResourceHandle() ;

   return ;
}

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

  Function: InitFilterString(void)

  Purpose:

    This function will create a "string" in memory in the form that the
    GetOpenFileName() function will expect for the filters it fills into
    the "List Files of Type" combo box.

  Returns: Nothing.

  Comments:

    The szFilterInits and szCustFiltInits arrays are initialized to
    contain some default strings.  Eventually the strings in
    these arrays must be arranged one after the other with a null
    character between them and two null characters at the end:

    "Text files\0*.txt\0All files\0*.*\0\0"

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

void InitFilterString(void)
{
  int i ;
  int nInc = 0 ;
  LPTSTR lpStr = szFilterString ;

  /* First, zero out this memory just for the sake of sanity */

  for (i=0; i<MAXBUF; i++)
    szFilterString[i] = 0 ;

  /* Now, for each string in the szFilterInits array, concatenate it to
     the last one right after the last one's null terminator */

  i = 0 ;

  while (szFilterInits[i][0] != (TCHAR) 0)
  {
    lstrcpy(lpStr, &szFilterInits[i][0]) ;
    nInc+=lstrlen(&szFilterInits[i][0]) + 1 ;   //1 past null term...
    lpStr = &szFilterString[nInc] ;
    i++ ;
  }

  szFilterString[nInc] = (TCHAR) 0 ;  //double terminator

  /* Set the lpszFilterString to point to the memory we just filled in
     with the filters because lpszFilterString is what is in
     OPENFILENAME->lpstrFilter */

  lpszFilterString = szFilterString ;

  return ;
}

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

  Function: InitCustFiltString(void)

  Purpose:

    This function will create a "string" in memory in the form that the
    GetOpenFileName() function will expect for a custom filter.

  Returns: Nothing.

  Comments:

    The szFilterInits and szCustFiltInits arrays are initialized to
    contain some default strings.  Eventually the strings in
    these arrays must be arranged one after the other with a null
    character between them and two null characters at the end:

    "Text files\0*.txt\0All files\0*.*\0\0"

    This program initializes these strings, but they do not need to be
    initialized.  The GetOpenFileName() functiion will write a filter
    into this memory area if the user types a new filter into the
    "FileName" box and returns by clicking the OK button (indicating that
    a file matching that filter was found).

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

void InitCustFilterString(void)
{
  int i ;
  LPTSTR lpStr = szCustFilterString ;
  int nInc = 0 ;

  for (i=0; i<MAXBUF; i++)
    szCustFilterString[i] = 0 ;

  i = 0 ;

  for(i=0; i<2; i++)  //only two for the custom filter
  {
    lstrcpy(lpStr, &szCustFiltInits[i][0]) ;
    nInc+=lstrlen(&szCustFiltInits[i][0]) + 1 ;
    lpStr = &szCustFilterString[nInc] ;
  }

  szCustFilterString[nInc] = (TCHAR) 0 ;

  lpszCustFilterString = szCustFilterString ;

  return ;
}

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

  Function: InterpretCustomFilterString(void)

  Purpose:

    This function will parse the memory that is being used for the
    custom filter string.  If the user returned TRUE after entering
    their own filter, the new filter will be here and so we need to
    add it to our custom filter init array...

  Returns: Nothing.

  Comments:

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

void InterpretCustomFilterString(void)
{
  LPTSTR pNext ;

  int nCount = lstrlen(ofn.lpstrCustomFilter) ;

  pNext = ofn.lpstrCustomFilter + nCount + 1 ;  //one past the NULL

  /* add it to the filter inits array */

  lstrcpy(&szCustFiltInits[0][0], ofn.lpstrCustomFilter) ;
  lstrcpy(&szCustFiltInits[1][0], pNext) ;

}

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

  Function: GetCorrectResourceHandle(void)

  Purpose:

    This function will use FindResource() to find the correct custom
    template resource, use LoadResource() to get a handle to it.

  Returns: A handle to a custom template resource.

  Comments:

    The names for the custom template for GetOpenFileName() are

      "opentemp1" -- normal
      "opentemp2" -- contains a multi-select list box for the file names

    If the user has marked the "Preloaded Template" box and specified
    OFN_ENABLETEMPLATEHANDLE in the "Flags" edit box, this handle
    will be used to create the GetOpenFileName() dialog box.

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

HANDLE GetCorrectResourceHandle(void)
{
  if (ofn.Flags & OFN_ALLOWMULTISELECT)
  {
     hRes = FindResource(hInst, TEXT("opentemp2"), RT_DIALOG) ;
     hDialog = LoadResource(hInst, hRes) ;
  }

  else
  {
     hRes = FindResource(hInst, TEXT("opentemp1"), RT_DIALOG) ;
     hDialog = LoadResource(hInst, hRes) ;
  }

  return hDialog ;
}

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

  Function: OpenSaveHookProc(HWND, UINT, UINT, LONG) ;

  Purpose:

    This function is the hook function for the GetOpenFileName() function.
    If GetOpenFileName() is called with the OFN_ENABLEHOOK flag, this
    function will be called before the normal GetOpenFileName() dialog
    function is called.

  Returns: FALSE to pass the message on to the normal GetOpenFileName()
           logic, TRUE to discard the message.

  Comments:

    To enable this function in this program, enter the value for
    OFN_ENABLEHOOK in the "Flags" edit box.

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

UINT APIENTRY OpenSaveHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  LPOPENFILENAME pOfn ;
  TCHAR szMsg[50] ;

  switch(msg)
  {
    case WM_INITDIALOG:

      pOfn = (LPOPENFILENAME) lParam ;

      /* During initialization, if there is a hook proc, the getopen()
         code will send pointer to the OPENFILENAME strucure in the
         lParam.  To demonstrate this, pop up a message box if this
         structure has a non zero value in the lCustData structure member */

      if (pOfn->lCustData != 0L)
      {
        wsprintf(szMsg, TEXT("OPENFILENAME->lCustData is: %ld"), pOfn->lCustData) ;

        MessageBox(hwnd, szMsg, TEXT("lCustData Sent!"), MB_OK) ;
      }

      SetWindowText(hwnd, TEXT("Open Hook Proc Dialog")) ;

      break ;

    /* use the WM_CTLCOLOR* messages to change the color of the Open
       dialog */

    case WM_CTLCOLORDLG:

        if (!hBrushDlg)
            hBrushDlg = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushDlg ;

        break ;

    case WM_CTLCOLORBTN:

        SetBkMode((HDC) wParam, TRANSPARENT) ;   //sets background color
                                                 //for push and check box
                                                 //buttons...

        if (!hBrushButton)
            hBrushButton = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushButton ;

        break ;

    case WM_CTLCOLORSTATIC:

        SetTextColor((HDC) wParam, RGB(0x00, 0xff, 0x00)) ;  //green
        SetBkMode((HDC) wParam, TRANSPARENT) ;               //transparent text

        if (!hBrushDlg)
            hBrushDlg = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushDlg ;

        break ;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
          case chx1:
            MessageBox(hwnd, TEXT("Read-Only button clicked..."),
                       TEXT("Open"), MB_OK | MB_APPLMODAL) ;
            break ;

          case ID_FILEPREVIEW:
            MessageBox(hwnd, TEXT("File Preview Button Clicked"),
                       TEXT("Open"), MB_OK | MB_APPLMODAL) ;
            break ;

          default: break ;
        }
        break ;

    default:

      if (msg == nOpenShareVMsg)
      {
        MessageBox(hwnd, TEXT("The SHAREVSTRING message is here!"),
                         TEXT("Open"),
                         MB_ICONEXCLAMATION | MB_OK | MB_APPLMODAL) ;

        return OFN_SHAREWARN ;
      }
      break ;
  }

  return FALSE ;   //send msg to the common dialog code
}

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

  Function: MultiThreadOpenSave(void)

  Purpose:

    This function will start two threads and then return.  The two
    threads will create GetOpenFileName() dialogs, and the user
    can interact with two Open dialogs at once.

  Returns: Nothing.

  Comments:

    This will create two Open dialogs, but they will be created in the
    same location on the screen.  The only way to change that would be
    to call the function with a hookproc and move one of the windows.

    But that would mean that if the user did not enter OFN_ENABLEHOOK
    in the "Flags" edit box, we would be no better off than before.  So,
    in order to see both dialogs, you just have to move the top one out
    of the way a little.

    Multithreading note:

    This function will return before the common dialog functions return.

Therefore, do not pass any parameters to this function that will be
    referenced by the common dialogs because as soon as this function
    ends those parameters will be gone.

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

void MultiThreadOpenSave(void)
{

  if (!(hOpenSaveThread1 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                        (LPTHREAD_START_ROUTINE) OpenSaveThread1Proc,
                                        &dwThreadParm1, CREATE_SUSPENDED, &dwThreadID1)))

  {
    MessageBox(GetForegroundWindow(), TEXT("Cannot create thread 1"), NULL, MB_OK | MB_ICONEXCLAMATION) ;
    OpenMultiThreadEnableButtons(TRUE, hwndMainDialog) ;
    nOpenDialogCount = 0 ;
    return ;
  }

  if (!(hOpenSaveThread2 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                        (LPTHREAD_START_ROUTINE) OpenSaveThread2Proc,
                                        &dwThreadParm2, CREATE_SUSPENDED, &dwThreadID2)))
  {
    MessageBox(GetForegroundWindow(), TEXT("Cannot create thread 2"), NULL, MB_OK | MB_ICONEXCLAMATION) ;
    OpenMultiThreadEnableButtons(TRUE, hwndMainDialog) ;
    nOpenDialogCount = 0 ;
    return ;
  }

  ResumeThread(hOpenSaveThread1) ;
  ResumeThread(hOpenSaveThread2) ;

}

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

  Function: OpenSaveThreadProc1(LPDWORD)

  Purpose:

    This is the address where the first thread starts executing.

  Returns: Any DWORD value.

  Comments:

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

DWORD OpenSaveThread1Proc(LPDWORD pDw)
{

  /* Get the user's input */

  GetOpenDlg(hwndMainDialog, &ofnThread1) ;

  /* Do the dialog */

  DoOpenSaveStuff(&ofnThread1) ;

  /* send a message to the parent telling it to decrement the dialog count.
     When the dialog count reaches zero, all the buttons are enabled again. */

  SendMessage(hwndMainDialog, UMSG_DECREMENTDLGCOUNT, 0, 0L) ;

  return 0L ;
}

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

  Function: OpenSaveThreadProc2(LPDWORD)

  Purpose:

    This is the address where the second thread starts executing.

  Returns: Any DWORD value.

  Comments:

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

DWORD OpenSaveThread2Proc(LPDWORD pDw)
{
  GetOpenDlg(hwndMainDialog, &ofnThread2) ;

  DoOpenSaveStuff(&ofnThread2) ;

  SendMessage(hwndMainDialog, UMSG_DECREMENTDLGCOUNT, 0, 0L) ;

  return 0L ;
}

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

  Function: DoOpenSaveStuff(LPOPENFILENAME)

  Purpose:

    This is the function that actually calls GetOpenFileName() or
    GetSaveFileName().  It receives a pointer to the structure to
    use as its only parameter.

  Returns: Nothing.

  Comments:

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

void DoOpenSaveStuff(LPOPENFILENAME po)
{
  BOOL bRet = TRUE ;

  /* If bOpenDlg is TRUE, do GetOpenFileName(), else do GetSave().
     If the "NULL Structure" button is checked, send in a NULL pointer
     to the function */

  if (bDoOpenDlg)
  {
    if (IsDlgButtonChecked(hwndMainDialog, ID_NULLSTRUCTO) == 1)
      bRet = GetOpenFileName((LPOPENFILENAME) NULL) ;
    else
      bRet = GetOpenFileName(po) ;
  }

  else
  {
    if (IsDlgButtonChecked(hwndMainDialog, ID_NULLSTRUCTO))
      bRet = GetSaveFileName((LPOPENFILENAME) NULL) ;
    else
      bRet = GetSaveFileName(po) ;
  }

  /* Record the results */

  wsprintf(szTemp, szLongFilter, CommDlgExtendedError()) ;
  SetDlgItemText(hwndMainDialog, ID_ERRORO, szTemp) ;

  wsprintf(szTemp, szShortFilter, (int) bRet) ;
  SetDlgItemText(hwndMainDialog, ID_RETURNO, szTemp) ;

  InterpretCustomFilterString() ;

  FillOpenDlg(hwndMainDialog, po) ;

  if (hDialog)
  {
    FreeResource(hDialog) ;    //free ofn.hInstance
    hDialog = (HANDLE) 0 ;
    hRes = (HANDLE) 0 ;
  }
}

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

  Function: OpenMultiThreadEnableButtons(BOOL, HWND)

  Purpose:

    Enable or disable the buttons that might affect the state of the
    OPENFILENAME structures.  This is necessary because during a
    multithreading session, these buttons may be accessible even though
    there are Open dialogs still working and we don't want to change
    the OPENFILENAME structure out from under them.

  Returns: Nothing.

  Comments:

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

void OpenMultiThreadEnableButtons(BOOL bEnable, HWND hwnd)
{
  EnableWindow(GetDlgItem(hwnd, IDOK), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, IDCANCEL), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_RESETOPEN), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_MULTIOPEN), bEnable) ;
}


OPEN.H

BOOL bDoOpenDlg ;

OPENFILENAME ofn ;

#define MAXBUF 300

#define MAXFILTERS     10
#define MAXCUSTFILTER  MAXBUF
#define FILENAMESIZE   MAXBUF
#define FILETITLESIZE  MAXBUF
#define DLGTITLESIZE   MAXBUF
#define INITDIRSIZE    MAXBUF
#define DEFEXTSIZE     40
#define TEMPNAMESIZE   30

BOOL APIENTRY OpenFunc(HWND, UINT, UINT, LONG) ;
void FillOpenDlg(HWND, LPOPENFILENAME) ;
void InitOpenStruct(HWND, LPOPENFILENAME) ;
void GetOpenDlg(HWND, LPOPENFILENAME) ;
void InitFilterString(void) ;
void InitCustFilterString(void) ;

TCHAR szFileTitle[FILETITLESIZE]  ;
TCHAR szDlgTitle[DLGTITLESIZE]    ;
TCHAR szFileName[FILENAMESIZE]    ;
TCHAR szInitialDir[INITDIRSIZE]   ;
TCHAR szDefExt[DEFEXTSIZE]        ;
TCHAR szTempName[TEMPNAMESIZE]    ;

TCHAR szFilterInits[MAXFILTERS][30] ;
TCHAR szFilterString[MAXBUF] ;
LPTSTR lpszFilterString ;

TCHAR  szCustFiltInits[3][30] ;
TCHAR  szCustFilterString[MAXBUF] ;
LPTSTR lpszCustFilterString ;

DWORD dwFlags ;

void DoOpenDialog(HWND) ;

#define ID_FILTERO                   400
#define ID_CUSTFILTO                 401
#define ID_MAXCUSTFILTO              402
#define ID_FILTINDEXO                403
#define ID_SZFILEO                   404
#define ID_MAXSZFILEO                405
#define ID_SZFILETITLEO              406
#define ID_MAXSZFILETITLEO           407
#define ID_SZINITDIRO                408
#define ID_SZTITLEO                  409
#define ID_FLAGSO                    410
#define ID_FILEOFFO                  411
#define ID_FILEEXTO                  412
#define ID_SZDEFEXTO                 413
#define ID_ERRORO                    414
#define ID_RETURNO                   415
#define ID_OPENDIALOG                606
#define ID_STRUCTSIZEO               416
#define ID_HWNDOWNERO                417
#define ID_HINSTANCEO                418
#define ID_CUSTDATAO                 419
#define ID_HOOKO                     420
#define ID_TEMPLATEO                 421
#define ID_ADD1O                     422
#define ID_ADD2O                     423
#define ID_CLEAR1O                   424
#define ID_CLEAR2O                   425
#define ID_RESETOPEN                 601
#define ID_NULLSTRUCTO               602
#define ID_USEHINSTO                 607
#define ID_MULTIOPEN                 608
#define ID_FILEPREVIEW               609


PRINT.C

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

  File: print.c

  Purpose:  To manage CDTEST's print dialog box.

  Functions:

    DoPrintDlg()            -- Creates CDTEST's print dialog box.

    PrintProc()             -- Callback function for CDTEST's print dialog box.

    DevnamesProc()          -- Callback function for CDTESTS Devnames dlg box.

    DevmodeProc()           -- Callback function for CDTESTS Devmode dlg box.

    InitPrintStruct()       -- Fills a PRINTDLG struct with some default values.

    FillPrintDlg()          -- Fills CDTEST's print dialog with contents of a
                               PRINTDLG structure.

    GetPrintDlg()           -- Retrieves the users edits from CDTEST's print dlg.

    FillDevnamesDlg()       -- Fills CDTEST's Devnames dialog with the contents
                               of a DEVNAMES structure.

    GetDevnamesDlg()        -- Retrieves the user's edits from CDTEST's devnames dlg.

    FillDevmodeDlg()        -- Fills CDTEST's devmode dialog with the contents of
                               a DEVMODE structure.

    GetDevmodeDlg()         -- Retrieves the user's edits from CDTEST's devmode dlg.

    GetPrintDlgHandle()     -- Loads and returns a handle to a Print dlg custom template.

    GetSetupDlgHandle()     -- Loads and returns a handle to a Setup dlg custom template.

    PrintHookProc()         -- Callback function for PRINTDLG->lpfnPrintHook and
                               for PRINTDLG->lpfnSetupHook

    PrintMultiProc1()       -- Starting address for multithread option thread 1

    PrintMultiProc2()       -- Starting address for multithread option thread 2

    MultiThreadPrintDlg()   -- Creates two threads which each call PrintDlg()

    EnablePrintControls()   -- Enables or disables CDTEST's print dlg controls.

    DoPrintDlgStuff()       -- Calls PrintDlg()

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

#include <windows.h>
#include <commdlg.h>
#include <stdlib.h>
#include <winnls.h>
#include "cdtest.h"
#include "print.h"
#include "devnames.h"
#include "devmode.h"
#include "dlgs.h"

/* Externs, prototypes, variable declarations for print.c */

extern UINT uMode ;
extern LONG MyAtol(LPTSTR, BOOL, LPBOOL) ;
UINT APIENTRY PrintHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam) ;
void InitPrintStruct(HWND, LPPRINTDLG) ;
void FillPrintDlg(HWND, LPPRINTDLG) ;
void GetPrintDlg(HWND, LPPRINTDLG) ;
void FillDevnamesDlg(HWND, LPPRINTDLG) ;
void GetDevnamesDlg(HWND, LPPRINTDLG) ;
void FillDevmodeDlg(HWND, LPPRINTDLG) ;
void GetDevmodeDlg(HWND, LPPRINTDLG) ;
HANDLE GetPrintDlgHandle(void) ;
HANDLE GetSetupDlgHandle(void) ;
void DoPrintDlgStuff(HWND, LPPRINTDLG) ;

HANDLE hResPrint ;
HANDLE hDialogPrint ;
PRINTDLG pd ;
HANDLE hDevNames ;
HANDLE hDevMode ;

TCHAR szDriverOffset[100] ;
TCHAR szDeviceOffset[100] ;
TCHAR szOutputOffset[100] ;
TCHAR szPrintTempName[40] ;
TCHAR szSetupTempName[40] ;

/* Multithreading function declarations, variables */

DWORD PrintMultiProc1(LPDWORD) ;
DWORD PrintMultiProc2(LPDWORD) ;
void MultiThreadPrintDlg(void) ;
void EnablePrintControls(HWND, BOOL) ;

HANDLE hPrintThread1 ;
HANDLE hPrintThread2 ;
DWORD dwPrintThreadID1 ;
DWORD dwPrintThreadID2 ;
DWORD dwPrintThreadParm1 ;
DWORD dwPrintThreadParm2 ;
PRINTDLG pdThread1 ;
PRINTDLG pdThread2 ;
HWND hwndMainPrint ;
int nOpenDialogCount ;

HBRUSH hBrushDlg ;
HBRUSH hBrushEdit ;    //brush handles for new colors done with hook proc
HBRUSH hBrushButton ;

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

  Function: DoPrintDialog(HWND)

  Purpose:  Creates CDTEST's print dialog box.

  Returns:  Nothing.

  Comments: There is only one dialog for both Print Setup and Print Print.
            The Print Print dialog is the default.  To create the Print
            Setup dialog, enter the value for PD_PRINTSETUP into the
            "Flags" edit box and click OK.

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

void DoPrintDialog(HWND hwnd)
{

  DialogBox(hInst, MAKEINTRESOURCE(ID_PRINTDIALOG),
            hwnd, PrintProc) ;

}

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

  Function: PrintProc(HWND, UINT, UINT, LONG)

  Purpose:  Callback function for CDTEST's print dialog box.

  Returns:  TRUE or FALSE depending on the situation/message.

  Comments:

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

BOOL APIENTRY PrintProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  switch (msg)
  {
    case WM_INITDIALOG:

        InitPrintStruct(hwnd, &pd) ;
        FillPrintDlg(hwnd, &pd) ;

        *(&pdThread1) = *(&pdThread2) = *(&pd) ;

        hwndMainPrint = hwnd ;

        nOpenDialogCount = 0 ;

        SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEP)) ;

        break ;

    case UMSG_DECREMENTDLGCOUNT: //user defined message that is send when
                                 //each thread created when multithreading ends
        nOpenDialogCount-- ;

        if (nOpenDialogCount == 0)               //once both multitheading dlgs
            EnablePrintControls(hwnd, TRUE) ;   //are done, enable the controls again

        break ;

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
          case IDOK:
            GetPrintDlg(hwnd, &pd) ;
            DoPrintDlgStuff(hwnd, &pd) ;
            break ;

          case IDCANCEL:

            if (pd.hDC)
              ReleaseDC(hwnd, pd.hDC) ;

            EndDialog(hwnd, FALSE) ;
            break ;

          case ID_RESETPRINT:
            SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEP)) ;
            InitPrintStruct(hwnd, &pd) ;
            FillPrintDlg(hwnd, &pd) ;
            SendDlgItemMessage(hwnd, ID_PRESETUPP,   BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;
            SendDlgItemMessage(hwnd, ID_PREPRINTP,   BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;
            SendDlgItemMessage(hwnd, ID_NULLSTRUCTP, BM_SETCHECK, (WPARAM)0, (LPARAM)0) ;
            break ;

          case ID_EDITDEVNAMES:
            DialogBox(hInst, MAKEINTRESOURCE(ID_DEVNAMESDIALOG),
                      hwnd, DevnamesProc) ;
            break ;

          case ID_EDITDEVMODE:
            DialogBox(hInst, MAKEINTRESOURCE(ID_DEVMODEDIALOG),
                      hwnd, DevmodeProc) ;
            break ;

          case ID_MULTITHREADPRINT:
            nOpenDialogCount = 2 ;
            EnablePrintControls(hwnd, FALSE) ;
            MultiThreadPrintDlg() ;
            break ;

          default: break ;
        }
    }

    default:

      /* If the help button is pressed in the PrintDlg() dialog box,
         it will send a message Registered with RegisterWindowMessage()
         to the parent window.  The message nHelpMessage was registered
         at application startup */

      if (msg == nHelpMessage)
        MessageBox(GetForegroundWindow(), TEXT("Hello from the help button"),
                   TEXT("Print Help Button"), MB_OK | MB_APPLMODAL) ;
      break ;
  }

  return FALSE ;
}

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

  Function: DevnamesProc(HWND, UINT, UINT, LONG)

  Purpose:  Callback function for the Devnames dialog box

  Returns:  TRUE or FALSE depending on situation / message

  Comments:

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

BOOL APIENTRY DevnamesProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  switch (msg)
  {
    case WM_INITDIALOG:
        FillDevnamesDlg(hwnd, &pd) ;
        break ;

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
          case IDOK:
            GetDevnamesDlg(hwnd, &pd) ;
            EndDialog(hwnd, TRUE) ;
            break ;

          case IDCANCEL:
            EndDialog(hwnd, FALSE) ;
            break ;

          default: break ;
        }
    }

    default: break ;
  }

  return FALSE ;
}

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

  Function: DevmodeProc(HWND, UINT, UINT, LONG)

  Purpose:  Callback function for the Devmode dialog box

  Returns:  TRUE or FALSE depending on situation / message

  Comments:

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

BOOL APIENTRY DevmodeProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  switch (msg)
  {
    case WM_INITDIALOG:
        FillDevmodeDlg(hwnd, &pd) ;
        break ;

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
          case IDOK:
            GetDevmodeDlg(hwnd, &pd) ;
            EndDialog(hwnd, TRUE) ;
            break ;

          case IDCANCEL:
            EndDialog(hwnd, FALSE) ;
            break ;

          default: break ;
        }
    }

    default: break ;
  }

  return FALSE ;
}

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

  Function: InitPrintStruct(HWND, LPPRINTDLG)

  Purpose:  Fills a PRINTDLG structure with some default values.

  Returns:  Nothing.

  Comments:

    In order to fill in the hDevMode and hDevNames PRINTDLG structure
    elements, a call to PrintDlg() must be made with the
    (PD_RETURNDEFAULT | PD_RETURNDC) flag bits set.

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

void InitPrintStruct(HWND hwnd, LPPRINTDLG ppd)
{
  DWORD dwTempFlags = 0 ;

  ppd->lStructSize = sizeof(PRINTDLG) ;
  ppd->hwndOwner = hwnd ;
  ppd->hDevMode = (HANDLE) 0 ;
  ppd->hDevNames = (HANDLE) 0 ;
  ppd->hDC = (HDC) 0 ;
  ppd->Flags = PD_RETURNDC | PD_COLLATE | PD_SHOWHELP | PD_PAGENUMS ;
  ppd->nFromPage = 1 ;
  ppd->nToPage = 10 ;
  ppd->nMinPage = 0 ;
  ppd->nMaxPage = 9 ;
  ppd->nCopies = 1000 ;
  ppd->hInstance = (HANDLE) hInst ;
  ppd->lCustData = 0L ;

  ppd->lpfnPrintHook = PrintHookProc ;
  ppd->lpfnSetupHook = PrintHookProc ;

  lstrcpy(szPrintTempName, TEXT("prtemp1")) ;
  ppd->lpPrintTemplateName = szPrintTempName ;

  lstrcpy(szSetupTempName, TEXT("prtemp2")) ;
  ppd->lpSetupTemplateName = szSetupTempName ;

  ppd->hPrintTemplate = (HANDLE) 0 ;   //these are unknown right now
  ppd->hSetupTemplate = (HANDLE) 0 ;

  /* Let's fill the hDevMode and hDevNames structures. */
  /* This call should just return handles to structures of this */
  /* type filled with the info for the default printer */

  dwTempFlags = ppd->Flags ;
  ppd->Flags = PD_RETURNDEFAULT | PD_RETURNDC ;
  PrintDlg(ppd) ;

  ppd->Flags = dwTempFlags ;
}

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

  Function: FillPrintDlg(HWND, LPPRINTDLG)

  Purpose:  Fills CDTEST's print dialog with the contents of a
            PRINTDLG structure.

  Returns:  Nothing.

  Comments:

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

void FillPrintDlg(HWND hwnd, LPPRINTDLG ppd)
{
  wsprintf(szTemp, szLongFilter, ppd->lStructSize) ;
  SetDlgItemText(hwnd, ID_STRUCTSIZEP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hwndOwner) ;
  SetDlgItemText(hwnd, ID_HWNDOWNERP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hDevMode) ;
  SetDlgItemText(hwnd, ID_HDEVMODEP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hDevNames) ;
  SetDlgItemText(hwnd, ID_HDEVNAMESP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hDC) ;
  SetDlgItemText(hwnd, ID_HDCP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->Flags) ;
  SetDlgItemText(hwnd, ID_FLAGSP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->nFromPage) ;
  SetDlgItemText(hwnd, ID_FROMPAGEP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->nToPage) ;
  SetDlgItemText(hwnd, ID_TOPAGEP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->nMinPage) ;
  SetDlgItemText(hwnd, ID_MINPAGEP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->nMaxPage) ;
  SetDlgItemText(hwnd, ID_MAXPAGEP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->nCopies) ;
  SetDlgItemText(hwnd, ID_COPIESP, szTemp) ;

  wsprintf(szTemp, szLongFilter, ppd->hInstance) ;
  SetDlgItemText(hwnd, ID_HINSTANCEP, szTemp) ;

  wsprintf(szTemp, szLongFilter, ppd->lCustData) ;
  SetDlgItemText(hwnd, ID_CUSTDATAP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->lpfnPrintHook) ;
  SetDlgItemText(hwnd, ID_PRINTHOOKP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->lpfnSetupHook) ;
  SetDlgItemText(hwnd, ID_SETUPHOOKP, szTemp) ;

  SetDlgItemText(hwnd, ID_LPPRINTTEMPP, ppd->lpPrintTemplateName) ;

  SetDlgItemText(hwnd, ID_LPSETUPTEMPP, ppd->lpSetupTemplateName) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hPrintTemplate) ;
  SetDlgItemText(hwnd, ID_HPRINTTEMPP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hSetupTemplate) ;
  SetDlgItemText(hwnd, ID_HSETUPTEMPP, szTemp) ;
}

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

  Function: GetPrintDlg(HWND, LPPRINTDLG)

  Purpose:  Fills a PRINTDLG structure with the users choices in CDTEST's
            print dialog box.

  Returns:  Nothing.

  Comments:

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

void GetPrintDlg(HWND hwnd, LPPRINTDLG ppd)
{
  BOOL b ;
  TCHAR szNum[20] ;

  #define WMAX 20

  GetDlgItemText(hwnd, ID_STRUCTSIZEP, szNum, WMAX) ;
  ppd->lStructSize = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HWNDOWNERP, szNum, WMAX) ;
  ppd->hwndOwner = (HWND) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HDEVMODEP, szNum, WMAX) ;
  ppd->hDevMode = (HANDLE) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HDEVNAMESP, szNum, WMAX) ;
  ppd->hDevNames = (HANDLE) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HDCP, szNum, WMAX) ;
  ppd->hDC = (HDC) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_FLAGSP, szNum, WMAX) ;
  ppd->Flags = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_FROMPAGEP, szNum, WMAX) ;
  ppd->nFromPage = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_TOPAGEP, szNum, WMAX) ;
  ppd->nToPage = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_MINPAGEP, szNum, WMAX) ;
  ppd->nMinPage = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_MAXPAGEP, szNum, WMAX) ;
  ppd->nMaxPage = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_COPIESP, szNum, WMAX) ;
  ppd->nCopies = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HINSTANCEP, szNum, WMAX) ;
  ppd->hInstance = (HANDLE) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_CUSTDATAP, szNum, WMAX) ;
  ppd->lCustData = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_PRINTHOOKP, szNum, WMAX) ;
  ppd->lpfnPrintHook = (LPPRINTHOOKPROC) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_SETUPHOOKP, szNum, WMAX) ;
  ppd->lpfnSetupHook = (LPSETUPHOOKPROC) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LPPRINTTEMPP, szPrintTempName, 100) ;

  GetDlgItemText(hwnd, ID_LPSETUPTEMPP, szSetupTempName, 100) ;

  GetDlgItemText(hwnd, ID_HPRINTTEMPP, szNum, WMAX) ;
  ppd->hPrintTemplate = (HANDLE) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HSETUPTEMPP, szNum, WMAX) ;
  ppd->hSetupTemplate = (HANDLE) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

}

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

  Function: FillDevnamesDlg(HWND, LPPRINTDLG)

  Purpose:  Fills CDTEST's devnames dialog box with the values in
            The hDevNames structure member of a PRINTDLG structure.

  Returns:  Nothing.

  Comments:

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

void FillDevnamesDlg(HWND hwnd, LPPRINTDLG ppd)
{
  LPDEVNAMES pn = (LPDEVNAMES) 0 ;

  if (ppd->hDevNames == (HANDLE) 0)
    return ;

  pn = (LPDEVNAMES) GlobalLock(ppd->hDevNames) ;
  if (pn == (LPDEVNAMES) NULL) return ;

  /* To find the strings in this block of memory, add the correct
     offset to the original pointer returned by GlobalLock() */

  SetDlgItemText(hwnd, ID_WDRIVEROFF, (LPTSTR) pn + pn->wDriverOffset) ;

  SetDlgItemText(hwnd, ID_WDEVICEOFF, (LPTSTR) pn + pn->wDeviceOffset) ;

  SetDlgItemText(hwnd, ID_WOUTPUTOFF, (LPTSTR) pn + pn->wOutputOffset) ;

  wsprintf(szTemp, szShortFilter, (int) pn->wDefault) ;
  SetDlgItemText(hwnd, ID_WDEFAULT, szTemp) ;

  GlobalUnlock(ppd->hDevNames) ;
}

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

  Function: GetDevnamesDlg(HWND, LPPRINTDLG)

  Purpose:  Retrieves the user's choices in CDTEST's devnames dialog box,
            Allocates some memory to hold a new DEVNAMES structure, fills
            the users choices into the new DEVNAMES structure, and sets
            the hDevNames member of a PRINTDLG structure to this new
            memory handle.

  Returns:  Nothing.

  Comments:

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

void GetDevnamesDlg(HWND hwnd, LPPRINTDLG ppd)
{
  BOOL b ;
  TCHAR szNum[30] ;
  LPDEVNAMES pn ;
  TCHAR sz1[100] ;
  TCHAR sz2[100] ;
  TCHAR sz3[100] ;
  DWORD dwSize ;
  int n1, n2, n3 ;

  GetDlgItemText(hwnd, ID_WDRIVEROFF, sz1, 100) ;
  GetDlgItemText(hwnd, ID_WDEVICEOFF, sz2, 100) ;
  GetDlgItemText(hwnd, ID_WOUTPUTOFF, sz3, 100) ;

  n1 = lstrlen(sz1) ;
  n2 = lstrlen(sz2) ;
  n3 = lstrlen(sz3) ;

  if (hDevNames)
  {
    GlobalFree(hDevNames) ;
    hDevNames = (HANDLE) 0 ;
  }

  /* allocate size of everything + 5 extra bytes for zeroes */

  dwSize = sizeof(DEVNAMES) + ((n1+n2+n3+5) * sizeof(TCHAR)) ;

  /* Allocate the memory and lock it down */

  if (!(hDevNames = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwSize)))
  {
    MessageBox(hwnd, TEXT("Can't Alloc memory for hDevNames!"),
               TEXT("Fatal Error!"), MB_OK | MB_APPLMODAL) ;
    return ;
  }

  if (!(pn = (LPDEVNAMES) GlobalLock(hDevNames)))
  {
    MessageBox(hwnd, TEXT("Can't Lock New hDevNames memory!"),
               TEXT("Fatal Error"), MB_OK | MB_APPLMODAL) ;

    GlobalFree(hDevNames) ;

    hDevNames = (HANDLE) 0 ;

    return ;
  }

  /* skip over devnames and copy string 1 there.  Make sure to
     divide by sizeof(TCHAR) or the compiler will create a pointer
     that points too far if you compile as a UNICODE program */

  pn->wDriverOffset = sizeof(DEVNAMES) / sizeof(TCHAR) ;
  lstrcpy((LPTSTR) pn + pn->wDriverOffset, (LPTSTR) sz1) ;

  /* skip over string 1 and add string 1 with a zero between them */

  pn->wDeviceOffset = pn->wDriverOffset + n1 + 1 ;
  lstrcpy((LPTSTR) pn + pn->wDeviceOffset, (LPTSTR) sz2) ;

  /* skip over string 2 and put string 3 there with a zero between them */

  pn->wOutputOffset = pn->wDeviceOffset + n2 + 1 ;
  lstrcpy((LPTSTR) pn + pn->wOutputOffset, (LPTSTR) sz3) ;

  GetDlgItemText(hwnd, ID_WDEFAULT, szNum, 30) ;
  pn->wDefault = (WORD) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GlobalUnlock(hDevNames) ;

  ppd->hDevNames = hDevNames ;

  wsprintf(szTemp, szLongFilter, ppd->hDevNames) ;
  SetDlgItemText(GetParent(hwnd), ID_HDEVNAMESP, szTemp) ;

}

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

  Function: FillDevmodeDlg(HWND, LPPRINTDLG)

  Purpose: Fills CDTEST's devmode dialog with the contents of the DEVMODE
           structure handle of a PRINTDLG structure.

  Returns:  Nothing.

  Comments:

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

void FillDevmodeDlg(HWND hwnd, LPPRINTDLG ppd)
{
  LPDEVMODE p ;

  if (ppd->hDevMode == (HANDLE) 0)
    return ;

  p = (LPDEVMODE) GlobalLock(ppd->hDevMode) ;
  if (p == (LPDEVMODE) NULL) return ;

  SetDlgItemText(hwnd, ID_DMDEVNAME, (LPTSTR) p->dmDeviceName) ;

  wsprintf(szTemp, szShortFilter, (int) p->dmSpecVersion) ;
  SetDlgItemText(hwnd, ID_DMSPECVER, szTemp) ;

  wsprintf(szTemp, szShortFilter, (int) p->dmDriverVersion) ;
  SetDlgItemText(hwnd, ID_DMDRIVERVER, szTemp) ;

  wsprintf(szTemp, szShortFilter, (int) p->dmSize) ;
  SetDlgItemText(hwnd, ID_DMSIZEDEV, szTemp) ;

  wsprintf(szTemp, szShortFilter, (int) p->dmDriverExtra) ;
  SetDlgItemText(hwnd, ID_DMDRIVEREXTRA, szTemp) ;

  wsprintf(szTemp, szShortFilter, (int) p->dmFields) ;
  SetDlgItemText(hwnd, ID_DMFIELDS, szTemp) ;

  wsprintf(szTemp, szShortFilter, (int) p->dmOrientation) ;
  SetDlgItemText(hwnd, ID_DMORIENTATION, szTemp) ;

  wsprintf(szTemp, szShortFilter, (int) p->dmPaperSize) ;
  SetDlgItemText(hwnd, ID_DMPAPERSIZE, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmPaperLength) ;
  SetDlgItemText(hwnd, ID_DMPAPERLENGTH, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmPaperWidth) ;
  SetDlgItemText(hwnd, ID_DMPAPERWIDTH, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmScale) ;
  SetDlgItemText(hwnd, ID_DMSCALE, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmCopies) ;
  SetDlgItemText(hwnd, ID_DMCOPIES, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmDefaultSource) ;
  SetDlgItemText(hwnd, ID_DMDEFAULTSOURCE, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmPrintQuality) ;
  SetDlgItemText(hwnd, ID_DMPRINTQUALITY, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmColor) ;
  SetDlgItemText(hwnd, ID_DMCOLOR, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmDuplex) ;
  SetDlgItemText(hwnd, ID_DMDUPLEX, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmCollate) ;
  SetDlgItemText(hwnd, ID_DMCOLLATE, szTemp) ;

  SetDlgItemText(hwnd, ID_DMFORMNAME, (LPTSTR) p->dmFormName) ;

  wsprintf(szTemp, szLongFilter, p->dmBitsPerPel) ;
  SetDlgItemText(hwnd, ID_DMBITSPERPEL, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmPelsWidth) ;
  SetDlgItemText(hwnd, ID_DMPELSWIDTH, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmPelsHeight) ;
  SetDlgItemText(hwnd, ID_DMPELSHEIGHT, szTemp) ;

  wsprintf(szTemp, szLongFilter, p->dmDisplayFrequency) ;
  SetDlgItemText(hwnd, ID_DMDISPLAYFREQ, szTemp) ;

  GlobalUnlock(ppd->hDevMode) ;
}

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

  Function: GetDevmodeDlg(HWND, LPPRINTDLG)

  Purpose:  Retrieves the user's edits in CDTEST's DEVMODE dialog box,
            allocates memory for a new DEVMODE structure, fills that
            memory with the user's edits, and puts a handle to that
            memory in the hDevMode member of a PRINTDLG structure.

  Returns:  Nothing.

  Comments:

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

void GetDevmodeDlg(HWND hwnd, LPPRINTDLG ppd)
{
  LPDEVMODE p ;
  BOOL b ;
  TCHAR szNum[30] ;

  #define WMAXDEV 30

  if (hDevMode)
  {
    GlobalFree(hDevMode) ;
    hDevMode = (HANDLE) 0 ;
  }

  if (!(hDevMode = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE, sizeof(DEVMODE))))
  {
    MessageBox(hwnd, TEXT("Can't allocate memory for hDevMode"),
               TEXT("Fatal Error"), MB_OK | MB_APPLMODAL) ;
    return ;
  }

  if (!(p = (LPDEVMODE) GlobalLock(hDevMode)))
  {
    MessageBox(hwnd, TEXT("Can't lock memory for hDevMode"),
               TEXT("Fatal Error"), MB_OK | MB_APPLMODAL) ;

    GlobalFree(hDevMode) ;
    hDevMode = (HANDLE) 0 ;

    return ;
  }

  GetDlgItemText(hwnd, ID_DMDEVNAME, p->dmDeviceName, 32) ;

  GetDlgItemText(hwnd, ID_DMSPECVER, szNum, WMAXDEV) ;
  p->dmSpecVersion = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMDRIVERVER, szNum, WMAXDEV) ;
  p->dmDriverVersion = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMSIZEDEV, szNum, WMAXDEV) ;
  p->dmSize = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMDRIVEREXTRA, szNum, WMAXDEV) ;
  p->dmDriverExtra = (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMFIELDS, szNum, WMAXDEV) ;
  p->dmFields = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMORIENTATION, szNum, WMAXDEV) ;
  p->dmOrientation = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMPAPERSIZE, szNum, WMAXDEV) ;
  p->dmPaperSize = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMPAPERLENGTH, szNum, WMAXDEV) ;
  p->dmPaperLength = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMPAPERWIDTH, szNum, WMAXDEV) ;
  p->dmPaperWidth = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMSCALE, szNum, WMAXDEV) ;
  p->dmScale = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMCOPIES, szNum, WMAXDEV) ;
  p->dmCopies = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMDEFAULTSOURCE, szNum, WMAXDEV) ;
  p->dmDefaultSource = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMPRINTQUALITY, szNum, WMAXDEV) ;
  p->dmPrintQuality = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMCOLOR, szNum, WMAXDEV) ;
  p->dmColor = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMDUPLEX, szNum, WMAXDEV) ;
  p->dmDuplex = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMCOLLATE, szNum, WMAXDEV) ;
  p->dmCollate = (short) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMFORMNAME, p->dmFormName, 32) ;

  GetDlgItemText(hwnd, ID_DMBITSPERPEL, szNum, WMAXDEV) ;
  p->dmBitsPerPel = (USHORT) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMPELSWIDTH, szNum, WMAXDEV) ;
  p->dmPelsWidth = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMPELSHEIGHT, szNum, WMAXDEV) ;
  p->dmPelsHeight = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_DMDISPLAYFREQ, szNum, WMAXDEV) ;
  p->dmDisplayFrequency = MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

  GlobalUnlock(hDevMode) ;
  ppd->hDevMode = hDevMode ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hDevMode) ;
  SetDlgItemText(GetParent(hwnd), ID_HDEVMODEP, szTemp) ;
}

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

  Function: GetPrintDlgHandle(void)

  Purpose:  Finds the custom Print dialog template in the EXE, loads it
            into a handle, and returns the handle.

  Returns:  Nothing.

  Comments:

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

HANDLE GetPrintDlgHandle(void)
{
  hResPrint = FindResource(hInst, TEXT("prtemp1"), RT_DIALOG) ;

  hDialogPrint = LoadResource(hInst, hResPrint) ;

  return hDialogPrint ;
}

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

  Function: GetSetupDlgHandle(void)

  Purpose:  Finds the custom Setup dialog template in the EXE, loads it
            into a handle, and returns the handle.

  Returns:  Nothing.

  Comments:

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

HANDLE GetSetupDlgHandle(void)
{
  hResPrint = FindResource(hInst, TEXT("prtemp2"), RT_DIALOG) ;

  hDialogPrint = LoadResource(hInst, hResPrint) ;

  return hDialogPrint ;
}

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

  Function: PrintHookProc(HWND, UINT, UINT, LONG)

  Purpose:  A callback function that will receive messages intended for
            the PrintDlg() dialog boxes before the normal common dialog
            routine receives them.

  Returns:  FALSE to allow the common dialogs to process the message
            with its normal logic.  TRUE to discard the message.

  Comments:

     To enable this function for the Print dialog in CDTEST, enter the
     value for PD_ENABLEPRINTHOOK in the "Flags" edit box.

     To enable this function for the Setup dialog in CDTEST, enter the
     value for (PD_ENABLESETUPHOOK | PD_PRINTSETUP) in the "Flags" edit box.

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

UINT APIENTRY PrintHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  LPPRINTDLG pPr ;
  TCHAR szMsg[50] ;

  switch(msg)
  {
    case WM_INITDIALOG:

      pPr = (LPPRINTDLG) lParam ;

      if (pPr->lCustData != 0)
      {
        wsprintf(szMsg, TEXT("PRINTDLG->lCustData is: %ld"), pPr->lCustData) ;

        MessageBox(hwnd, szMsg, TEXT("lCustData Sent!"), MB_OK | MB_APPLMODAL) ;
      }

      SetWindowText(hwnd, TEXT("Print Hook Proc Dialog")) ;

      break ;

    /* use the WM_CTLCOLOR* messages to change the color of the Open
       dialog */

    case WM_CTLCOLORDLG:

        if (!hBrushDlg)
            hBrushDlg = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushDlg ;

        break ;

    case WM_CTLCOLORBTN:

        SetBkMode((HDC) wParam, TRANSPARENT) ;   //sets background color
                                                 //for push and check box
                                                 //buttons...

        if (!hBrushButton)
            hBrushButton = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushButton ;

        break ;

    case WM_CTLCOLORSTATIC:

        SetTextColor((HDC) wParam, RGB(0x00, 0xff, 0x00)) ;  //green
        SetBkMode((HDC) wParam, TRANSPARENT) ;               //transparent text

        if (!hBrushDlg)
            hBrushDlg = GetStockObject(LTGRAY_BRUSH) ;

        return (UINT) hBrushDlg ;

        break ;

    default:
      break ;
  }

  return FALSE ;   //send msg to the common dialog code
}

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

  Function: PrintMultiProc1(LPDWORD)

  Purpose:  Is the starting address for the first new thread when
            multithreading.

  Returns:  Any DWORD value.

  Comments:
            The new thread will start executing here and will end when
            the PrintDlg() function returns and it has posted a message
            to the CDTEST's print dialog window saying that one of the
            two multithreading dialogs been terminated.

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

DWORD PrintMultiProc1(LPDWORD pdw)
{
  GetPrintDlg(hwndMainPrint, &pdThread1) ;

  DoPrintDlgStuff(hwndMainPrint, &pdThread1) ;

  PostMessage(hwndMainPrint, UMSG_DECREMENTDLGCOUNT, 0, 0L ) ;

  return 0L ;
}

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

  Function: PrintMultiProc2(LPDWORD)

  Purpose:  Is the starting address for the second new thread when
            multithreading.

  Returns:  Any DWORD value.

  Comments:
            The new thread will start executing here and will end when
            the PrintDlg() function returns and it has posted a message
            to the CDTEST's print dialog window saying that one of the
            two multithreading dialogs been terminated.

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

DWORD PrintMultiProc2(LPDWORD pdw)
{
  GetPrintDlg(hwndMainPrint, &pdThread2) ;

  DoPrintDlgStuff(hwndMainPrint, &pdThread2) ;

  PostMessage(hwndMainPrint, UMSG_DECREMENTDLGCOUNT, 0, 0L ) ;

  return 0L ;
}

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

  Function: MultiThreadPrintDlg(void)

  Purpose:  Is the starting address for the first new thread when
            multithreading.

  Returns:  Any DWORD value.

  Comments:
            The new thread will start executing here and will end when
            the PrintDlg() function returns and it has posted a message
            to the CDTEST's print dialog window saying that one of the
            two multithreading dialogs been terminated.

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

void MultiThreadPrintDlg(void)
{

  dwPrintThreadParm1 = dwPrintThreadParm2 = 0L ;

  if (!(hPrintThread1 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                     (LPTHREAD_START_ROUTINE) PrintMultiProc1,
                                     &dwPrintThreadParm1, CREATE_SUSPENDED, &dwPrintThreadID1)))

  {
    MessageBox(GetForegroundWindow(), TEXT("Error creating thread 1"), NULL,
               MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;

    nOpenDialogCount = 0 ;

    EnablePrintControls(hwndMainPrint, TRUE) ;

    return ;
  }

  if (!(hPrintThread2 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                     (LPTHREAD_START_ROUTINE) PrintMultiProc2,
                                     &dwPrintThreadParm2, CREATE_SUSPENDED, &dwPrintThreadID2)))
  {
    MessageBox(GetForegroundWindow(), TEXT("Error creating thread 2"), NULL,
               MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;

    nOpenDialogCount = 0 ;

    EnablePrintControls(hwndMainPrint, TRUE) ;

    return ;
  }

  ResumeThread(hPrintThread1) ;

  ResumeThread(hPrintThread2) ;

  return ;
}

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

  Function: EnablePrintControls(HWND, BOOL)

  Purpose:  Enable or disable CDTEST's print dialog's controls.

  Returns:  Nothing.

  Comments: This is necessary when CDTEST is multithreading.

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

void EnablePrintControls(HWND hwnd, BOOL bEnable)
{
  EnableWindow(GetDlgItem(hwnd, IDOK), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, IDCANCEL), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_RESETPRINT), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_MULTITHREADPRINT), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_EDITDEVMODE), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_EDITDEVNAMES), bEnable) ;
}

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

  Function: DoPrintDlgStuff(HWND, LPPRINTDLG)

  Purpose:  Calls PrintDlg() with the correct parameters.

  Returns:  Nothing.

  Comments:

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

void DoPrintDlgStuff(HWND hwnd, LPPRINTDLG ppd)
{
  BOOL bRet = FALSE ;

  /* Figure out how the user want's to call PrintDlg() */

  if (IsDlgButtonChecked(hwnd, ID_PREPRINTP) == 1)
    ppd->hPrintTemplate = GetPrintDlgHandle() ;

  if (IsDlgButtonChecked(hwnd, ID_PRESETUPP) == 1)
    ppd->hSetupTemplate = GetSetupDlgHandle() ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hPrintTemplate) ;
  SetDlgItemText(hwnd, ID_HPRINTTEMPP, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) ppd->hSetupTemplate) ;
  SetDlgItemText(hwnd, ID_HSETUPTEMPP, szTemp) ;

  /* Call the function */

  if (IsDlgButtonChecked(hwnd, ID_NULLSTRUCTP) == 1)
    bRet = PrintDlg((LPPRINTDLG) NULL) ;
  else
    bRet = PrintDlg(ppd) ;

  /* Clean up and show results */

  wsprintf(szTemp, szLongFilter, CommDlgExtendedError()) ;
  SetDlgItemText(hwnd, ID_ERRORP, szTemp) ;

  SetDlgItemInt(hwnd, ID_RETURNP, bRet, TRUE) ;

  if (hDialogPrint)
  {
    FreeResource(hDialogPrint) ; //obsolete call, but
    hDialogPrint = (HANDLE) 0 ;  //it's possible that this
    hResPrint = (HANDLE) 0 ;    //might be recompiled for win 3.x
  }

  pd.hDevMode = ppd->hDevMode ;    //need to fix this so that FillDevnamesDlg()
  pd.hDevNames = ppd->hDevNames ;  //and FillDevmodeDlg() work...

  FillPrintDlg(hwnd, ppd) ;

  if (ppd->hDC)  //release the HDC after its value is
                 //returned to the test app
  {
    ReleaseDC(ppd->hwndOwner, ppd->hDC) ;
    ppd->hDC = (HDC) 0 ;
  }
}


PRINT.H

void DoPrintDialog(HWND) ;

BOOL APIENTRY PrintProc(HWND, UINT, UINT, LONG) ;
BOOL APIENTRY DevnamesProc(HWND, UINT, UINT, LONG) ;
BOOL APIENTRY DevmodeProc(HWND, UINT, UINT, LONG) ;

#define ID_STRUCTSIZEP              119
#define ID_HWNDOWNERP               120
#define ID_HDCP                     123
#define ID_FLAGSP                   124
#define ID_FROMPAGEP                125
#define ID_TOPAGEP                  126
#define ID_MINPAGEP                 127
#define ID_MAXPAGEP                 128
#define ID_COPIESP                  129
#define ID_HINSTANCEP               130
#define ID_CUSTDATAP                131
#define ID_LPPRINTTEMPP             133
#define ID_LPSETUPTEMPP             134
#define ID_HPRINTTEMPP              135
#define ID_HSETUPTEMPP              136
#define ID_SZDEVMODEP               606
#define ID_SZDEVNAMESP              607
#define ID_ADD1                     610
#define ID_CLEAR1                   611
#define ID_ADD2                     612
#define ID_CLEAR2P                  613
#define ID_ADD1P                    614
#define ID_CLEAR1P                  615
#define ID_ADD2P                    616
#define ID_HDEVMODEP                608
#define ID_HDEVNAMESP               609
#define ID_ERRORP                   621
#define ID_RETURNP                  622
#define ID_EDITDEVMODE              617
#define ID_EDITDEVNAMES             618
#define ID_PRINTHOOKP               132
#define ID_SETUPHOOKP               624

#define ID_PRINTDIALOG              605
#define ID_RESETPRINT               625
#define ID_NULLSTRUCTP              626
#define ID_PRESETUPP                627
#define ID_PREPRINTP                628
#define ID_MULTITHREADPRINT         629


REPLACE.C

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

  File: replace.c

  Purpose:

    This file contains only one function that set the bDoFindDlg global
    variable to FALSE and then creates a dialog box with the same Callback
    function as the DoFindDialog() function does.

    Since FindText() and ReplaceText() both use the FINDREPLACE
    structure to create their dialogs, one dialog proc and dialog box
    can handle both.

  Functions:

    - DoReplaceDialog()    -- Creates CDTEST's Find/Replace dialog.

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

#include <windows.h>
#include <commdlg.h>
#include "cdtest.h"
#include "replace.h"
#include "find.h"

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

  Function: DoReplaceDialog(HWND)

  Purpose: To create CDTEST's find/replace dialog box.

  Returns: Nothing.

  Comments:

    FindText() and ReplaceText() are similiar enough so that
    the same dialog can be used to edit their creation structure elements,
    so a global variable "bDoFindDlg" keeps track of which one to create
    when the user clicks the OK or Multithread buttons...

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

void DoReplaceDialog(HWND hwnd)
{

  bDoFindDlg = FALSE ;

  /* in order for the keyboard to work, a hook has to be installed before
     the dialog is created.  See find.c for more detail on this. */

  hHook = SetWindowsHookEx(WH_MSGFILTER, lpfnFilterProc, hInst, GetCurrentThreadId()) ;

  if (!hHook) return ;

  DialogBox(hInst, MAKEINTRESOURCE(ID_FINDDIALOG), hwnd, FindProc) ;

  UnhookWindowsHookEx(hHook) ;

}


REPLACE.H

void DoReplaceDialog(HWND) ;


SAVE.C

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

  File: save.c

  Purpose:

    This file contains only one function that set the bDoOpenDlg global
    variable to FALSE and then creates a dialog box with the same Callback
    function as the DoOpenDialog() function does.

    Since GetOpenFileName() and GetSaveFileName() both use the
    OPENFILENAME structure to create their dialogs, one dialog
    proc and dialog box can handle both.

  Functions:

    - DoSaveDialog()    -- Creates CDTEST's Open/Save dialog.

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

#include <windows.h>
#include <commdlg.h>
#include "cdtest.h"
#include "save.h"
#include "open.h"

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

  Function: DoSaveDialog(HWND)

  Purpose: To create the GetOpenFileName() and GetSaveFileName()
           creation dialog.

  Returns: Nothing.

  Comments:

    GetOpenFileName() and GetSaveFileName() are similiar enough so that
    the same dialog can be used to edit their creation structure elements,
    so a global variable "bDoOpenDlg" keeps track of which one to create
    when the user clicks the OK or Multithread buttons...

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

void DoSaveDialog(HWND hwnd)
{
  bDoOpenDlg = FALSE ;

  DialogBox(hInst, MAKEINTRESOURCE(ID_OPENDIALOG), hwnd, OpenFunc) ;

}


SAVE.H

void DoSaveDialog(HWND) ;


TITLE.C

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

  File: title.c

  Purpose:

     Contains the functions that control CDTEST's GetFileTitle()
     dialog box.

  Functions:

    - DoTitleDialog()    -- Creates CDTEST's GetFileTitle() dialog.

    - TitleProc()        -- The callback function for CDTEST's
                            GetFileTitle() dialog box.

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

#include <windows.h>
#include <commdlg.h>
#include "cdtest.h"
#include "title.h"

extern UINT uMode ;                               //see cdtest.c
extern LONG MyAtol(LPTSTR, BOOL, LPBOOL) ;

#define MAXTITLE 256

TCHAR szTitle[MAXTITLE] ;
TCHAR szPath[MAXTITLE] ;
short nReturn ;
WORD wBufSize ;

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

  Function: DoTitleDialog(HWND)

  Purpose: Creates CDTEST's GetFileTitle() dialog box.

  Returns: Nothing.

  Comments:

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

void DoTitleDialog(HWND hwnd)
{
  DialogBox(hInst, MAKEINTRESOURCE(ID_TITLEDIALOG), hwnd, TitleProc) ;

}

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

  Function: TitleProc(HWND, UINT, UINT, LONG)

  Purpose: Is the callback function for CDTEST's GetFileTitle() dialog.

  Returns: TRUE or FALSE depending on the situation.

  Comments:

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

BOOL APIENTRY TitleProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  TCHAR szNum[30] ;
  BOOL b ;

  switch (msg)
  {
    case WM_INITDIALOG:
      SetDlgItemText(hwnd, ID_BUFFSIZETI, TEXT("256")) ;
      break ;

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
          case IDOK:
            GetDlgItemText(hwnd, ID_PATHTI, szPath, MAXTITLE) ;
            GetDlgItemText(hwnd, ID_BUFFSIZETI, szNum, 30) ;

            wBufSize =  (WORD) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

            if (wBufSize > 256)
            {
                wBufSize = 256 ;
                SetDlgItemText(hwnd, ID_BUFFSIZETI, TEXT("256")) ;
            }

            nReturn = GetFileTitle(szPath, szTitle, wBufSize) ;
            wsprintf(szTemp, szShortFilter, nReturn) ;
            SetDlgItemText(hwnd, ID_RETURNTI, szTemp) ;
            SetDlgItemText(hwnd, ID_TITLETI, szTitle) ;

            break ;

          case IDCANCEL:
            EndDialog(hwnd, FALSE) ;
            break ;

          case ID_RESETTITLE:
            szPath[0] = 0 ;
            szNum[0] = 0 ;
            szTemp[0] = 0 ;
            szTitle[0] = 0 ;
            SetDlgItemText(hwnd, ID_PATHTI, szPath) ;
            SetDlgItemText(hwnd, ID_RETURNTI, szTemp) ;
            SetDlgItemText(hwnd, ID_TITLETI, szTitle) ;
            SetDlgItemText(hwnd, ID_BUFFSIZETI, TEXT("256")) ;
            break ;

          default: break ;
        }
    }

    default: break ;
  }

  return FALSE ;
}


TITLE.H

void DoTitleDialog(HWND) ;

BOOL APIENTRY TitleProc(HWND, UINT, UINT, LONG) ;

#define ID_PATHTI                   105
#define ID_TITLETI                  106
#define ID_BUFFSIZETI               107
#define ID_RETURNTI                 108

#define ID_TITLEDIALOG              601
#define ID_RESETTITLE               603


README.TXT

Common Dialog Boxes

The COMDLG32 sample demonstrates how to use the file open common dialog
box. It shows how to create a hook procedure for GetOpenFileName.


COMDLG32.H

//---------------------------------------------------------------------------
//CMNDLG32.H
//---------------------------------------------------------------------------

#ifndef STRICT
#define STRICT
#endif

#ifdefRC_INVOKED
#include <winuser.h>
#else// RC_INVOKED
#include <windows.h>
#endif// RC_INVOKED
#include "resource.h"

// window ids
#define ID_EDITCHILD500

// string constants

#define IDS_DIALOGFAILURE     1
#define IDS_STRUCTSIZE        2
#define IDS_INITIALIZATION    3
#define IDS_NOTEMPLATE        4
#define IDS_NOHINSTANCE       5
#define IDS_LOADSTRFAILURE    6
#define IDS_FINDRESFAILURE    7
#define IDS_LOADRESFAILURE    8
#define IDS_LOCKRESFAILURE    9
#define IDS_MEMALLOCFAILURE  10
#define IDS_MEMLOCKFAILURE   11
#define IDS_NOHOOK           12
#define IDS_SETUPFAILURE     13
#define IDS_PARSEFAILURE     14
#define IDS_RETDEFFAILURE    15
#define IDS_LOADDRVFAILURE   16
#define IDS_GETDEVMODEFAIL   17
#define IDS_INITFAILURE      18
#define IDS_NODEVICES        19
#define IDS_NODEFAULTPRN     20
#define IDS_DNDMMISMATCH     21
#define IDS_CREATEICFAILURE  22
#define IDS_PRINTERNOTFOUND  23
#define IDS_NOFONTS          24
#define IDS_SUBCLASSFAILURE  25
#define IDS_INVALIDFILENAME  26
#define IDS_BUFFERTOOSMALL   27
#define IDS_FILTERSTRING     28
#define IDS_UNKNOWNERROR     29

// constants

#define MAX_PATH             260

// Function prototypes

// procs
long APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
BOOL APIENTRY About(HWND, UINT, UINT, LONG);
BOOL APIENTRY EnterNew(HWND, UINT, UINT, LONG);
BOOL CALLBACK ComDlg32DlgProc(HWND, UINT, WPARAM, LPARAM);

//functions
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);
BOOL OpenTheFile( HWND, HWND );
void ProcessCDError(DWORD, HWND);
BOOL NEAR PASCAL TestNotify(HWND, LPOFNOTIFY);


COMDLG32.C

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM:Comdlg32.c
//
//PURPOSE:Demonstrates how to manipulate the file open common dialog.
//
//PLATFORMS:Windows 95
//
//FUNCTIONS:
//WinMain() - calls initialization function, processes message loop
//InitApplication() - Initializes window data nd registers window
//InitInstance() -saves instance handle and creates main window
//MainWindProc() Processes messages
//About() - Process menssages for "About" dialog box
//      OpenTheFile() - Processes the File Open common dialog box
//      ComDlg32DlgProc() - Hook procedure for GetOpenFileName() common dialog
//TestNotify() - Processes the WM_NOTIFY function for ComDlg32DlgProc
//ProcessCDError() - uses CommonDialogExtendedError() to output useful error
//      messages
//
//SPECIAL INSTRUCTIONS: N/A
//

#include <windows.h>    // includes basic windows functionality
#include <commdlg.h>    // includes common dialog functionality
#include <dlgs.h>       // includes common dialog template defines
#include <cderr.h>      // includes the common dialog error codes
#include "COMDLG32.h"   // includes application-specific information

const char szmsgSHAREVIOLATION[] = SHAREVISTRING;  // string for sharing violation
const char szmsgFILEOK[]         = FILEOKSTRING;   // string for OK button
const char szCommdlgHelp[]       = HELPMSGSTRING;  // string for Help button

UINT cdmsgShareViolation = 0;  // identifier from RegisterWindowMessage
UINT cdmsgFileOK         = 0;  // identifier from RegisterWindowMessage
UINT cdmsgHelp           = 0;  // identifier from RegisterWindowMessage

typedef struct _MYDATA
{
char szTest1[80];// a test buffer containing the file selected
char szTest2[80];       // a test buffer containing the file path
} MYDATA, FAR * LPMYDATA;

HINSTANCE g_hInst;// the current instance
MYDATA sMyData;// an instance of a MYDATA

//
//  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//
//  PURPOSE: Entry point for the application.
//
//  COMMENTS:
//
//This function initializes the application and processes the
//message loop.
//
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow )
{

    MSG msg;

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

    // Create the main window.
    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    // Acquire and dispatch messages until a WM_QUIT message is received.
    while (GetMessage(&msg,NULL,0,0))
        {
            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:
//
//       In this function, we initialize a window class by filling out a data
//       structure of type WNDCLASS and calling the Windows RegisterClass()
//       function.
//
BOOL InitApplication(HANDLE hInstance)
{
    WNDCLASS  wc;

    // Register the window class for my window.                                                           */
    wc.style = 0;                       // Class style.
    wc.lpfnWndProc = (WNDPROC)MainWndProc; // Window procedure for 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 =  "Comdlg32Menu";   // Name of menu resource in .RC file.
    wc.lpszClassName = "Comdlg32WClass"; // Name used in call to CreateWindow.

    return (RegisterClass(&wc));
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance( HANDLE hInstance, int nCmdShow)
{
    HWND            hWnd;

// Save off the handle to the current instance.
    g_hInst = hInstance;

    // Create a main window for this application instance.
    hWnd = CreateWindow(
        "Comdlg32WClass",
        "Common Dialog Sample Application",
        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.
        g_hInst,                        // This instance owns this window.
        NULL                            // Pointer not needed.
    );

    // If window could not be created, return "failure".
    if (!hWnd)
        return (FALSE);

    // Register the window messages to receive.
    cdmsgShareViolation = RegisterWindowMessage(szmsgSHAREVIOLATION);
    cdmsgFileOK         = RegisterWindowMessage(szmsgFILEOK);
    cdmsgHelp           = RegisterWindowMessage(szCommdlgHelp);

    // Make the window visible; update its client area; and return "success".
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return (TRUE);

}

//
//  FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  MESSAGES:
//
//WM_CREATE - initialize window and create the MLE
//WM_COMMAND - process the application menu
//WM_SIZE - size the MLE in the main window
//WM_DESTROY - post a quit message and return
//
//
LONG APIENTRY MainWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam)
{
static HWND hwndEdit;
    CHAR lpszHello[] = "Choose File Open from the Options menu for a demo.";

    switch (message) {

        case WM_CREATE:
// Create an MLE for the file contents.
hwndEdit = CreateWindow(
                "EDIT",
NULL,
                WS_CHILD | WS_VISIBLE | WS_VSCROLL |
                    ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
                0, 0, 0, 0,
                hWnd,
                (HMENU) ID_EDITCHILD,
                (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE),
                NULL);

            // Update the MLE.
            SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM) lpszHello);
            return 0;

        case WM_SIZE:
            // Make the edit control the size of the window's client area.
            MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            return 0;

        case WM_COMMAND:           // message: command from application menu
            switch( LOWORD( wParam ))
            {
                case IDM_FILEOPEN:
// Call the FileOpen common dialog to open the file.
                    OpenTheFile( hWnd, hwndEdit );
                    break;

                case IDM_EXIT:
                    PostQuitMessage(0);
                    break;

                case IDM_ABOUT:
                    DialogBox(g_hInst,
                        MAKEINTRESOURCE(IDD_ABOUT),
                        hWnd,
                        (DLGPROC)About);
                    break;

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

            }
            break;

        case WM_DESTROY:                  // message: window being destroyed
            PostQuitMessage(0);
            break;

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

//
//   FUNCTION: OpenTheFile(HWND hwnd, HWND hwndEdit)
//
//   PURPOSE: Invokes common dialog function to open a file and opens it.
//
//   COMMENTS:
//
//This function initializes the OPENFILENAME structure and calls
//            the GetOpenFileName() common dialog function.
//
//    RETURN VALUES:
//        TRUE - The file was opened successfully and read into the buffer.
//        FALSE - No files were opened.
//
//
BOOL OpenTheFile( HWND hWnd, HWND hWndEdit )
{
    HANDLE hFile;
    DWORD dwBytesRead;
DWORD dwFileSize;
OPENFILENAME OpenFileName;
TCHAR         szFile[MAX_PATH]      = "\0";
char *lpBufPtr;

    strcpy( szFile, "");

// Fill in the OPENFILENAME structure to support a template and hook.
OpenFileName.lStructSize       = sizeof(OPENFILENAME);
    OpenFileName.hwndOwner         = hWnd;
    OpenFileName.hInstance         = g_hInst;
    OpenFileName.lpstrFilter       = NULL;
    OpenFileName.lpstrCustomFilter = NULL;
    OpenFileName.nMaxCustFilter    = 0;
    OpenFileName.nFilterIndex      = 0;
    OpenFileName.lpstrFile         = szFile;
    OpenFileName.nMaxFile          = sizeof(szFile);
    OpenFileName.lpstrFileTitle    = NULL;
    OpenFileName.nMaxFileTitle     = 0;
    OpenFileName.lpstrInitialDir   = NULL;
    OpenFileName.lpstrTitle        = "Open a File";
    OpenFileName.nFileOffset       = 0;
    OpenFileName.nFileExtension    = 0;
    OpenFileName.lpstrDefExt       = NULL;
    OpenFileName.lCustData         = (LPARAM)&sMyData;
OpenFileName.lpfnHook    = ComDlg32DlgProc;
OpenFileName.lpTemplateName    = MAKEINTRESOURCE(IDD_COMDLG32);
    OpenFileName.Flags             = OFN_SHOWHELP | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLETEMPLATE;

// Call the common dialog function.
    if (GetOpenFileName(&OpenFileName))
    {
// Open the file.
if ((hFile = CreateFile((LPCTSTR)OpenFileName.lpstrFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL)) == (HANDLE)-1)
{
    MessageBox( hWnd, "File open failed.", NULL, MB_OK );
    return FALSE;
}

// Get the size of the file.
dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize == 0xFFFFFFFF)
{
    MessageBox( NULL, "GetFileSize failed!", NULL, MB_OK);
    return FALSE;
}

// Allocate a buffer for the file to be read into.
lpBufPtr = (char *)GlobalAlloc( GMEM_FIXED, dwFileSize );
if (lpBufPtr == NULL)
{
    MessageBox( NULL, "GlobalAlloc failed!", NULL, MB_OK);
    CloseHandle( hFile );
    return FALSE;
}

// Read it's contents into a buffer.
ReadFile(hFile,(LPVOID)lpBufPtr, dwFileSize, &dwBytesRead, NULL);

if (dwBytesRead == 0)
{
    MessageBox( hWnd, "Zero bytes read.", NULL, MB_OK );
    return FALSE;
}

        // Update the MLE with the file contents.
        SendMessage(hWndEdit, WM_SETTEXT, 0, (LPARAM) lpBufPtr);

// Close the file.
CloseHandle(hFile);

return TRUE;
    }
    else
   {
ProcessCDError(CommDlgExtendedError(), hWnd );
return FALSE;
    }

}

//
//   FUNCTION: TestNotify( HWND hDlg, LPOFNOTIFY pofn)
//
//  PURPOSE:  Processes the WM_NOTIFY message notifications that is sent
//    to the hook dialog procedure for the File Open common dialog.
//
//
BOOL NEAR PASCAL TestNotify(HWND hDlg, LPOFNOTIFY pofn)
{
switch (pofn->hdr.code)
{
// The selection has changed.
case CDN_SELCHANGE:
{
char szFile[MAX_PATH];

// Get the file specification from the common dialog.
if (CommDlg_OpenSave_GetSpec(GetParent(hDlg),
szFile, sizeof(szFile)) <= sizeof(szFile))
{
// Set the dialog item to reflect this.
SetDlgItemText(hDlg, IDE_SELECTED, szFile);
}

// Get the path of the selected file.
if (CommDlg_OpenSave_GetFilePath(GetParent(hDlg),
szFile, sizeof(szFile)) <= sizeof(szFile))
{
// Display this path in the appropriate box.
SetDlgItemText(hDlg, IDE_PATH, szFile);
}
}
break;

// A new folder has been opened.
case CDN_FOLDERCHANGE:
{
char szFile[MAX_PATH];

if (CommDlg_OpenSave_GetFolderPath(GetParent(hDlg),
szFile, sizeof(szFile)) <= sizeof(szFile))
{
// Display this new path in the appropriate box.
SetDlgItemText(hDlg, IDE_SELECTED, szFile);
}
}
break;

// The "Help" pushbutton has been pressed.
case CDN_HELP:
MessageBox(hDlg, "Got the Help button notify.", "ComDlg32 Test", MB_OK);
break;

// The 'OK' pushbutton has been pressed.
case CDN_FILEOK:
// Update the appropriate box.
SetDlgItemText(hDlg,IDE_SELECTED, pofn->lpOFN->lpstrFile);
SetWindowLong(hDlg, DWL_MSGRESULT, 1L);
break;

// Received a sharing violation.
case CDN_SHAREVIOLATION:
// Update the appropriate box.
SetDlgItemText(hDlg, IDE_SELECTED, pofn->pszFile);
MessageBox(hDlg, "Got a sharing violation notify.", "ComDlg32 Test", MB_OK);
break;
}

return(TRUE);
}

//
//   FUNCTION: ComDlg32DlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//  PURPOSE:  Processes messages for the File Open common dialog box.
//
//    MESSAGES:
//
//WM_INITDIALOG - save pointer to the OPENFILENAME structure in User data
//WM_DESTROY - get the text entered and fill in the MyData structure
//WM_NOTIFY - pass this message onto the TestNotify function
//default - check for a sharing violation or the OK button and
//    display a message box.
//
//
BOOL CALLBACK ComDlg32DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

switch (uMsg)
{
case WM_INITDIALOG:
// Save off the long pointer to the OPENFILENAME structure.
SetWindowLong(hDlg, DWL_USER, lParam);
break;

case WM_DESTROY:
{
LPOPENFILENAME lpOFN = (LPOPENFILENAME)GetWindowLong(hDlg, DWL_USER);
LPMYDATA psMyData = (LPMYDATA)lpOFN->lCustData;

GetDlgItemText(hDlg, IDE_PATH, psMyData->szTest1, sizeof(psMyData->szTest1));
GetDlgItemText(hDlg, IDE_SELECTED, psMyData->szTest2, sizeof(psMyData->szTest2));
}
break;

case WM_NOTIFY:
TestNotify(hDlg, (LPOFNOTIFY)lParam);

default:
if (uMsg == cdmsgFileOK)
{
SetDlgItemText(hDlg, IDE_SELECTED, ((LPOPENFILENAME)lParam)->lpstrFile);
if (MessageBox(hDlg, "Got the OK button message.\n\nShould I open it?", "ComDlg32 Test", MB_YESNO)
== IDNO)
{
SetWindowLong(hDlg, DWL_MSGRESULT, 1L);
}
break;
}
else if (uMsg == cdmsgShareViolation)
{
SetDlgItemText(hDlg, IDE_SELECTED, (LPSTR)lParam);
MessageBox(hDlg, "Got a sharing violation message.", "ComDlg32 Test", MB_OK);
break;
}
return FALSE;
}
return TRUE;
}

//
//  FUNCTION: About(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for "About" dialog box
//
//  MESSAGES:
//
//WM_INITDIALOG - initialize dialog box
//WM_COMMAND    - Input received
//
//
BOOL APIENTRY About( HWND hDlg, UINT message, UINT wParam, LONG lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            return (TRUE);

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

}

//
//  FUNCTION: ProcessCDError(DWORD)
//
//  PURPOSE: Processes errors from the common dialog functions.
//
//  COMMENTS:
//
//        This function is called whenever a common dialog function
//        fails.  The CommonDialogExtendedError() value is passed to
//        the function which maps the error value to a string table.
//        The string is loaded and displayed for the user.
//
//
void ProcessCDError(DWORD dwErrorCode, HWND hWnd)
{
   WORD  wStringID;
   TCHAR  buf[MAX_PATH];

   switch(dwErrorCode)
      {
 case CDERR_DIALOGFAILURE:   wStringID=IDS_DIALOGFAILURE;   break;
 case CDERR_STRUCTSIZE:      wStringID=IDS_STRUCTSIZE;      break;
 case CDERR_INITIALIZATION:  wStringID=IDS_INITIALIZATION;  break;
 case CDERR_NOTEMPLATE:      wStringID=IDS_NOTEMPLATE;      break;
 case CDERR_NOHINSTANCE:     wStringID=IDS_NOHINSTANCE;     break;
 case CDERR_LOADSTRFAILURE:  wStringID=IDS_LOADSTRFAILURE;  break;
 case CDERR_FINDRESFAILURE:  wStringID=IDS_FINDRESFAILURE;  break;
 case CDERR_LOADRESFAILURE:  wStringID=IDS_LOADRESFAILURE;  break;
 case CDERR_LOCKRESFAILURE:  wStringID=IDS_LOCKRESFAILURE;  break;
 case CDERR_MEMALLOCFAILURE: wStringID=IDS_MEMALLOCFAILURE; break;
 case CDERR_MEMLOCKFAILURE:  wStringID=IDS_MEMLOCKFAILURE;  break;
 case CDERR_NOHOOK:          wStringID=IDS_NOHOOK;          break;
 case PDERR_SETUPFAILURE:    wStringID=IDS_SETUPFAILURE;    break;
 case PDERR_PARSEFAILURE:    wStringID=IDS_PARSEFAILURE;    break;
 case PDERR_RETDEFFAILURE:   wStringID=IDS_RETDEFFAILURE;   break;
 case PDERR_LOADDRVFAILURE:  wStringID=IDS_LOADDRVFAILURE;  break;
 case PDERR_GETDEVMODEFAIL:  wStringID=IDS_GETDEVMODEFAIL;  break;
 case PDERR_INITFAILURE:     wStringID=IDS_INITFAILURE;     break;
 case PDERR_NODEVICES:       wStringID=IDS_NODEVICES;       break;
 case PDERR_NODEFAULTPRN:    wStringID=IDS_NODEFAULTPRN;    break;
 case PDERR_DNDMMISMATCH:    wStringID=IDS_DNDMMISMATCH;    break;
 case PDERR_CREATEICFAILURE: wStringID=IDS_CREATEICFAILURE; break;
 case PDERR_PRINTERNOTFOUND: wStringID=IDS_PRINTERNOTFOUND; break;
 case CFERR_NOFONTS:         wStringID=IDS_NOFONTS;         break;
 case FNERR_SUBCLASSFAILURE: wStringID=IDS_SUBCLASSFAILURE; break;
 case FNERR_INVALIDFILENAME: wStringID=IDS_INVALIDFILENAME; break;
 case FNERR_BUFFERTOOSMALL:  wStringID=IDS_BUFFERTOOSMALL;  break;

 case 0:   //User may have hit CANCEL or we got a *very* random error
    return;

 default:
    wStringID=IDS_UNKNOWNERROR;
      }

   LoadString(NULL, wStringID, buf, sizeof(buf));
   MessageBox(hWnd, buf, NULL, MB_OK);
   return;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by comdlg32.RC
//
#define IDS_DIALOGFAILURE               1
#define IDS_STRUCTSIZE                  2
#define IDS_INITIALIZATION              3
#define IDS_NOTEMPLATE                  4
#define IDS_NOHINSTANCE                 5
#define IDS_LOADSTRFAILURE              6
#define IDS_FINDRESFAILURE              7
#define IDS_LOADRESFAILURE              8
#define IDS_LOCKRESFAILURE              9
#define IDS_MEMALLOCFAILURE             10
#define IDS_MEMLOCKFAILURE              11
#define IDS_NOHOOK                      12
#define IDS_SETUPFAILURE                13
#define IDS_PARSEFAILURE                14
#define IDS_RETDEFFAILURE               15
#define IDS_LOADDRVFAILURE              16
#define IDS_GETDEVMODEFAIL              17
#define IDS_INITFAILURE                 18
#define IDS_NODEVICES                   19
#define IDS_NODEFAULTPRN                20
#define IDS_DNDMMISMATCH                21
#define IDS_CREATEICFAILURE             22
#define IDS_PRINTERNOTFOUND             23
#define IDS_NOFONTS                     24
#define IDS_SUBCLASSFAILURE             25
#define IDS_INVALIDFILENAME             26
#define IDS_BUFFERTOOSMALL              27
#define IDS_FILTERSTRING                28
#define IDS_UNKNOWNERROR                29
#define IDD_COMDLG32                    100
#define IDE_PATH                        102
#define IDM_FILEOPEN                    106
#define IDM_EXIT                        107
#define IDM_ABOUT                       108
#define IDD_ABOUT                       113
#define IDE_SELECTED                    1022
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        114
#define _APS_NEXT_COMMAND_VALUE         109
#define _APS_NEXT_CONTROL_VALUE         1023
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

RichEdit Test Program

The REITP sample demonstrates the main features of a RichEdit control. It is
also a good replacement for the NotePad applet.


COMMON.H

/*
 * COMMON.H
 *
 * Structures and definitions applicable to all OLE 2.0 UI dialogs.
 *
 * Copyright (c)1992-1996 Microsoft Corporation, All Right Reserved
 */

#ifndef _COMMON_H_
#define _COMMON_H_

#include "ansiapi.h"

//Structure for label and source extraction from a metafile
typedef struct tagLABELEXTRACT
   {
   LPOLESTR    lpsz;
   UINT        Index;      // index in lpsz (so we can retrieve 2+ lines)
   DWORD       PrevIndex;  // index of last line (so we can mimic word wrap)

   union
      {
      UINT    cch;        //Length of label for label extraction
      UINT    iIcon;      //Index of icon in source extraction.
      } u;

   //For internal use in enum procs
   BOOL        fFoundIconOnly;
   BOOL        fFoundSource;
   BOOL        fFoundIndex;
   } LABELEXTRACT, FAR * LPLABELEXTRACT;

//Structure for extracting icons from a metafile (CreateIcon parameters)
typedef struct tagICONEXTRACT
   {
   HICON       hIcon;          //Icon created in the enumeration proc.

   /*
    * Since we want to handle multitasking well we have the caller
    * of the enumeration proc instantiate these variables instead of
    * using statics in the enum proc (which would be bad).
    */
   BOOL        fAND;
   HGLOBAL     hMemAND;        //Enumeration proc allocates and copies
   } ICONEXTRACT, FAR * LPICONEXTRACT;

//Structure to use to pass info to EnumMetafileDraw
typedef struct tagDRAWINFO
   {
   RECT     Rect;
   BOOL     fIconOnly;
   } DRAWINFO, FAR * LPDRAWINFO;

int CALLBACK EXPORT EnumMetafileIconDraw(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPARAM);
int CALLBACK EXPORT EnumMetafileExtractLabel(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPLABELEXTRACT);
int CALLBACK EXPORT EnumMetafileExtractIcon(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPICONEXTRACT);
int CALLBACK EXPORT EnumMetafileExtractIconSource(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPLABELEXTRACT);

//Shared globals:  our instance and clipboard formats used by the PasteSpecial dialog

extern HINSTANCE  ghInst;

extern UINT       cfObjectDescriptor;
extern UINT       cfLinkSrcDescriptor;
extern UINT       cfEmbedSource;
extern UINT       cfEmbeddedObject;
extern UINT       cfLinkSource;
extern UINT       cfOwnerLink;
extern UINT       cfFileName;

#endif //_COMMON_H_


DBUGIT.H

/*
 *DBUGIT.H
 *
 *Debug Support Routines
 */

#ifndef _DBUGIT_
#define _DBUGIT_

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/*
 *"Normal" assertion checking.  Provided for compatibility with
 *imported code.
 *
 *Assert(a)Displays a message indicating the file and line number
 *of this Assert() if a == 0.
 *AssertSz(a,b)As Assert(); also displays the message b (which should
 *be a string literal.)
 *SideAssert(a)As Assert(); the expression a is evaluated even if
 *asserts are disabled.
 */
#undef AssertSz
#undef Assert

#defineASSERTDATA

#define AssertSz(a,b)
#define Assert(a)
#define SideAssert(a)(a)

#define NYI(a)if (1) { \
MessageBoxA(NULL, "Not Yet Implemented: "##a, "Microsoft Mail", MB_ICONINFORMATION|MB_OK|MB_TASKMODAL); \
} else

#if 0
/*
 *Tracing Access Group.  Trace points
 *and asserts are divided into groups sharing a single TAG.
 *The user interface and this API both provide ways of
 *enabling or disabling (at run time) a TAG group.
 *
 */
typedefint TAG;

#define tagNull((TAG) 0)

/*
 *TraceTags
 */

// System related
#define tagCOM((TAG) 1)
#define tagDISK((TAG) 2)
#define tagERROR((TAG) 3)
#define tagErrDbgBrk((TAG) 4)

// Memory

// VLB
#define tagLbx((TAG) 10)
#define tagLbxRoutines((TAG) 11)
#define tagLbxThumbing   ((TAG) 12)
#define tagLbxOtherRoutines((TAG) 13)
#define tagLbxStore   ((TAG) 14)
#define tagLbxFixItem   ((TAG) 15)
#define tagLbxRender   ((TAG) 16)
#define tagLbxEventRoutines((TAG) 17)
#define tagLbxNotify   ((TAG) 18)
#define tagLbxDragDrop   ((TAG) 19)
#define tagLbxWndProc   ((TAG) 20)
#define tagLbxError((TAG) 73)

// MAPID
#define tagMapidlg((TAG) 21)
#define tagAB((TAG) 22)
#define tagDetails((TAG) 23)
#define tagABVlbCbc((TAG) 24)
#define tagTripole((TAG) 25)
#define tagTripoleStub((TAG) 26)
#define tagMailExt((TAG) 27)
#define tagMemVerbose((TAG) 28)
#define tagCapHelp((TAG) 72)
#define tagToolbar((TAG) 79)

// MAPIN
#define tagNoteLaunchDebug((TAG) 29)
#define tagMapiNote((TAG) 30)
#define tagNoteDlg((TAG) 31)
#define tagNoteCmd((TAG) 32)
#define tagNoteOle((TAG) 33)
#define tagPrint((TAG) 34)
#define tagFileAttach((TAG) 35)
#define tagFileAttachStub((TAG) 36)
#define tagCharFormat((TAG) 37)
#define tagAttach((TAG) 38)
#define tagStmILB((TAG) 39)
#define tagSaveAs((TAG) 40)
#define tagMsgAttach((TAG) 41)
#define tagMsgAttachStub((TAG) 42)
#define tagAttachStg((TAG) 43)
#define tagSpell((TAG) 44)
#define tagSpellV((TAG) 45)
#define tagRTFSync((TAG) 46)
#define tagEditOnlyText((TAG) 47)
#define tagRTFComp((TAG) 48)
#define tagMCacheTrace((TAG) 49)
#define tagMCacheData((TAG) 50)
#define tagFreeDoc((TAG) 51)
#define tagFDSite((TAG) 52)
#define tagNoteDataObj((TAG) 53)
#define tagReplyForward((TAG) 54)

// MAPIN - DBCS related
#define tagDBCS((TAG) 55)
#define tagFEToolbar((TAG) 56)
#define tagIME((TAG) 57)

// MLVIEW
#define tagMailView((TAG) 58)
#define tagMailVD((TAG) 59)
#define tagMailVDPaint((TAG) 60)
#define tagMlvbr((TAG) 61)
#define tagFcext((TAG) 62)
#define tagFcextRefc((TAG) 63)
#define tagHmt((TAG) 64)
#define tagTmt((TAG) 65)
#define tagTmtn((TAG) 66)
#define tagDragDrop((TAG) 67)
#define tagSRestrictions((TAG) 68)
#define tagMsmail((TAG) 69)
#define tagFormCache((TAG) 83)
#define tagConversation((TAG) 84)

// MLFIND
#define tagFinder((TAG) 70)
#define tagCreateInIPM((TAG) 71)

// Debugging
#define tagPeterDur((TAG) 74)
#define tagForms((TAG) 75)
#define tagCentral((TAG) 76)
#define tagMl3ext((TAG) 77)
#define tagEricWong((TAG) 78)
#define tagWmsuiRef((TAG) 80)
#define tagSession((TAG) 81)
#define tagDateTime((TAG) 82)

// Other
#define tagUserMin((TAG) 85)

#define tagMax((TAG) 1024)
#endif // 0

#define TraceTag;/##/
#define TraceString(_sz)
#define TraceError(_sz, _sc)
#define TraceTagString(_tag, _sz)

VOID WINAPIDebugBreak2(VOID);

#ifdef __cplusplus
}
#endif  /* __cplusplus */

#endif /* !_DBUGIT_ */


DRAWICON.C

/*
 * DRAWICON.C
 *
 * Functions to handle creation of metafiles with icons and labels
 * as well as functions to draw such metafiles with or without the label.
 *
 * The metafile is created with a comment that marks the records containing
 * the label code.  Drawing the metafile enumerates the records, draws
 * all records up to that point, then decides to either skip the label
 * or draw it.
 *
 * Copyright (c)1992-1996 Microsoft Corporation, All Right Reserved
 */

#define STRICT  1
#include "olestd.h"
#include "common.h"
#include "utility.h"

/*
 * Strings for metafile comments.  KEEP THESE IN SYNC WITH THE
 * STRINGS IN GETICON.C.
 */

static char szIconOnly[]="IconOnly";        //Where to stop to exclude label.

/*
 * OleUIMetafilePictIconFree
 *
 * Purpose:
 *  Deletes the metafile contained in a METAFILEPICT structure and
 *  frees the memory for the structure itself.
 *
 * Parameters:
 *  hMetaPict       HGLOBAL metafilepict structure created in
 *                  OleUIMetafilePictFromIconAndLabel
 *
 * Return Value:
 *  None
 */

STDAPI_(void) OleUIMetafilePictIconFree(HGLOBAL hMetaPict)
   {
   LPMETAFILEPICT      pMF;

   if (NULL==hMetaPict)
      return;

   pMF=(LPMETAFILEPICT)GlobalLock(hMetaPict);

   if (NULL!=pMF)
      {
      if (NULL!=pMF->hMF)
         DeleteMetaFile(pMF->hMF);
      }

   GlobalUnlock(hMetaPict);
   GlobalFree(hMetaPict);
   return;
   }

/*
 * OleUIMetafilePictIconDraw
 *
 * Purpose:
 *  Draws the metafile from OleUIMetafilePictFromIconAndLabel, either with
 *  the label or without.
 *
 * Parameters:
 *  hDC             HDC on which to draw.
 *  pRect           LPRECT in which to draw the metafile.
 *  hMetaPict       HGLOBAL to the METAFILEPICT from
 *                  OleUIMetafilePictFromIconAndLabel
 *  fIconOnly       BOOL specifying to draw the label or not.
 *
 * Return Value:
 *  BOOL            TRUE if the function is successful, FALSE if the
 *                  given metafilepict is invalid.
 */

STDAPI_(BOOL) OleUIMetafilePictIconDraw(HDC hDC, LPRECT pRect, HGLOBAL hMetaPict
                             , BOOL fIconOnly)
   {
   LPMETAFILEPICT  pMF;
   DRAWINFO        di;
   int             cx, cy;
   SIZE            size;
   POINT           point;

   if (NULL==hMetaPict)
      return FALSE;

   pMF=GlobalLock(hMetaPict);

   if (NULL==pMF)
      return FALSE;

   di.Rect = *pRect;
   di.fIconOnly = fIconOnly;

   //Transform to back to pixels
   cx=XformWidthInHimetricToPixels(hDC, pMF->xExt);
   cy=XformHeightInHimetricToPixels(hDC, pMF->yExt);

   SaveDC(hDC);

   SetMapMode(hDC, pMF->mm);
   SetViewportOrgEx(hDC, (pRect->right - cx) / 2, 0, &point);

   SetViewportExtEx(hDC, min ((pRect->right - cx) / 2 + cx, cx), cy, &size);

   if (fIconOnly)
      {
      // Since we've used the __export keyword on the
      // EnumMetafileIconDraw proc, we do not need to use
      // MakeProcInstance
      EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileIconDraw
         , (LPARAM)(LPDRAWINFO)&di);
      }
   else
      PlayMetaFile(hDC, pMF->hMF);

   RestoreDC(hDC, -1);

   GlobalUnlock(hMetaPict);
   return TRUE;
   }

/*
 * EnumMetafileIconDraw
 *
 * Purpose:
 *  EnumMetaFile callback function that draws either the icon only or
 *  the icon and label depending on given flags.
 *
 * Parameters:
 *  hDC             HDC into which the metafile should be played.
 *  phTable         HANDLETABLE FAR * providing handles selected into the DC.
 *  pMFR            METARECORD FAR * giving the enumerated record.
 *  lParam          LPARAM flags passed in EnumMetaFile.
 *
 * Return Value:
 *  int             0 to stop enumeration, 1 to continue.
 */

int CALLBACK EXPORT EnumMetafileIconDraw(HDC hDC, HANDLETABLE FAR *phTable
   , METARECORD FAR *pMFR, int cObj, LPARAM lParam)
   {
   LPDRAWINFO lpdi = (LPDRAWINFO)lParam;

   /*
    * We play everything blindly except for DIBBITBLT (or DIBSTRETCHBLT)
    * and ESCAPE with MFCOMMENT.  For the BitBlts we change the x,y to
    * draw at (0,0) instead of wherever it was written to draw.  The
    * comment tells us there to stop if we don't want to draw the label.
    */

   //If we're playing icon only, stop enumeration at the comment.
   if (lpdi->fIconOnly)
      {
      if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0])
         {
         if (0==lstrcmpi(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
            return 0;
         }

      /*
       * Check for the records in which we want to munge the coordinates.
       * destX is offset 6 for BitBlt, offset 9 for StretchBlt, either of
       * which may appear in the metafile.
       */
      if (META_DIBBITBLT==pMFR->rdFunction)
         pMFR->rdParm[6]=0;

      if (META_DIBSTRETCHBLT==pMFR->rdFunction)
           pMFR->rdParm[9] = 0;

      }

   PlayMetaFileRecord(hDC, phTable, pMFR, cObj);
   return 1;
   }

/*
 * OleUIMetafilePictExtractLabel
 *
 * Purpose:
 *  Retrieves the label string from metafile representation of an icon.
 *
 * Parameters:
 *  hMetaPict       HGLOBAL to the METAFILEPICT containing the metafile.
 *  lpszLabel       LPSTR in which to store the label.
 *  cchLabel        UINT length of lpszLabel.
 *  lpWrapIndex     DWORD index of first character in last line. Can be NULL
 *                  if calling function doesn't care about word wrap.
 *
 * Return Value:
 *  UINT            Number of characters copied.
 */
STDAPI_(UINT) OleUIMetafilePictExtractLabel(HGLOBAL hMetaPict, LPOLESTR lpszLabel
                                , UINT cchLabel, LPDWORD lpWrapIndex)
   {
   LPMETAFILEPICT  pMF;
   LABELEXTRACT    le;
   HDC             hDC;

   /*
    * We extract the label by getting a screen DC and walking the metafile
    * records until we see the ExtTextOut record we put there.  That
    * record will have the string embedded in it which we then copy out.
    */

   if (NULL==hMetaPict || NULL==lpszLabel || 0==cchLabel)
      return FALSE;

   pMF=GlobalLock(hMetaPict);

   if (NULL==pMF)
      return FALSE;

   le.lpsz=lpszLabel;
   le.u.cch=cchLabel;
   le.Index=0;
   le.fFoundIconOnly=FALSE;
   le.fFoundSource=FALSE;  //Unused for this function.
   le.fFoundIndex=FALSE;   //Unused for this function.
   le.PrevIndex = 0;

   //Use a screen DC so we have something valid to pass in.
   hDC=GetDC(NULL);

   // Since we've used the EXPORT keyword on the
   // EnumMetafileExtractLabel proc, we do not need to use
   // MakeProcInstance

   EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractLabel, (LONG)(LPLABELEXTRACT)&le);

   ReleaseDC(NULL, hDC);

   GlobalUnlock(hMetaPict);

   //Tell where we wrapped (if calling function cares)
   if (NULL != lpWrapIndex)
      *lpWrapIndex = le.PrevIndex;

   //Return amount of text copied
   return le.u.cch;
   }

/*
 * EnumMetafileExtractLabel
 *
 * Purpose:
 *  EnumMetaFile callback function that walks a metafile looking for
 *  ExtTextOut, then concatenates the text from each one into a buffer
 *  in lParam.
 *
 * Parameters:
 *  hDC             HDC into which the metafile should be played.
 *  phTable         HANDLETABLE FAR * providing handles selected into the DC.
 *  pMFR            METARECORD FAR * giving the enumerated record.
 *  pLE             LPLABELEXTRACT providing the destination buffer and length.
 *
 * Return Value:
 *  int             0 to stop enumeration, 1 to continue.
 */

int CALLBACK EXPORT EnumMetafileExtractLabel(HDC hDC, HANDLETABLE FAR *phTable
   , METARECORD FAR *pMFR, int cObj, LPLABELEXTRACT pLE)
   {

   /*
    * We don't allow anything to happen until we see "IconOnly"
    * in an MFCOMMENT that is used to enable everything else.
    */
   if (!pLE->fFoundIconOnly)
      {
      if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0])
         {
         if (0==lstrcmpi(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
            pLE->fFoundIconOnly=TRUE;
         }

      return 1;
      }

   //Enumerate all records looking for META_EXTTEXTOUT - there can be more
   //than one.
   if (META_EXTTEXTOUT==pMFR->rdFunction)
      {
      UINT        cchMax;
      LPOLESTR    lpszTemp;

       /*
       * If ExtTextOut has NULL fuOptions, then the rectangle is omitted
       * from the record, and the string starts at rdParm[4].  If
       * fuOptions is non-NULL, then the string starts at rdParm[8]
       * (since the rectange takes up four WORDs in the array).  In
       * both cases, the string continues for (rdParm[2]+1) >> 1
       * words.  We just cast a pointer to rdParm[8] to an LPSTR and
       * lstrcpyn into the buffer we were given.
       *
       * Note that we use element 8 in rdParm instead of 4 because we
       * passed ETO_CLIPPED in for the options on ExtTextOut--docs say
       * [4] which is rect doesn't exist if we passed zero there.
       *
       */

      cchMax=min(pLE->u.cch - pLE->Index, (UINT)pMFR->rdParm[2]);
      lpszTemp = /*(LPOLESTR)*/((/*(LPSTR)*/pLE->lpsz) + pLE->Index);

      A2W ((LPSTR)&(pMFR->rdParm[8]), lpszTemp, cchMax);
      lpszTemp[cchMax]='\0';

      pLE->u.cch = OLESTRLEN (pLE->lpsz);
      pLE->PrevIndex = pLE->Index;
      pLE->Index += cchMax;
      }

   return 1;
   }

/*
 * OleUIMetafilePictExtractIcon
 *
 * Purpose:
 *  Retrieves the icon from metafile into which DrawIcon was done before.
 *
 * Parameters:
 *  hMetaPict       HGLOBAL to the METAFILEPICT containing the metafile.
 *
 * Return Value:
 *  HICON           Icon recreated from the data in the metafile.
 */
STDAPI_(HICON) OleUIMetafilePictExtractIcon(HGLOBAL hMetaPict)
   {
   LPMETAFILEPICT  pMF;
   HDC             hDC;
   ICONEXTRACT     ie;

   /*
    * We extract the label by getting a screen DC and walking the metafile
    * records until we see the ExtTextOut record we put there.  That
    * record will have the string embedded in it which we then copy out.
    */

   if (NULL==hMetaPict)
      return NULL;

   pMF=GlobalLock(hMetaPict);

   if (NULL==pMF)
      return FALSE;

   //Use a screen DC so we have something valid to pass in.
   hDC=GetDC(NULL);
   ie.fAND=TRUE;

   // We get information back in the ICONEXTRACT structure.
   // (Since we've used the EXPORT keyword on the
   // EnumMetafileExtractLabel proc, we do not need to use
   // MakeProcInstance)
   EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIcon, (LONG)(LPICONEXTRACT)&ie);

   ReleaseDC(NULL, hDC);
   GlobalUnlock(hMetaPict);

   return ie.hIcon;
   }

/*
 * EnumMetafileExtractIcon
 *
 * Purpose:
 *  EnumMetaFile callback function that walks a metafile looking for
 *  StretchBlt (3.1) and BitBlt (3.0) records.  We expect to see two
 *  of them, the first being the AND mask and the second being the XOR
 *  data.  We
 *  ExtTextOut, then copies the text into a buffer in lParam.
 *
 * Parameters:
 *  hDC             HDC into which the metafile should be played.
 *  phTable         HANDLETABLE FAR * providing handles selected into the DC.
 *  pMFR            METARECORD FAR * giving the enumerated record.
 *  pIE             LPICONEXTRACT providing the destination buffer and length.
 *
 * Return Value:
 *  int             0 to stop enumeration, 1 to continue.
 */

int CALLBACK EXPORT EnumMetafileExtractIcon(HDC hDC, HANDLETABLE FAR *phTable
   , METARECORD FAR *pMFR, int cObj, LPICONEXTRACT pIE)
   {
   LPBITMAPINFO        lpBI;
   LPBITMAPINFOHEADER  lpBH;
   LPBYTE              lpbSrc;
   LPBYTE              lpbDst;
   UINT                uWidth, uHeight;
   DWORD               cb;
   HGLOBAL             hMem;
   BITMAP              bm;
   HBITMAP             hBmp;
   int                 cxIcon, cyIcon;

   //Continue enumeration if we don't see the records we want.
   if (META_DIBBITBLT!=pMFR->rdFunction && META_DIBSTRETCHBLT!=pMFR->rdFunction)
      return 1;

   /*
    * Windows 3.0 DrawIcon uses META_DIBBITBLT in whereas 3.1 uses
    * META_DIBSTRETCHBLT so we have to handle each case separately.
    */

   if (META_DIBBITBLT==pMFR->rdFunction)       //Win3.0
      {
      //Get dimensions and the BITMAPINFO struct.
      uHeight=pMFR->rdParm[1];
      uWidth =pMFR->rdParm[2];
      lpBI=(LPBITMAPINFO)&(pMFR->rdParm[8]);
      }

   if (META_DIBSTRETCHBLT==pMFR->rdFunction)   //Win3.1
      {
      //Get dimensions and the BITMAPINFO struct.
      uHeight=pMFR->rdParm[2];
      uWidth =pMFR->rdParm[3];
      lpBI=(LPBITMAPINFO)&(pMFR->rdParm[10]);
      }

   lpBH=(LPBITMAPINFOHEADER)&(lpBI->bmiHeader);

   //Pointer to the bits which follows the BITMAPINFO structure.
   lpbSrc=(LPBYTE)lpBI+lpBH->biSize;

   //Add the length of the color table.
   if (0!=lpBH->biClrUsed)
      lpbSrc+=(DWORD)(lpBH->biClrUsed*sizeof(RGBQUAD));
   else
      {
      /*
       * 1 << bc gives 2, 16, 256 for 1, 4, or 8 bits.  24-bit
       * bitmaps have no color table, so there's no need to
       * change lpbSrc.
       */
      if (lpBH->biBitCount <= 8)
          lpbSrc+=(DWORD)((1 << (lpBH->biBitCount))*sizeof(RGBQUAD));
      }

   /*
    * All the bits we have in lpbSrc are device-independent, so we
    * need to change them over to be device-dependent using SetDIBits.
    * Once we have a bitmap with the device-dependent bits, we can
    * GetBitmapBits to have buffers with the real data.
    *
    * For each pass we have to allocate memory for the bits.  We save
    * the memory for the mask between passes.
    */

   //Use CreateBitmap for ANY monochrome bitmaps
   if (pIE->fAND || 1==lpBH->biBitCount || lpBH->biBitCount > 8)
      hBmp=CreateBitmap((UINT)lpBH->biWidth, (UINT)lpBH->biHeight, 1, 1, NULL);
   else if (lpBH->biBitCount <= 8)
      hBmp=CreateCompatibleBitmap(hDC, (UINT)lpBH->biWidth, (UINT)lpBH->biHeight);

   if (!hBmp || !SetDIBits(hDC, hBmp, 0, (UINT)lpBH->biHeight, (LPVOID)lpbSrc, lpBI, DIB_RGB_COLORS))
      {
      if (!pIE->fAND)
         GlobalFree(pIE->hMemAND);

      DeleteObject(hBmp);
      return 0;
      }

   //Allocate memory and get the DDBits into it.
   GetObject(hBmp, sizeof(bm), &bm);

   cb=bm.bmHeight*bm.bmWidthBytes * bm.bmPlanes;

//    if (cb % 4 != 0)        // dword align
//      cb += 4 - (cb % 4);

   hMem=GlobalAlloc(GHND, cb);

   if (NULL==hMem)
      {
      if (NULL!=pIE->hMemAND)
         GlobalFree(pIE->hMemAND);

      DeleteObject(hBmp);
      return 0;
      }

   lpbDst=(LPBYTE)GlobalLock(hMem);
   GetBitmapBits(hBmp, cb, (LPVOID)lpbDst);

   DeleteObject(hBmp);
   GlobalUnlock(hMem);

   /*
    * If this is the first pass (pIE->fAND==TRUE) then save the memory
    * of the AND bits for the next pass.
    */
   if (pIE->fAND)
      {
      pIE->fAND=FALSE;
      pIE->hMemAND=hMem;

      //Continue enumeration looking for the next blt record.
      return 1;
      }
   else
      {
      //Get the AND pointer again.
      lpbSrc=(LPBYTE)GlobalLock(pIE->hMemAND);

      /*
       * Create the icon now that we have all the data.  lpbDst already
       * points to the XOR bits.
       */
      cxIcon = GetSystemMetrics(SM_CXICON);
      cyIcon = GetSystemMetrics(SM_CYICON);

      pIE->hIcon=CreateIcon(ghInst,
                       uWidth,
                       uHeight,
                       (BYTE)bm.bmPlanes,
                       (BYTE)bm.bmBitsPixel,
                       (LPVOID)lpbSrc,
                       (LPVOID)lpbDst);

      GlobalUnlock(pIE->hMemAND);
      GlobalFree(pIE->hMemAND);
      GlobalFree(hMem);

      //We're done so we can stop.
      return 0;
      }
   }

/*
 * OleUIMetafilePictExtractIconSource
 *
 * Purpose:
 *  Retrieves the filename and index of the icon source from a metafile
 *  created with OleUIMetafilePictFromIconAndLabel.
 *
 * Parameters:
 *  hMetaPict       HGLOBAL to the METAFILEPICT containing the metafile.
 *  lpszSource      LPSTR in which to store the source filename.  This
 *                  buffer should be OLEUI_CCHPATHMAX characters.
 *  piIcon          UINT FAR * in which to store the icon's index
 *                  within lpszSource
 *
 * Return Value:
 *  BOOL            TRUE if the records were found, FALSE otherwise.
 */
STDAPI_(BOOL) OleUIMetafilePictExtractIconSource(HGLOBAL hMetaPict
   , LPOLESTR lpszSource, UINT FAR *piIcon)
   {
   LPMETAFILEPICT  pMF;
   LABELEXTRACT    le;
   HDC             hDC;

   /*
    * We will walk the metafile looking for the two comment records
    * following the IconOnly comment.  The flags fFoundIconOnly and
    * fFoundSource indicate if we have found IconOnly and if we have
    * found the source comment already.
    */

   if (NULL==hMetaPict || NULL==lpszSource || NULL==piIcon)
      return FALSE;

   pMF=GlobalLock(hMetaPict);

   if (NULL==pMF)
      return FALSE;

   le.lpsz=lpszSource;
   le.fFoundIconOnly=FALSE;
   le.fFoundSource=FALSE;
   le.fFoundIndex=FALSE;

   //Use a screen DC so we have something valid to pass in.
   hDC=GetDC(NULL);

   EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIconSource, (LONG)(LPLABELEXTRACT)&le);

   ReleaseDC(NULL, hDC);
   GlobalUnlock(hMetaPict);

   //Copy the icon index to the caller's variable.
   *piIcon=le.u.iIcon;

   //Check that we found everything.
   return (le.fFoundIconOnly && le.fFoundSource && le.fFoundIndex);
   }

/*
 * EnumMetafileExtractIconSource
 *
 * Purpose:
 *  EnumMetaFile callback function that walks a metafile skipping the first
 *  comment record, extracting the source filename from the second, and
 *  the index of the icon in the third.
 *
 * Parameters:
 *  hDC             HDC into which the metafile should be played.
 *  phTable         HANDLETABLE FAR * providing handles selected into the DC.
 *  pMFR            METARECORD FAR * giving the enumerated record.
 *  pLE             LPLABELEXTRACT providing the destination buffer and
 *                  area to store the icon index.
 *
 * Return Value:
 *  int             0 to stop enumeration, 1 to continue.
 */
int CALLBACK EXPORT EnumMetafileExtractIconSource(HDC hDC, HANDLETABLE FAR *phTable
   , METARECORD FAR *pMFR, int cObj, LPLABELEXTRACT pLE)
   {
   LPSTR       psz;

   /*
    * We don't allow anything to happen until we see "IconOnly"
    * in an MFCOMMENT that is used to enable everything else.
    */
   if (!pLE->fFoundIconOnly)
      {
      if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0])
         {
         if (0==lstrcmpi(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
            pLE->fFoundIconOnly=TRUE;
         }

      return 1;
      }

   //Now see if we find the source string.
   if (!pLE->fFoundSource)
      {
      if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0])
         {
         A2W  ((LPSTR)&pMFR->rdParm[2], pLE->lpsz, OLEUI_CCHPATHMAX);
         pLE->lpsz[OLEUI_CCHPATHMAX-1] = '\0';
         pLE->fFoundSource=TRUE;
         }

      return 1;
      }

   //Next comment will be the icon index.
   if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0])
      {
      /*
       * This string contains the icon index in string form,
       * so we need to convert back to a UINT.  After we see this
       * we can stop the enumeration.  The comment will have
       * a null terminator because we made sure to save it.
       */
      psz=(LPSTR)&pMFR->rdParm[2];
      pLE->u.iIcon=0;

      //Do Ye Olde atoi
      while (*psz)
         pLE->u.iIcon=(10*pLE->u.iIcon)+((*psz++)-'0');

      pLE->fFoundIndex=TRUE;
      return 0;
      }

   return 1;
   }


FRMTBAR.C

/*
 *frmtbar.c
 *
 *Implementation of a richedit format bar
 *
 */

#include "preinc.h"

#include <windows.h>
#include <windowsx.h>

#include <richedit.h>

#include <commctrl.h>
#include "dbugit.h"

#include "reitp.rh"
#include "frmtbar.h"

ASSERTDATA

/*
 *The current format state
 */
typedef struct tagFormatBarState
{
HWNDhwndToolbar;// Toolbar
HWNDhwndName;// Font name combo
HWNDhwndSize;// Font size combo
HWNDhwndColor;// Font color combo

HDChdc;// Our copy of the DC to use
INTcyPerInch;// Pixels per inch vertically

HBRUSHhbrushWindow;// Common brushes
HBRUSHhbrushHighlight;
HBRUSHhbrushButtonFace;
COLORREFcrWindow;// Common colors
COLORREFcrHighlight;
COLORREFcrButtonFace;

CHARFORMATcf;// The current char format
PARAFORMATpf;// The current paragraph format
DWORDdwCFMaskChange;// What changed

LONGrglSize[128];// Possible font sizes
LONGclSize;// Number of font sizes
BOOLfTrueType;// TrueType flag

BOOLfExpectChoice;// Flag that we want next CBN_*
BOOLfGiveUpFocus;// Flag that we are done
} FormatBarState;

#definePfbsGetWindowPtr(_hwnd)\
((FormatBarState *) GetWindowLong(_hwnd, 0))
#defineSetWindowPtr(_hwnd, _p)SetWindowLong(_hwnd, 0, (LONG) _p)

#defineGetFormatBarField(_hwnd, _fld)(PfbsGetWindowPtr(_hwnd)->_fld)

/*
 *Range of sizes we'll show user for TrueType fonts
 */
#definelTrueTypeSizeMin4
#definelTrueTypeSizeMac127

static TCHARszFormatBar[] = FORMATBARCLASSNAME;
static TCHARszToolbar[] = TOOLBARCLASSNAME;
static TCHARszComboBox[] = "ComboBox";

/*
 *Our own private messages from the combobox edit control to the format bar
 *letting up know about important keyevents
 *wParam =window ID of the combobox owning the edit control
 *lParam =window handle of the combobox owning the edit control
 *returns: none
 */
#defineWM_TAB( WM_USER + 1 )
#defineWM_RETURN( WM_USER + 2 )
#defineWM_ESCAPE( WM_USER + 3 )

#definecxDownButton16
#define cxName128 + cxDownButton
#define cxSize40 + cxDownButton
#define cxColor24 + cxDownButton
#definecxGap1(8 + cxName + 8 + cxSize + 8)
#definecxGap2(cxColor + 8)

#defineCFM_MASKS( CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_COLOR )
#defineCFE_EFFECTS( CFE_BOLD | CFE_ITALIC | CFE_UNDERLINE | \
CFE_AUTOCOLOR )

#defineINITIAL_COLOR0x40000000

static TBBUTTON rgtbbutton[] =
{
{ cxGap1, 0L, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0L, -1},

{ tbBold, TBI_Bold, TBSTATE_ENABLED, TBSTYLE_CHECK, 0, 0, 0L, -1},
{ tbItalic, TBI_Italic, TBSTATE_ENABLED, TBSTYLE_CHECK, 0, 0, 0L, -1},
{ tbUnderline, TBI_Underline, TBSTATE_ENABLED, TBSTYLE_CHECK, 0, 0, 0L, -1},
{ cxGap2, 0L, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0L, -1},

{ tbBullet, TBI_Bullet, TBSTATE_ENABLED, TBSTYLE_CHECK, 0, 0, 0L, -1},
{ 0, 0L, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0L, -1},

{ tbDecreaseIndent, TBI_DecreaseIndent, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0L, -1},
{ tbIncreaseIndent, TBI_IncreaseIndent, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0L, -1},
{ 0, 0L, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0L, -1},

{ tbLeft, TBI_Left, TBSTATE_ENABLED, TBSTYLE_CHECKGROUP, 0, 0, 0L, -1},
{ tbCenter, TBI_Center, TBSTATE_ENABLED, TBSTYLE_CHECKGROUP, 0, 0, 0L, -1},
{ tbRight, TBI_Right, TBSTATE_ENABLED, TBSTYLE_CHECKGROUP, 0, 0, 0L, -1},
{ 0, 0L, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0L, -1},
};
#define ctbbutton(sizeof(rgtbbutton) / sizeof(TBBUTTON))

static DWORDrgdwCFEffect[]={ CFE_BOLD, CFE_ITALIC, CFE_UNDERLINE };
static WORDrgwPFAlignment[]={ PFA_LEFT, PFA_CENTER, PFA_RIGHT };

// Hold the old wndproc's !
//$ REVIEW: Make this per instance ?
static WNDPROCpfnEditWndProc = NULL;
static WNDPROCpfnComboBoxWndProc = NULL;
static WNDPROCpfnToolbarWndProc = NULL;

/*
 *Color table for dropdown on toolbar.  Matches COMMDLG colors
 *exactly.
 */
static DWORD rgrgbColors[] = {
RGB(  0,   0, 0),  /* Black*/
RGB(128,   0, 0),  /* Dark red*/
RGB(  0, 128, 0),  /* Dark green*/
RGB(128, 128, 0),  /* Dark yellow*/
RGB(  0,   0, 128),/* Dark blue*/
RGB(128,   0, 128),/* Dark purple*/
RGB(  0, 128, 128),/* Dark aqua*/
RGB(128, 128, 128),/* Dark grey*/
RGB(192, 192, 192),/* Light grey*/
RGB(255,   0, 0),  /* Light red*/
RGB(  0, 255, 0),  /* Light green*/
RGB(255, 255, 0),  /* Light yellow */
RGB(  0,   0, 255),/* Light blue*/
RGB(255,   0, 255),/* Light purple */
RGB(  0, 255, 255),/* Light aqua*/
RGB(255, 255, 255),/* White*/
};

const INT crgbColorsMax = sizeof(rgrgbColors) / sizeof(rgrgbColors[0]);

#defineTraceCharFormat(_sz, _pcf)

/*
 *UpdateBrush
 *
 *Purpose:
 *Syncs up the brushes with the known color scheme as needed
 *
 *Arguments:
 *hwndHandle of the format bar
 *
 *Returns:
 *None.
 */
LOCAL VOID UpdateBrush(HBRUSH * phbrush, COLORREF * pcr, INT nIndex)
{
COLORREFcr;

if (((cr = GetSysColor(nIndex)) != *pcr) ||
!*phbrush)
{
if (*phbrush)
DeleteObject(*phbrush);
*phbrush = CreateSolidBrush(*pcr = cr);
}
}

/*
 *UpdateBrushes
 *
 *Purpose:
 *Syncs up the brushes with the known color scheme as needed
 *
 *Arguments:
 *hwndHandle of the format bar
 *
 *Returns:
 *None.
 */
LOCAL VOID UpdateBrushes(HWND hwnd)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);

UpdateBrush(&pfbs->hbrushWindow, &pfbs->crWindow, COLOR_WINDOW);
UpdateBrush(&pfbs->hbrushHighlight, &pfbs->crHighlight, COLOR_HIGHLIGHT);
UpdateBrush(&pfbs->hbrushButtonFace, &pfbs->crButtonFace, COLOR_BTNFACE);
}

/*
 *LFBEditWndProc
 *
 *Purpose:
 *Take care of handling the notifications from the combobox controls we
 *get for the format bar
 *
 *Arguments:
 *hwnd
 *wMsg
 *wParam
 *lParam
 *
 *Returns:
 *LRESULT
 */
LRESULT CALLBACK LFBEditWndProc(HWND hwnd, UINT wMsg, WPARAM wParam,
LPARAM lParam)
{
switch (wMsg)
{
case WM_KEYDOWN:
{
HWNDhwndCombo = GetParent(hwnd);
HWNDhwndFormatBar = GetParent(GetParent(hwndCombo));
INTnID = GetWindowID(hwndCombo);

switch(wParam)
{

case VK_ESCAPE:
SendMessage(hwndFormatBar, WM_ESCAPE, nID,
(LPARAM) (LPTSTR) hwndCombo);

case VK_TAB:
case VK_RETURN:
return 0;
}
}
break;

case WM_CHAR:
{
HWNDhwndCombo = GetParent(hwnd);
HWNDhwndFormatBar = GetParent(GetParent(hwndCombo));
INTnID = GetWindowID(hwndCombo);

switch(wParam)
{
case VK_RETURN:
SendMessage(hwndFormatBar, WM_RETURN, nID,
(LPARAM) (LPTSTR) hwndCombo);
return 0;

case VK_TAB:
SendMessage(hwndFormatBar, WM_TAB, nID,
(LPARAM) (LPTSTR) hwndCombo);
return 0;

   case VK_ESCAPE:
return 0;
}
}
break;

case WM_KEYUP:
switch (wParam)
{
case VK_RETURN:
case VK_TAB:
case VK_ESCAPE:
return 0;
}
break;
}

return CallWindowProc(pfnEditWndProc, hwnd, wMsg, wParam, lParam);
}

/*
 *LFBComboBoxWndProc
 *
 *Purpose:
 *Take care of handling the notifications from the combobox controls we
 *get for the format bar
 *
 *Arguments:
 *hwnd
 *wMsg
 *wParam
 *lParam
 *
 *Returns:
 *LRESULT
 */
LRESULT CALLBACK LFBComboBoxWndProc(HWND hwnd, UINT wMsg, WPARAM wParam,
LPARAM lParam)
{
switch (wMsg)
{
case WM_KEYDOWN:
{
HWNDhwndFormatBar = GetParent(GetParent(hwnd));
INTnID = GetWindowID(hwnd);

switch(wParam)
{
case VK_TAB:
SendMessage(hwndFormatBar, WM_TAB, nID,
(LPARAM) (LPTSTR) hwnd);
return 0;

case VK_RETURN:
SendMessage(hwndFormatBar, WM_RETURN, nID,
(LPARAM) (LPTSTR) hwnd);
return 0;

case VK_ESCAPE:
SendMessage(hwndFormatBar, WM_ESCAPE, nID,
(LPARAM) (LPTSTR) hwnd);
return 0;
}
}
break;

case WM_KEYUP:
case WM_CHAR:
switch(wParam)
{
case VK_TAB:
case VK_RETURN:
case VK_ESCAPE:
return 0;
}
break;
}

return CallWindowProc(pfnComboBoxWndProc, hwnd, wMsg, wParam, lParam);
}

/*
 *LFBToolbarWndProc
 *
 *Purpose:
 *Take care of handling the notifications from the combobox controls we
 *get for the format bar
 *
 *Arguments:
 *hwnd
 *wMsg
 *wParam
 *lParam
 *
 *Returns:
 *LRESULT
 */

LRESULT CALLBACK LFBToolbarWndProc(HWND hwnd, UINT wMsg, WPARAM wParam,
LPARAM lParam)
{
switch (wMsg)
{
case WM_CTLCOLORLISTBOX:
{
FormatBarState *pfbs = PfbsGetWindowPtr(GetParent(hwnd));

// Peek at the current information
UpdateBrush(&pfbs->hbrushButtonFace, &pfbs->crButtonFace,
COLOR_BTNFACE);
return (LRESULT) (LPTSTR) pfbs->hbrushButtonFace;
}
break;
}

return CallWindowProc(pfnToolbarWndProc, hwnd, wMsg, wParam, lParam);
}

/*
 *NEnumFontNameProc
 *
 *Purpose:
 *The callback for EnumFontFamilies which fills the font names combobox
 *with the available fonts
 *
 *Arguments:
 *
 *Returns:
 *Non-zero as long a font name can be inserted into the combobox
 */
INT CALLBACK NEnumFontNameProc(LOGFONT * plf, TEXTMETRIC * ptm,
INT nFontType, LPARAM lParam)
{
LRESULT lr;
DWORDdw;

lr = SendMessage((HWND) lParam, CB_ADDSTRING, 0, (LPARAM) plf->lfFaceName);
if (!(lr == CB_ERR || lr == CB_ERRSPACE))
{
Assert (!(plf->lfCharSet & 0xFFFFFF00));
Assert (!(plf->lfPitchAndFamily & 0xFFFFFF00));
Assert (!(nFontType & 0xFFFF0000));
dw = plf->lfCharSet & 0xFF;
dw <<= 8;
dw |= plf->lfPitchAndFamily & 0xFF;
dw <<= 16;
dw |= nFontType & 0xFFFF;
SendMessage((HWND) lParam, CB_SETITEMDATA, LOWORD(lr), (LPARAM) dw);
}
return !(lr == CB_ERR || lr == CB_ERRSPACE);
}

/*
 *FillNames
 *
 *Purpose:
 *Fills the font name combobox with the names of the available fonts
 *
 *Arguments:
 *hwndHandle of the names combobox
 *
 *Returns:
 *None.
 */
LOCAL void FillNames(HWND hwnd)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
HDChdc = pfbs->hdc;
HWNDhwndName = pfbs->hwndName;

// Empty the current list
SendMessage(hwndName, CB_RESETCONTENT, 0, 0);

// Ask for all the font families
EnumFontFamilies((HDC) hdc, (LPTSTR) NULL,
 (FONTENUMPROC) NEnumFontNameProc,
 (LPARAM) (LPTSTR) hwndName);
}

/*
 *FInsertSize
 *
 *Purpose:
 *Inserts a font size entry into the array. Takes care of the necessary
 *conversion from logical units to points size. The array is kept in
 *ascending order.
 *
 *Arguments:
 *lSizeThe font size in logical units
 *pfbsThe current format bar state
 *fPointsFlag whether the size is in points or not
 *
 *Returns:
 *TRUE if the font size was successfully added to the array, or was
 *present.
 */
BOOL FInsertSize(LONG lSize, FormatBarState * pfbs, BOOL fPoints)
{
LONGclLeft = pfbs->clSize;
LONG *plSize = pfbs->rglSize;

if (clLeft >= sizeof(pfbs->rglSize) / sizeof(LONG))
return FALSE;

// Convert to point sizes
if (!fPoints)
lSize = MulDiv((INT) lSize, 72, pfbs->cyPerInch);

while (clLeft > 0 && *plSize < lSize)
{
++plSize;
--clLeft;
}
if (clLeft && *plSize == lSize)
;
else
{
if (clLeft)
{
MoveMemory(plSize + 1, plSize, clLeft * sizeof(LONG));
}
*plSize = lSize;
++pfbs->clSize;
}
return TRUE;
}

/*
 *NEnumFontSizeProc
 *
 *Purpose:
 *The callback for EnumFontFamilies which fills the font names combobox
 *with the available fonts
 *
 *Arguments:
 *
 *Returns:
 *Non-zero as long a font name can be inserted into the combobox
 */
INT CALLBACK NEnumFontSizeProc(LOGFONT * plf, TEXTMETRIC * ptm,
INT nFontType, LPARAM lParam)
{
FormatBarState *pfbs = (FormatBarState *) lParam;

if (ptm->tmPitchAndFamily & TMPF_TRUETYPE)
pfbs->fTrueType |= TRUE;
return FInsertSize(ptm->tmHeight - ptm->tmInternalLeading, pfbs, FALSE);
}

/*
 *FFillSizes
 *
 *Purpose:
 *Fills the font name combobox with the names of the available fonts
 *
 *Arguments:
 *hwndHandle of the names combobox
 *
 *Returns:
 *TRUE if successful
 */
LOCAL BOOL FFillSizes(HWND hwnd)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
HDChdc = pfbs->hdc;
HWNDhwndSize = pfbs->hwndSize;
TCHAR *pszFaceName = pfbs->cf.szFaceName;
LONGilSize;
LONGlCurrSize;
LONGilMatch;
LONG *plSize  = pfbs->rglSize;
TCHARszT[10];
LRESULTlr;
BOOLfSuccess = FALSE;

// Empty the current list
SendMessage(hwndSize, CB_RESETCONTENT, 0, 0);
pfbs->clSize = 0;
pfbs->fTrueType = FALSE;

// Ask for all the font sizes for the given font
EnumFontFamilies((HDC) hdc, pszFaceName,
 (FONTENUMPROC) NEnumFontSizeProc, (LPARAM) pfbs);

// If we got a TrueType font, just fill in the array with our sizes
if (pfbs->fTrueType)
{
ilSize = lTrueTypeSizeMin;
while (ilSize < lTrueTypeSizeMac)
*plSize++ = ilSize++;
*plSize = ilSize;
pfbs->clSize = lTrueTypeSizeMac - lTrueTypeSizeMin + 1;
}
// Save ourselves some dereferencing and covert to points
lCurrSize = MulDiv((INT) pfbs->cf.yHeight, 72, 1440);

// Now go through our entries and put them into our listbox
plSize  = pfbs->rglSize;
ilMatch = -1;
for (ilSize = 0; ilSize < pfbs->clSize; ++ilSize, ++plSize)
{
wsprintf(szT, "%ld", *plSize);
lr = SendMessage(hwndSize, CB_ADDSTRING, 0, (LPARAM) szT);
AssertSz(!(lr == CB_ERR || lr == CB_ERRSPACE), "Can't add more sizes");
if (lr == CB_ERR || lr == CB_ERRSPACE)
goto CleanUp;

// Try to find out which element will become our current selection
if (*plSize == lCurrSize)
ilMatch = ilSize;
}

// Set our current selection
if (ilMatch >= 0)
SendMessage(hwndSize, CB_SETCURSEL, (WPARAM) ilMatch, 0);
else
{
if (lCurrSize)
wsprintf(szT, "%ld", lCurrSize);
else
szT[0] = 0;
SetWindowText(hwndSize, szT);
}
fSuccess = TRUE;

CleanUp:
return fSuccess;
}

/*
 *FillColors
 *
 *Purpose:
 *Fills the font colors combobox with samples of the available colors
 *
 *Arguments:
 *hwndHandle of the names combobox
 *
 *Returns:
 *None.
 */
LOCAL void FillColors(HWND hwnd)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
HWNDhwndColor = pfbs->hwndColor;
INTilMatch = -1;
LRESULTlr;
INTicr;
DWORD *pdw;

// Empty the current list
SendMessage(hwndColor, CB_RESETCONTENT, 0, 0);

for (icr = 0, pdw = rgrgbColors; icr < crgbColorsMax; ++icr, ++pdw)
{
lr = SendMessage(hwndColor, CB_ADDSTRING, 0, (LPARAM) *pdw);
if (lr == CB_ERR || lr == CB_ERRSPACE)
{
TraceError("FillColors: No more room for colors", -1);
break;
}

if (*pdw == pfbs->cf.crTextColor)
ilMatch = icr;
}

SendMessage(hwndColor, CB_SETCURSEL, (WPARAM) ilMatch > 0 ? ilMatch : 0, 0);
}

/*
 *FB_OnNcCreate
 *
 *Purpose:
 *We want to draw our own colors for the colors combobox
 *
 *Arguments:
 *hwndThe window
 *pmisPointer to a MEASUREITEMSTRUCT to be filled out
 *
 *Returns:
 *None.
 */
LOCAL BOOL FB_OnNcCreate(HWND hwnd, CREATESTRUCT * pcs)
{
RECTrc;
INTcyButton;
INTcyList;
INTyPos;
INTxPos;
HFONThfont;
FormatBarState *pfbs = NULL;
HINSTANCEhinst = pcs->hInstance;
WNDPROC pfnWndProcT;
POINTpt = { 1, 1 };
HWNDhwndT;

// Tell format bar where to find it's state information
pfbs = (FormatBarState *) GlobalAllocPtr(GHND, sizeof(FormatBarState));
if (!pfbs)
goto ErrorNoState;
SetWindowPtr(hwnd, pfbs);
AssertSz(PfbsGetWindowPtr(hwnd) == pfbs, "We didn't write it!");

// Get a few brushes that we'll be using all the time
UpdateBrushes(hwnd);

// Create the toolbar
pfbs->hwndToolbar = CreateToolbarEx(hwnd, CCS_TOP | WS_CHILD,
0, tbFormatBarMax, hinst,
BMP_FormatBar, rgtbbutton, ctbbutton,
16, 16, 16, 16, sizeof(TBBUTTON));

if (!pfbs->hwndToolbar)
goto ErrorNoToolbar;

// Subclass the toolbar
pfnWndProcT = (WNDPROC) SetWindowLong(pfbs->hwndToolbar, GWL_WNDPROC,
(LONG) LFBToolbarWndProc);

// If we don't know what the global ToolbarWndProc is, save it
if (!pfnToolbarWndProc)
pfnToolbarWndProc = pfnWndProcT;

// Determine how tall the buttons are so we can size our other controls
// accordingly
SendMessage(pfbs->hwndToolbar, TB_GETITEMRECT, 1, (LPARAM) &rc);
cyButton = rc.bottom - rc.top + 1;

// Determine how tall the toolbar is so that we can center the comboboxes
GetClientRect(pfbs->hwndToolbar, &rc);
yPos = (rc.bottom - rc.top + 1 - cyButton) / 2 + 1;

// Let's make the comboboxes dropdown about 5 times the height
cyList = 5 * cyButton;

// Get the font to use for the comboboxes
hfont = (HFONT) SendMessage(pfbs->hwndToolbar, WM_GETFONT, 0, 0);

// Now create the other format bar controls

// The name
xPos = 8;
pfbs->hwndName = CreateWindow(szComboBox, NULL,
WS_CHILD | WS_VSCROLL | CBS_DROPDOWN |
CBS_SORT | CBS_HASSTRINGS | WS_VISIBLE,
xPos, yPos, cxName, cyList,
pfbs->hwndToolbar, (HMENU) TBI_Name,
hinst, NULL);
if (!pfbs->hwndName)
goto ErrorNoName;
SetWindowFont(pfbs->hwndName, hfont, TRUE);
xPos += cxName + 8;

// The Size
pfbs->hwndSize = CreateWindow(szComboBox, NULL,
WS_CHILD | WS_VSCROLL | CBS_DROPDOWN |
WS_VISIBLE,
xPos, yPos, cxSize, cyList,
pfbs->hwndToolbar, (HMENU) TBI_Size,
hinst, NULL);
if (!pfbs->hwndSize)
goto ErrorNoSize;
SetWindowFont(pfbs->hwndSize, hfont, TRUE);

// The color
//$ REVIEW: Magic number
SendMessage(pfbs->hwndToolbar, TB_GETITEMRECT, 3, (LPARAM) &rc);
pfbs->hwndColor = CreateWindow(szComboBox, NULL,
WS_CHILD | WS_VSCROLL | CBS_DROPDOWNLIST |
CBS_OWNERDRAWFIXED | WS_VISIBLE,
rc.right, yPos - 1, cxColor, cyList,
pfbs->hwndToolbar, (HMENU) TBI_Color,
hinst, NULL);
if (!pfbs->hwndColor)
goto ErrorNoColor;
SetWindowFont(pfbs->hwndColor, hfont, TRUE);

// Set the initial color to black so it won't be redrawn.
pfbs->cf.dwMask |= INITIAL_COLOR;

// Subclass the comboboxes' edit controls

// Do the name first
hwndT = ChildWindowFromPoint(pfbs->hwndName, pt);
pfnWndProcT = (WNDPROC) SetWindowLong(hwndT, GWL_WNDPROC,
(LONG) LFBEditWndProc);

// If we don't know what the global EditWndProc is, save it
if (!pfnEditWndProc)
pfnEditWndProc = pfnWndProcT;

// Next the size
hwndT = ChildWindowFromPoint(pfbs->hwndSize, pt);
(WNDPROC) SetWindowLong(hwndT, GWL_WNDPROC, (LONG) LFBEditWndProc);

// Lastly the color
pfnWndProcT = (WNDPROC) SetWindowLong(pfbs->hwndColor, GWL_WNDPROC,
(LONG) LFBComboBoxWndProc);

// If we don't know what the global ComboBoxWndProc is, save it
if (!pfnComboBoxWndProc)
pfnComboBoxWndProc = pfnWndProcT;

// Create a copy of the DC that the user gave up to play with
if(pcs->lpCreateParams)
{
HDC *phdc = (HDC *) pcs->lpCreateParams;

pfbs->hdc = CreateCompatibleDC(*phdc);
}
else
{
HDC hdc = GetDC(pcs->hwndParent);

pfbs->hdc = CreateCompatibleDC(hdc);
ReleaseDC(pcs->hwndParent, hdc);
}

if (!pfbs->hdc)
goto ErrorNoColor;

// Get the number of pixels per inch vertically so we can do point sizes
pfbs->cyPerInch = GetDeviceCaps(pfbs->hdc, LOGPIXELSY);

// Load up the names of the fonts and colors
FillNames(hwnd);
FillColors(hwnd);
// Continue creating
return TRUE;

ErrorNoColor:
DestroyWindow(pfbs->hwndSize);
pfbs->hwndSize = NULL;

ErrorNoSize:
DestroyWindow(pfbs->hwndName);
pfbs->hwndName = NULL;

ErrorNoName:
DestroyWindow(pfbs->hwndToolbar);
pfbs->hwndToolbar = NULL;

ErrorNoToolbar:
if (pfbs)
GlobalFreePtr(pfbs);

ErrorNoState:
return FALSE;
}

/*
 *FB_OnMeasureItem
 *
 *Purpose:
 *We want to draw our own colors for the colors combobox
 *
 *Arguments:
 *hwndThe window
 *pmisPointer to a MEASUREITEMSTRUCT to be filled out
 *
 *Returns:
 *None.
 */
LOCAL void FB_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT * pmis)
{
switch (pmis->CtlID)
{
case TBI_Color:
pmis->itemWidth = 128;
pmis->itemHeight = 16;
break;
default:
TraceError("Don't know anything about CtlID", -1);
break;
}
}

/*
 *FB_OnDrawItem
 *
 *Purpose:
 *We want to drawn our own colors for our color combobox
 *
 *Arguments:
 *hwndThe window
 *pdisPointer to a DRAWITEMSTRUCT to be filled out
 *
 *Returns:
 *None.
 */
LOCAL void FB_OnDrawItem(HWND hwnd, DRAWITEMSTRUCT * pdis)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
HDChdc = pdis->hDC;
RECTrc;
HBRUSHhbrush;
COLORREFcr = (COLORREF) pdis->itemData;
INTnIter = 2;

// As far as we know we have only one owner drawn control
AssertSz(pdis->CtlType == ODT_COMBOBOX, "Know only comboboxes");
if (pdis->CtlID != TBI_Color)
return;

// NULL object wanted ?
if (pdis->itemData == -1)
goto HandleFocus;

switch (pdis->itemAction)
{
case ODA_DRAWENTIRE:
rc = pdis->rcItem;
InflateRect(&rc, -3, -3);
hbrush = CreateSolidBrush((COLORREF)cr);
FillRect(hdc, &rc, hbrush);
DeleteObject(hbrush);
FrameRect(hdc, &rc, GetStockObject(BLACK_BRUSH));

// *** FALL THROUGH ***

case ODA_SELECT:
rc = pdis->rcItem;
if (pdis->itemState & ODS_SELECTED)
hbrush = pfbs->hbrushHighlight;
else
hbrush = pfbs->hbrushWindow;
while (nIter--)
{
InflateRect(&rc, -1, -1);
FrameRect(hdc, &rc, hbrush);
}

if (pdis->itemAction != ODA_DRAWENTIRE)
break;

// *** FALL THROUGH ***

HandleFocus:
case ODA_FOCUS:
if (pdis->itemState & ODS_FOCUS)
hbrush = pfbs->hbrushHighlight;
else
hbrush = pdis->itemData == -1 ? pfbs->hbrushButtonFace :
pfbs->hbrushWindow;
FrameRect(hdc, &pdis->rcItem, hbrush);
break;
}

}

/*
 *PaintName
 *
 *Purpose:
 *Paint the current name
 *
 *Arguments:
 *pfbsThe current format bar state
 *
 *Returns:
 *None.
 */
LOCAL VOID PaintName(FormatBarState * pfbs)
{
LONGilFound;

TraceCharFormat("PaintName", &pfbs->cf);
if (pfbs->cf.dwMask & CFM_FACE)
{
ilFound = SendMessage(pfbs->hwndName, CB_FINDSTRING, 0,
(LPARAM) pfbs->cf.szFaceName);
SendMessage(pfbs->hwndName, CB_SETCURSEL, (WPARAM) ilFound, 0);
if (ilFound == CB_ERR)
SetWindowText(pfbs->hwndName, pfbs->cf.szFaceName);
}
else
SetWindowText(pfbs->hwndName, TEXT(""));

}

/*
 *PaintSize
 *
 *Purpose:
 *Paint the current size
 *
 *Arguments:
 *pfbsThe current format bar state
 *
 *Returns:
 *None.
 */
LOCAL VOID PaintSize(FormatBarState * pfbs)
{
LONGlSize = MulDiv((INT) pfbs->cf.yHeight, 72, 1440);
LONGilFound;
TCHARszT[10];

TraceCharFormat("PaintSize", &pfbs->cf);
szT[0] = 0;
if (lSize > 0 && (pfbs->cf.dwMask & CFM_SIZE))
wsprintf(szT, "%ld", lSize);
ilFound = SendMessage(pfbs->hwndSize, CB_FINDSTRINGEXACT, 0,
(LPARAM) szT);
SendMessage(pfbs->hwndSize, CB_SETCURSEL, (WPARAM) ilFound, 0);
SetWindowText(pfbs->hwndSize, szT);
}

/*
 *PaintColor
 *
 *Purpose:
 *Paint the current color
 *
 *Arguments:
 *pfbsThe current format bar state
 *
 *Returns:
 *None.
 */
LOCAL VOID PaintColor(FormatBarState * pfbs)
{
LONGilFound = -1;
COLORREFcrTextColor = pfbs->cf.crTextColor;

//$ FUTURE: Handle autocolor

// If color isn't known, choose white
TraceCharFormat("PaintColor", &pfbs->cf);
if (pfbs->cf.dwMask & CFM_COLOR)
{
ilFound = SendMessage(pfbs->hwndColor, CB_FINDSTRINGEXACT, 0,
(LPARAM) crTextColor);
if (ilFound < 0)
{
// Not found, add a new color
ilFound = SendMessage(pfbs->hwndColor, CB_ADDSTRING, 0,
(LPARAM) crTextColor);
}
}
SendMessage(pfbs->hwndColor, CB_SETCURSEL, (WPARAM) ilFound, 0);
}

/*
 *PaintEffects
 *
 *Purpose:
 *Paint the current effects
 *
 *Arguments:
 *pfbsThe current format bar state
 *
 *Returns:
 *None.
 */
LOCAL VOID PaintEffects(FormatBarState * pfbs)
{
HWNDhwndToolbar = pfbs->hwndToolbar;
DWORDdwMask = pfbs->cf.dwMask;
DWORDdwEffects = pfbs->cf.dwEffects;
BOOLfEffect;
BOOLfMask;
INTnID;

TraceCharFormat("PaintEffects", &pfbs->cf);
for (nID = TBI_Bold; nID <= TBI_Underline; nID++)
{
fMask = dwMask & rgdwCFEffect[nID - TBI_Bold] ? TRUE : FALSE;
fEffect = dwEffects & rgdwCFEffect[nID - TBI_Bold] ? TRUE : FALSE;
#ifdef LIKE_WORD2
// Act like Word
SendMessage(hwndToolbar, TB_CHECKBUTTON, nID, MAKELONG(fEffect, 0));
SendMessage(hwndToolbar, TB_INDETERMINATE, nID, MAKELONG(!fMask, 0));
#elif defined(LIKE_T3)
// Act like T3
SendMessage(hwndToolbar, TB_CHECKBUTTON, nID,
MAKELONG(fEffect && fMask, 0));
#else
//$ Raid 2375: Show the user what is actually going to happen
SendMessage(hwndToolbar, TB_CHECKBUTTON, nID, MAKELONG(fEffect, 0));
#endif
}

}

/*
 *PaintAlignment
 *
 *Purpose:
 *Paint the current alignment
 *
 *Arguments:
 *pfbsThe current format bar state
 *
 *Returns:
 *None.
 */
LOCAL VOID PaintAlignment(FormatBarState * pfbs)
{
HWNDhwndToolbar = pfbs->hwndToolbar;
DWORDwAlignment = pfbs->pf.wAlignment;
INTnID;

if (pfbs->pf.dwMask & PFM_ALIGNMENT)
{
#ifdef LIKE_WORD
// Make all the buttons active
for (nID = TBI_Left; nID <= TBI_Right; nID++)
SendMessage(hwndToolbar, TB_INDETERMINATE, nID,
MAKELONG(FALSE, 0));
#endif
// And press down one of them
switch (wAlignment)
{
case PFA_CENTER:
nID = TBI_Center;
break;

case PFA_RIGHT:
nID = TBI_Right;
break;

case PFA_LEFT:
default:
nID = TBI_Left;
break;
}
SendMessage(hwndToolbar, TB_CHECKBUTTON, nID, MAKELONG(TRUE, 0));
}
else
{
#ifdef LIKE_WORD
// Make all the buttons indeterminate
for (nID = TBI_Left; nID <= TBI_Right; nID++)
SendMessage(hwndToolbar, TB_INDETERMINATE, nID, MAKELONG(TRUE, 0));
#else
// Pop all the buttons
for (nID = TBI_Left; nID <= TBI_Right; nID++)
SendMessage(hwndToolbar, TB_CHECKBUTTON, nID, MAKELONG(FALSE, 0));
#endif
}
}

/*
 *FB_OnCommand
 *
 *Purpose:
 *Handle some of the notifications
 *
 *Arguments:
 *hwndThe window
 *pdisPointer to a DRAWITEMSTRUCT to be filled out
 *
 *Returns:
 *None.
 */
LOCAL void FB_OnCommand(HWND hwnd, INT nID, HWND hwndCtl, INT nNotify)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
LONGiSel;
if (nNotify == CBN_ERRSPACE)
{
//$ FUTURE: Handle error out of space
return;
}

switch (nNotify)
{
case CBN_SELENDOK:
// Flag the next combobox message as somethng we want
pfbs->fExpectChoice = TRUE;
pfbs->fGiveUpFocus = TRUE;
return;

case 0:
// This is a button from the toolbar if hwndCtl == pfbs->hwndToolbar
if (hwndCtl != pfbs->hwndToolbar)
return;
break;

case CBN_KILLFOCUS:
switch (nID)
{
case TBI_Name:
PaintName(pfbs);
break;

case TBI_Size:
PaintSize(pfbs);
break;

case TBI_Color:
PaintColor(pfbs);
break;
}
return;

case CBN_SELCHANGE:
// The user is making up his mind so ignore the sel change
if (!pfbs->fExpectChoice)
return;
break;

case CBN_DROPDOWN:
if (nID == TBI_Size)
FFillSizes(hwnd);
return;

//$ REVIEW: Just check for fExpectChoice to shorten this switch ?

// Throw away these notifications from the toolbar
case TBN_BEGINDRAG:
case TBN_ENDDRAG:

// Throw away these notifications from the comboboxes
case CBN_SETFOCUS:
case CBN_CLOSEUP:
case CBN_SELENDCANCEL:
case CBN_EDITCHANGE:
case CBN_EDITUPDATE:
case CBN_DBLCLK:

default:
return;
}

switch (nID)

{
case TBI_Name:
if (pfbs->fExpectChoice)
{
DWORDdw;

// Get the new font name
pfbs->cf.dwMask |= CFM_FACE | CFM_CHARSET;
pfbs->dwCFMaskChange = CFM_FACE | CFM_CHARSET;

// Get the new selection
iSel = SendMessage(pfbs->hwndName, CB_GETCURSEL, 0, 0);
if (iSel < 0)
{
//DebugStr("\pBefore GetWindowText in Frmtbar.c");
GetWindowText(pfbs->hwndName, pfbs->cf.szFaceName,
sizeof(pfbs->cf.szFaceName) / sizeof(TCHAR));
pfbs->cf.bPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
pfbs->cf.bCharSet = DEFAULT_CHARSET;
}
else
{
SendMessage(pfbs->hwndName, CB_GETLBTEXT, (WPARAM) iSel,
(LPARAM) pfbs->cf.szFaceName);

// Pull the other bits of data we had packed away.
dw = ComboBox_GetItemData(pfbs->hwndName, iSel);
dw >>= 16;
pfbs->cf.bPitchAndFamily = (BYTE) dw & 0xFF;
dw >>= 8;
pfbs->cf.bCharSet = (BYTE) dw & 0xFF;
}

// Update the list of font sizes
FFillSizes(hwnd);
pfbs->fExpectChoice = FALSE;
}
break;

case TBI_Size:
if (pfbs->fExpectChoice)
{
TCHARszT[10];
TCHAR *pch = szT;
LONGlSize = 0;

// Get the new font size
pfbs->cf.dwMask |= CFM_SIZE;
pfbs->dwCFMaskChange = CFM_SIZE;
szT[0] = 0;

// Get the new selection
iSel = SendMessage(pfbs->hwndSize, CB_GETCURSEL, 0, 0);
if (iSel < 0)
{
//DebugStr("\pBefore GetWindowText in Format.c2");
GetWindowText(pfbs->hwndSize, szT, sizeof(szT) / sizeof(TCHAR));
}
else
SendMessage(pfbs->hwndSize, CB_GETLBTEXT, (WPARAM) iSel,
(LPARAM) szT);

while (*pch)
{
if (TEXT('0') <= *pch && *pch <= TEXT('9'))
{
lSize *= 10;
lSize += *pch - '0';
}
else
{
// Got a bad character, reject the user's input
lSize = 0;
break;
}
*pch++;
}

if (lSize > 0 && lSize <= yHeightCharPtsMost)
pfbs->cf.yHeight = lSize * 20;// Make twips
else
MessageBox(hwnd, "Size must be between 1 and 1638", "Font",
MB_ICONINFORMATION | MB_OK);
pfbs->fExpectChoice = FALSE;
}
break;

case TBI_Color:
if (pfbs->fExpectChoice)
{
LONG ilSel = SendMessage(pfbs->hwndColor, CB_GETCURSEL, 0, 0);

// Remember that we are picking a color
pfbs->cf.dwMask |= CFM_COLOR;
pfbs->dwCFMaskChange = CFM_COLOR;

//$ FUTURE: For now always turn-off autocolor, but later we'll
//have to handle picking autocolor
pfbs->cf.dwEffects &= ~CFE_AUTOCOLOR;

if (ilSel < 0)
pfbs->cf.crTextColor = 0;
else
pfbs->cf.crTextColor = (COLORREF) SendMessage(pfbs->hwndColor,
CB_GETITEMDATA, (WPARAM) ilSel, 0);
pfbs->fExpectChoice = FALSE;
}
break;

case TBI_Bold:
case TBI_Italic:
case TBI_Underline:

// NOTE: We reverse the if condition because by the time we get the
// notification, the button has already been pressed.
pfbs->dwCFMaskChange = rgdwCFEffect[nID - TBI_Bold];
pfbs->cf.dwMask |= rgdwCFEffect[nID - TBI_Bold];
#ifdef NEVER
if (!SendMessage(pfbs->hwndToolbar, TB_ISBUTTONCHECKED, nID, 0))
pfbs->cf.dwEffects &= ~rgdwCFEffect[nID - TBI_Bold];
else
pfbs->cf.dwEffects |= rgdwCFEffect[nID - TBI_Bold];
#else
pfbs->cf.dwEffects ^= rgdwCFEffect[nID - TBI_Bold];
#endif
pfbs->fGiveUpFocus = TRUE;
break;

case TBI_Bullet:
// NOTE: The button has already been pushed,
// so the checked state is accurate
pfbs->pf.dwMask |= PFM_NUMBERING | PFM_OFFSET;
if (SendMessage(pfbs->hwndToolbar, TB_ISBUTTONCHECKED, nID, 0))
{
pfbs->pf.wNumbering = PFN_BULLET;
pfbs->pf.dxOffset = cxBulletIndent;
}
else
{
pfbs->pf.wNumbering = 0;
pfbs->pf.dxOffset = 0;
}
pfbs->fGiveUpFocus = TRUE;
break;

case TBI_IncreaseIndent:
case TBI_DecreaseIndent:
pfbs->fGiveUpFocus = TRUE;
break;

case TBI_Left:
case TBI_Center:
case TBI_Right:
pfbs->pf.dwMask |= PFM_ALIGNMENT;
pfbs->pf.wAlignment = rgwPFAlignment[nID - TBI_Left];
pfbs->fGiveUpFocus = TRUE;
PaintAlignment(pfbs);
break;

}

// Give up the focus ?
if (pfbs->fGiveUpFocus)
{
pfbs->fGiveUpFocus = FALSE;
SetFocus(hwnd);
}

// Go tell our parent
SendMessage(GetParent(hwnd), WM_COMMAND,
GET_WM_COMMAND_MPS(nID, hwndCtl, nNotify));
}

/*
 *FB_OnPaint
 *
 *Purpose:
 *Update our screen
 *
 *Arguments:
 *hwndThe window
 *
 *Returns:
 *None.
 */
LOCAL void FB_OnPaint(HWND hwnd)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
PAINTSTRUCTps;

// Repaint the toolbar as needed
BeginPaint(hwnd, &ps);
//PaintName(pfbs);
//PaintSize(pfbs);
//PaintColor(pfbs);
//PaintEffects(pfbs);
//PaintAlignment(pfbs);
SendMessage(pfbs->hwndToolbar, WM_PAINT, 0, 0);
EndPaint(hwnd, &ps);
}

/*
 *SetCharFormat
 *
 *Purpose:
 *Set the current char format
 *
 *Arguments:
 *hwndThe format bar window
 *pcfNewThe new formatting
 *
 *Returns:
 *None.
 */
LOCAL VOID SetCharFormat(HWND hwnd, CHARFORMAT *pcfNew)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
CHARFORMAT *pcf = &pfbs->cf;
DWORDdwMask;
DWORDdwEffects;

TraceCharFormat("EM_SETCHARFORMAT", pcfNew);

if(pcfNew->cbSize != sizeof(CHARFORMAT))
return;

// Change in font name ?
if (lstrcmpi(pcf->szFaceName, pcfNew->szFaceName) ||
(pcf->bPitchAndFamily != pcfNew->bPitchAndFamily) ||
(pcf->dwMask & CFM_FACE) != (pcfNew->dwMask & CFM_FACE))
{
pcf->dwMask &= ~CFM_FACE;
pcf->dwMask |= pcfNew->dwMask & CFM_FACE;
lstrcpy(pcf->szFaceName, pcfNew->szFaceName);
pcf->bPitchAndFamily = pcfNew->bPitchAndFamily;
PaintName(pfbs);
}

// Change in charset ?
if (pcf->bCharSet != pcfNew->bCharSet ||
(pcf->dwMask & CFM_CHARSET) != (pcfNew->dwMask & CFM_CHARSET))
{
pcf->dwMask &= ~CFM_CHARSET;
pcf->dwMask |= pcfNew->dwMask & CFM_CHARSET;
pcf->bCharSet = pcfNew->bCharSet;
PaintSize(pfbs);
}

// Change in font size ?
if (pcf->yHeight != pcfNew->yHeight ||
(pcf->dwMask & CFM_SIZE) != (pcfNew->dwMask & CFM_SIZE))
{
pcf->dwMask &= ~CFM_SIZE;
pcf->dwMask |= pcfNew->dwMask & CFM_SIZE;
pcf->yHeight = pcfNew->yHeight;
PaintSize(pfbs);
}

// Change in font color ?
if (pcf->crTextColor != pcfNew->crTextColor ||
(pcf->dwMask & CFM_COLOR) != (pcfNew->dwMask & CFM_COLOR))
{
pcf->dwMask &= ~CFM_COLOR;
pcf->dwMask |= pcfNew->dwMask & CFM_COLOR;
pcf->crTextColor = pcfNew->crTextColor;
PaintColor(pfbs);
}

// Change in effects ?
dwMask = pcf->dwMask & CFM_MASKS;
dwEffects = pcf->dwEffects & CFE_EFFECTS;
if (((pcfNew->dwMask & CFM_MASKS) != dwMask) ||
((pcfNew->dwEffects & CFE_EFFECTS) != dwEffects))
{
pcf->dwMask &= ~CFM_MASKS;
pcf->dwMask |= pcfNew->dwMask & CFM_MASKS;
pcf->dwEffects = pcfNew->dwEffects & CFE_EFFECTS;
PaintEffects(pfbs);
}
}

/*
 *SetParaFormat
 *
 *Purpose:
 *Set the current char format
 *
 *Arguments:
 *hwndThe format bar window
 *ppfNewThe new formatting
 *
 *Returns:
 *None.
 */
LOCAL VOID SetParaFormat(HWND hwnd, PARAFORMAT *ppfNew)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
PARAFORMAT *ppf = &pfbs->pf;
DWORDdwMaskOld = ppf->dwMask;
BOOLfChanged = FALSE;

// update the mask for tri-state sake
ppf->dwMask = (ppf->dwMask & ~(PFM_NUMBERING | PFM_ALIGNMENT)) |
(ppfNew->dwMask & (PFM_NUMBERING | PFM_ALIGNMENT));

// Change in numbering ?
if (ppf->wNumbering != ppfNew->wNumbering)
{
const BOOL fBulleted = ppfNew->wNumbering == PFN_BULLET;

ppf->wNumbering = ppfNew->wNumbering;
SendMessage(pfbs->hwndToolbar, TB_CHECKBUTTON, TBI_Bullet, MAKELONG(fBulleted, 0));
}

// Change in alignment ?
if (ppf->wAlignment != ppfNew->wAlignment)
{
ppf->wAlignment = ppfNew->wAlignment;
fChanged |= TRUE;
}

if (fChanged || dwMaskOld != ppf->dwMask)
PaintAlignment(pfbs);
}

/*
 *LFormatBarWndProc
 *
 *Purpose:
 *Take care of handling the notifications from the combobox controls we
 *get from the format bar
 *
 *Arguments:
 *hwnd
 *wMsg
 *wParam
 *lParam
 *
 *Returns:
 *LRESULT
 */
LRESULT CALLBACK LFormatBarWndProc(HWND hwnd, UINT wMsg, WPARAM wParam,
LPARAM lParam)
{
FormatBarState *pfbs = PfbsGetWindowPtr(hwnd);
BOOL fShift;

switch(wMsg)
{
HANDLE_MSG(hwnd, WM_MEASUREITEM, FB_OnMeasureItem);

case WM_COMMAND:
FB_OnCommand(hwnd, GET_WM_COMMAND_ID(wParam, lParam),
 GET_WM_COMMAND_HWND(wParam, lParam),
 GET_WM_COMMAND_CMD(wParam, lParam));
return 0;

case WM_NCCREATE:
return FB_OnNcCreate(hwnd, (CREATESTRUCT *) lParam);

case WM_DRAWITEM:
FB_OnDrawItem(hwnd, (DRAWITEMSTRUCT *) lParam);
return TRUE;

case WM_PAINT:
FB_OnPaint(hwnd);
break;

case WM_DESTROY:
if (pfbs)
{
if (pfbs->hwndName)
DestroyWindow(pfbs->hwndName);
if (pfbs->hwndSize)
DestroyWindow(pfbs->hwndSize);
if (pfbs->hwndColor)
DestroyWindow(pfbs->hwndColor);
if (pfbs->hwndToolbar)
DestroyWindow(pfbs->hwndToolbar);
if (pfbs->hbrushWindow)
DeleteObject(pfbs->hbrushWindow);
if (pfbs->hbrushHighlight)
DeleteObject(pfbs->hbrushHighlight);
if (pfbs->hbrushButtonFace)
DeleteObject(pfbs->hbrushButtonFace);
if (pfbs->hdc)
DeleteDC(pfbs->hdc);
GlobalFreePtr(pfbs);
SetWindowPtr(hwnd, NULL);
}
break;

case WM_SHOWWINDOW:
if (pfbs)
{
INTnCmd =wParam ? SW_SHOW : SW_HIDE;

ShowWindow(pfbs->hwndToolbar, nCmd);
SendMessage(pfbs->hwndToolbar, WM_SIZE, 0, 0);
ShowWindow(pfbs->hwndName, nCmd);
ShowWindow(pfbs->hwndSize, nCmd);
ShowWindow(pfbs->hwndColor, nCmd);
}
return 0;

case WM_SIZE:
if (pfbs)
{
RECTrcMe;
RECTrcParent;

// Get our current dimensions
GetClientRect(pfbs->hwndToolbar, &rcMe);
rcMe.bottom += 3;

// We want to fit our parent's width
GetClientRect(GetParent(hwnd), &rcParent);

// Make it so
SetWindowPos(hwnd, HWND_TOP, 0, 0, rcParent.right - rcParent.left,
rcMe.bottom - rcMe.top, SWP_NOMOVE | SWP_NOZORDER);
}
return 0;

case WM_ENABLE:
if (pfbs)
{
INTnID;

EnableWindow(pfbs->hwndToolbar, wParam);
EnableWindow(pfbs->hwndName, wParam);
EnableWindow(pfbs->hwndSize, wParam);
EnableWindow(pfbs->hwndColor, wParam);
for (nID = TBI_Bold; nID <= TBI_Right; nID++)
SendMessage(pfbs->hwndToolbar, TB_ENABLEBUTTON, nID,
MAKELONG(wParam, 0));
}
break;

case WM_SYSCOLORCHANGE:
// Update our concept of the brushes
if (pfbs)
UpdateBrushes(hwnd);
break;

case EM_GETPARAFORMAT:
Assert(lParam);
if (lParam)
{
PARAFORMAT * const ppf = (PARAFORMAT *) lParam;

if(ppf->cbSize == sizeof(PARAFORMAT))
{
CopyMemory(((LPBYTE) ppf) + sizeof(ppf->cbSize),
((LPBYTE) &pfbs->pf) + sizeof(ppf->cbSize),
sizeof(PARAFORMAT) - sizeof(ppf->cbSize));
}
}
return 0;

case EM_SETPARAFORMAT:
Assert(lParam);
if (lParam)
SetParaFormat(hwnd, (PARAFORMAT *) lParam);
return TRUE;

case EM_GETCHARFORMAT:
Assert(lParam);
if (lParam)
{
CHARFORMAT * const pcf = (CHARFORMAT *) lParam;

if(pcf->cbSize == sizeof(CHARFORMAT))
{
CopyMemory(((LPBYTE) pcf) + sizeof(pcf->cbSize),
((LPBYTE) &pfbs->cf) + sizeof(pcf->cbSize),
sizeof(CHARFORMAT) - sizeof(pcf->cbSize));
pcf->dwMask &= pfbs->dwCFMaskChange;
TraceCharFormat("EM_GETCHARFORMAT", pcf);
}
}
return 0;

case EM_SETCHARFORMAT:
Assert(lParam);
if (lParam)
SetCharFormat(hwnd, (CHARFORMAT *) lParam);
return TRUE;

case WM_RETURN:
// The user made a choice
pfbs->fExpectChoice = TRUE;
pfbs->fGiveUpFocus = TRUE;
FB_OnCommand(hwnd, wParam, (HWND) lParam, CBN_SELCHANGE);
return 0;

case WM_TAB:
// Only change the choice if TAB was pressed on a closed dropdown
pfbs->fExpectChoice = !SendMessage((HWND) lParam, CB_GETDROPPEDSTATE,
0, 0);
FB_OnCommand(hwnd, wParam, (HWND) lParam, CBN_SELCHANGE);

// Change focus, FB_OnCommand will take care of copying changed data
fShift = GetKeyState(VK_SHIFT) & 0x8000 ? TRUE : FALSE;
switch (wParam)
{
case TBI_Name:
SetFocus(fShift ? pfbs->hwndColor : pfbs->hwndSize);
break;

case TBI_Size:
SetFocus(fShift ? pfbs->hwndName : pfbs->hwndColor);
break;

case TBI_Color:
SetFocus(fShift ? pfbs->hwndSize : pfbs->hwndName);
break;
}
return 0;

case WM_ESCAPE:
SetFocus(hwnd);
FB_OnPaint(hwnd);
return 0;
}

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

/*
 *FInitFormatBarClass()
 *
 *Purpose:
 *Superclasses the toolbar so that we get support for a format bar
 *
 *Arguments:
 *hinstThis instance
 *
 *Returns:
 *TRUE if class is successfully initialized.
 */
BOOL FInitFormatBarClass(HINSTANCE hinst)
{
WNDCLASS wc;

// Are format bars registered yet ?
if (!GetClassInfo(hinst, szFormatBar, &wc))
{
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = LFormatBarWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(FormatBarState *);
wc.hInstance = hinst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szFormatBar;

if (!RegisterClass(&wc))
{
TraceError("FInitFormatBarClass: Couldn't register", -1);
return FALSE;
}
}
return TRUE;
}

/*
 *FCreateFormatBar
 *
 *Purpose:
 *Creates a format bar
 *
 *Arguments:
 *hwndParentThe parent of this format bar
 *wIDThe control ID to be associated with this format bar
 *hdcA device context that we can copy so we can do various
 *font information functions
 *
 *Returns:
 *The window handle of the format bar
 */
HWND HwndCreateFormatBar(HWND hwndParent, WORD wID, HDC hdc)
{
HWNDhwndFormatBar = NULL;
HINSTANCEhinst = (HINSTANCE) GetWindowLong(hwndParent, GWL_HINSTANCE);
RECTrc;

// Initialize the format bar class
//$ REVIEW: Move into WinMain or LibMain
if (!FInitFormatBarClass(hinst))
{
TraceError("HwndCreateFormatBar: Not registered", -1);
return NULL;
}

// Create the main toolbar
GetClientRect(hwndParent, &rc);
hwndFormatBar = CreateWindow(szFormatBar, NULL, WS_CHILD | WS_VISIBLE,
0, 0, rc.right - rc.left, 29,
hwndParent, (HMENU) wID, hinst,
(LPVOID) (hdc ? &hdc : NULL));
if (!hwndFormatBar)
{
TraceError("HwndCreateFormatBar: Can't create", -1);
goto Error;
}

// Return a pointer to our state information
return hwndFormatBar;

Error:
return NULL;
}


FRMTBAR.H

/*
 *frmtbar.h
 *
 *Declarations for a richedit format bar
 *
 */

// Control IDs for the comboboxes. Returned to the user on selections
#defineTBI_Name21761
#defineTBI_Size21762
#defineTBI_Color21763

#define cxBulletIndent(1440 / 4)

BOOL FInitFormatBarClass(HINSTANCE hinst);
HWND HwndCreateFormatBar(HWND hwndParent, WORD wID, HDC hdc);

#defineFORMATBARCLASSNAME"REFormatBar"


MALSPY.H

//+---------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1993 - 1996.
//
//  File:       malspy.h
//
//  Contents:   CMallocSpy definitions
//
//----------------------------------------------------------------------

BOOL InitializeMallocSpy(void);

void UninitializeMallocSpy(void);


OLESTD.C

/*************************************************************************
**
**    OLE 2 Standard Utilities
**
**    olestd.c
**
**    This file contains utilities that are useful for most standard
**        OLE 2.0 compound document type applications.
**
**    (c) Copyright Microsoft Corp. 1992-1997 All Rights Reserved
**
*************************************************************************/

#define NONAMELESSUNION     // use strict ANSI standard (for DVOBJ.H)

#define STRICT  1
#include "olestd.h"
#include <stdlib.h>
#include <ctype.h>
#include <shellapi.h>
#include "common.h"

OLEDBGDATA

static char szAssertMemAlloc[] = "CoGetMalloc failed";
static int IsCloseFormatEtc(FORMATETC FAR* pFetcLeft, FORMATETC FAR* pFetcRight);

//The instance handle shared amongst all dialogs.
HINSTANCE     ghInst;

/* OleStdSwitchDisplayAspect
** -------------------------
**    Switch the currently cached display aspect between DVASPECT_ICON
**    and DVASPECT_CONTENT.
**
**    NOTE: when setting up icon aspect, any currently cached content
**    cache is discarded and any advise connections for content aspect
**    are broken.
**
**    RETURNS:
**      S_OK -- new display aspect setup successfully
**      E_INVALIDARG -- IOleCache interface is NOT supported (this is
**                  required).
**      <other SCODE> -- any SCODE that can be returned by
**                  IOleCache::Cache method.
**      NOTE: if an error occurs then the current display aspect and
**            cache contents unchanged.
*/
STDAPI OleStdSwitchDisplayAspect(
      LPOLEOBJECT             lpOleObj,
      LPDWORD                 lpdwCurAspect,
      DWORD                   dwNewAspect,
      HGLOBAL                 hMetaPict,
      BOOL                    fDeleteOldAspect,
      BOOL                    fSetupViewAdvise,
      LPADVISESINK            lpAdviseSink,
      BOOL FAR*               lpfMustUpdate
)
{
   LPOLECACHE      lpOleCache = NULL;
   LPVIEWOBJECT    lpViewObj = NULL;
   LPENUMSTATDATA  lpEnumStatData = NULL;
   STATDATA        StatData;
   FORMATETC       FmtEtc;
   STGMEDIUM       Medium;
   DWORD           dwAdvf;
   DWORD           dwNewConnection;
   DWORD           dwOldAspect = *lpdwCurAspect;
   HRESULT         hrErr;

   if (lpfMustUpdate)
      *lpfMustUpdate = FALSE;

   lpOleCache = (LPOLECACHE)OleStdQueryInterface(
                              (LPUNKNOWN)lpOleObj,&IID_IOleCache);

   // if IOleCache* is NOT available, do nothing
   if (! lpOleCache)
      return E_INVALIDARG;

   // Setup new cache with the new aspect
   FmtEtc.cfFormat = 0;     // whatever is needed to draw
   FmtEtc.ptd      = NULL;
   FmtEtc.dwAspect = dwNewAspect;
   FmtEtc.lindex   = -1;
   FmtEtc.tymed    = TYMED_NULL;

   /* NOTE: if we are setting up Icon aspect with a custom icon
   **    then we do not want DataAdvise notifications to ever change
   **    the contents of the data cache. thus we set up a NODATA
   **    advise connection. otherwise we set up a standard DataAdvise
   **    connection.
   */
   if (dwNewAspect == DVASPECT_ICON && hMetaPict)
      dwAdvf = ADVF_NODATA;
   else
      dwAdvf = ADVF_PRIMEFIRST;

   OLEDBG_BEGIN2("IOleCache::Cache called\r\n")
   hrErr = lpOleCache->lpVtbl->Cache(
         lpOleCache,
         (LPFORMATETC)&FmtEtc,
         dwAdvf,
         (LPDWORD)&dwNewConnection
   );
   OLEDBG_END2

   if (! SUCCEEDED(hrErr)) {
      OleDbgOutHResult("IOleCache::Cache returned", hrErr);
      OleStdRelease((LPUNKNOWN)lpOleCache);
      return hrErr;
   }

   *lpdwCurAspect = dwNewAspect;

   /* NOTE: if we are setting up Icon aspect with a custom icon,
   **    then stuff the icon into the cache. otherwise the cache must
   **    be forced to be updated. set the *lpfMustUpdate flag to tell
   **    caller to force the object to Run so that the cache will be
   **    updated.
   */
   if (dwNewAspect == DVASPECT_ICON && hMetaPict) {

      FmtEtc.cfFormat = CF_METAFILEPICT;
      FmtEtc.ptd      = NULL;
      FmtEtc.dwAspect = DVASPECT_ICON;
      FmtEtc.lindex   = -1;
      FmtEtc.tymed    = TYMED_MFPICT;

      Medium.tymed            = TYMED_MFPICT;
      Medium.u.hGlobal        = hMetaPict;
      Medium.pUnkForRelease   = NULL;

      OLEDBG_BEGIN2("IOleCache::SetData called\r\n")
      hrErr = lpOleCache->lpVtbl->SetData(
            lpOleCache,
            (LPFORMATETC)&FmtEtc,
            (LPSTGMEDIUM)&Medium,
            FALSE   /* fRelease */
      );
      OLEDBG_END2
   } else {
      if (lpfMustUpdate)
         *lpfMustUpdate = TRUE;
   }

   if (fSetupViewAdvise && lpAdviseSink) {
      /* NOTE: re-establish the ViewAdvise connection */
      lpViewObj = (LPVIEWOBJECT)OleStdQueryInterface(
                              (LPUNKNOWN)lpOleObj,&IID_IViewObject);

      if (lpViewObj) {

         OLEDBG_BEGIN2("IViewObject::SetAdvise called\r\n")
         lpViewObj->lpVtbl->SetAdvise(
               lpViewObj,
               dwNewAspect,
               0,
               lpAdviseSink
         );
         OLEDBG_END2

         OleStdRelease((LPUNKNOWN)lpViewObj);
      }
   }

   /* NOTE: remove any existing caches that are set up for the old
   **    display aspect. It WOULD be possible to retain the caches set
   **    up for the old aspect, but this would increase the storage
   **    space required for the object and possibly require additional
   **    overhead to maintain the unused cachaes. For these reasons the
   **    strategy to delete the previous caches is prefered. if it is a
   **    requirement to quickly switch between Icon and Content
   **    display, then it would be better to keep both aspect caches.
   */

   if (fDeleteOldAspect) {
      OLEDBG_BEGIN2("IOleCache::EnumCache called\r\n")
      hrErr = lpOleCache->lpVtbl->EnumCache(
            lpOleCache,
            (LPENUMSTATDATA FAR*)&lpEnumStatData
      );
      OLEDBG_END2

      while(hrErr == NOERROR) {
         hrErr = lpEnumStatData->lpVtbl->Next(
               lpEnumStatData,
               1,
               (LPSTATDATA)&StatData,
               NULL
         );
         if (hrErr != NOERROR)
            break;              // DONE! no more caches.

         if (StatData.formatetc.dwAspect == dwOldAspect) {

            // Remove previous cache with old aspect
            OLEDBG_BEGIN2("IOleCache::Uncache called\r\n")
            lpOleCache->lpVtbl->Uncache(lpOleCache,StatData.dwConnection);
            OLEDBG_END2
         }
      }

      if (lpEnumStatData) {
         OleStdVerifyRelease(
               (LPUNKNOWN)lpEnumStatData,
               OLESTR ("OleStdSwitchDisplayAspect: Cache enumerator NOT released"));
      }
   }

   if (lpOleCache)
      OleStdRelease((LPUNKNOWN)lpOleCache);

   return NOERROR;
}

/* OleStdQueryInterface
** --------------------
**    Returns the desired interface pointer if exposed by the given object.
**    Returns NULL if the interface is not available.
**    eg.:
**      lpDataObj = OleStdQueryInterface(lpOleObj, &IID_DataObject);
*/
STDAPI_(LPUNKNOWN) OleStdQueryInterface(LPUNKNOWN lpUnk, REFIID riid)
{
   LPUNKNOWN lpInterface;
   HRESULT hrErr;

   hrErr = lpUnk->lpVtbl->QueryInterface(
         lpUnk,
         riid,
         (LPVOID FAR*)&lpInterface
   );

   if (hrErr == NOERROR)
      return lpInterface;
   else
      return NULL;
}

/* OleStdFreeString
** ----------------
**    Free a string that was allocated with the currently active
**    IMalloc* allocator.
**
**    if the caller has the current IMalloc* handy, then it can be
**    passed as a argument, otherwise this function will retrieve the
**    active allocator and use it.
*/
STDAPI_(void) OleStdFreeString(LPOLESTR lpsz, LPMALLOC lpMalloc)
{
   BOOL fMustRelease = FALSE;

   if (! lpMalloc) {
      if (CoGetMalloc(MEMCTX_TASK, &lpMalloc) != NOERROR)
         return;
      fMustRelease = TRUE;
   }

   lpMalloc->lpVtbl->Free(lpMalloc, lpsz);

   if (fMustRelease)
      lpMalloc->lpVtbl->Release(lpMalloc);
}

/* Call Release on the object that is expected to go away.
**      if the refcnt of the object did no go to 0 then give a debug message.
*/
STDAPI_(ULONG) OleStdVerifyRelease(LPUNKNOWN lpUnk, LPOLESTR lpszMsg)
{
   ULONG cRef;
   char  lpszAnsiMsg[256];

   W2A (lpszMsg, lpszAnsiMsg, 256);
   cRef = lpUnk->lpVtbl->Release(lpUnk);

#if defined( _DEBUG )
   if (cRef != 0) {
      char szBuf[80];
      if (lpszMsg)
         MessageBox(NULL, lpszAnsiMsg, NULL, MB_ICONEXCLAMATION | MB_OK);
      wsprintf(
            /*(LPSTR)*/szBuf,
            "refcnt (%ld) != 0 after object (0x%lx) release\n",
            cRef,
            lpUnk
      );
      if (lpszMsg)
         OleDbgOut1(lpszAnsiMsg);
      OleDbgOut1(/*(LPSTR)*/szBuf);
      OleDbgAssertSz(cRef == 0, /*(LPSTR)*/szBuf);
   } else {
      char szBuf[80];
      wsprintf(
            /*(LPSTR)*/szBuf,
            "refcnt = 0 after object (0x%lx) release\n", lpUnk
      );
      OleDbgOut4(/*(LPSTR)*/szBuf);
   }
#endif
   return cRef;
}

/* Call Release on the object that is NOT necessarily expected to go away.
*/
STDAPI_(ULONG) OleStdRelease(LPUNKNOWN lpUnk)
{
   ULONG cRef;

   cRef = lpUnk->lpVtbl->Release(lpUnk);

#if defined( _DEBUG )
   {
      char szBuf[80];
      wsprintf(
            /*(LPSTR)*/szBuf,
            "refcnt = %ld after object (0x%lx) release\n",
            cRef,
            lpUnk
      );
      OleDbgOut4(/*(LPSTR)*/szBuf);
   }
#endif
   return cRef;
}

/*************************************************************************
** OleStdCreateRootStorage
**    create a root level Storage given a filename that is compatible
**    to be used by a top-level OLE container. if the filename
**    specifies an existing file, then an error is returned.
**    the root storage (Docfile) that is created by this function
**    is suitable to be used to create child storages for embedings.
**    (CreateChildStorage can be used to create child storages.)
**    NOTE: the root-level storage is opened in transacted mode.
*************************************************************************/

STDAPI_(LPSTORAGE) OleStdCreateRootStorage(LPOLESTR lpszStgName, DWORD grfMode)
{
   HRESULT hr;
   DWORD grfCreateMode = STGM_READWRITE | STGM_TRANSACTED;
   DWORD reserved = 0;
   LPSTORAGE lpRootStg;
   char szMsg[64];

   // if temp file is being created, enable delete-on-release
   if (! lpszStgName)
      grfCreateMode |= STGM_DELETEONRELEASE;

   hr = StgCreateDocfile(
         lpszStgName,
         grfMode | grfCreateMode,
         reserved,
         (LPSTORAGE FAR*)&lpRootStg
      );

   if (hr == NOERROR)
      return lpRootStg;               // existing file successfully opened

   OleDbgOutHResult("StgCreateDocfile returned", hr);

   if (0 == LoadString(ghInst, (UINT)IDS_OLESTDNOCREATEFILE, /*(LPSTR)*/szMsg, 64))
     return NULL;

   MessageBox(NULL, /*(LPSTR)*/szMsg, NULL,MB_ICONEXCLAMATION | MB_OK);
   return NULL;
}

/*
** OleStdCreateChildStorage
**    create a child Storage inside the given lpStg that is compatible
**    to be used by an embedded OLE object. the return value from this
**    function can be passed to OleCreateXXX functions.
**    NOTE: the child storage is opened in transacted mode.
*/
STDAPI_(LPSTORAGE) OleStdCreateChildStorage(LPSTORAGE lpStg, LPOLESTR lpszStgName)
{
   if (lpStg != NULL) {
      LPSTORAGE lpChildStg;
      DWORD grfMode = (STGM_READWRITE | STGM_TRANSACTED |
            STGM_SHARE_EXCLUSIVE);
      DWORD reserved = 0;

      HRESULT hrErr = lpStg->lpVtbl->CreateStorage(
            lpStg,
            lpszStgName,
            grfMode,
            reserved,
            reserved,
            (LPSTORAGE FAR*)&lpChildStg
         );

      if (hrErr == NOERROR)
         return lpChildStg;

      OleDbgOutHResult("lpStg->lpVtbl->CreateStorage returned", hrErr);
   }
   return NULL;
}

HRESULT HrGetClassFileA(LPCSTR szFilenameA, CLSID * pclsid)
{
    WCHAR lpszFilenameW[MAX_PATH];
    HRESULT hr;

    A2W (szFilenameA, lpszFilenameW, MAX_PATH);

    hr = GetClassFile(lpszFilenameW, pclsid);

    return hr;
}


OLESTD.H

/*************************************************************************
**
**    OLE 2.0 Utilities
**
**    olestd.h
**
**    This file contains file contains data structure defintions,
**    function prototypes, constants, etc. for the common OLE 2.0
**    utilities.
**
**    These utilities include the following:
**          Debuging Assert/Verify macros
**          HIMETRIC conversion routines
**          reference counting debug support
**          OleStd API's for common compound-document app support
**
**    (c) Copyright Microsoft Corp. 1990 - 1996 All Rights Reserved
**
*************************************************************************/

#if !defined( _OLESTD_H_ )
#define _OLESTD_H_

#if !defined(__cplusplus) && !defined( __TURBOC__)
#define NONAMELESSUNION     // use strict ANSI standard (for DVOBJ.H)
#endif

#include <windows.h>
#include <shellapi.h>
#include <ole2.h>
#include <string.h>
#include <dlgs.h>           //For fileopen dlg; standard include
#include "ansiapi.h"
#include "oledlg.h"

/*************************************************************************
** DEBUG ASSERTION ROUTINES
*************************************************************************/

#if DBG
#include <assert.h>
#define FnAssert(lpstrExpr, lpstrMsg, lpstrFileName, iLine)     \
        (_assert(lpstrMsg ? lpstrMsg : lpstrExpr,               \
                 lpstrFileName,                                 \
                 iLine), NOERROR)
#endif //DBG

// BEGINING OF OLD OLESTD.H FILE
#if defined( __TURBOC__ ) || defined( WIN32 )
#define _based(a)
#endif

#ifndef RC_INVOKED
#include <dos.h>        // needed for filetime
#endif  /* RC_INVOKED */

#include <commdlg.h>    // needed for LPPRINTDLG
#include <shellapi.h>   // needed for HKEY
#include "ansiapi.h"

// String table defines...
#define  IDS_OLESTDNOCREATEFILE   700
#define  IDS_OLESTDNOOPENFILE     701
#define  IDS_OLESTDDISKFULL       702

/*
 * Some C interface declaration stuff
 */

#if ! defined(__cplusplus)
typedef struct tagINTERFACEIMPL {
      IUnknownVtbl FAR*       lpVtbl;
      LPVOID                  lpBack;
      int                     cRef;   // interface specific ref count.
} INTERFACEIMPL, FAR* LPINTERFACEIMPL;

#define INIT_INTERFACEIMPL(lpIFace, pVtbl, pBack)   \
      ((lpIFace)->lpVtbl = pVtbl, \
         ((LPINTERFACEIMPL)(lpIFace))->lpBack = (LPVOID)pBack,   \
         ((LPINTERFACEIMPL)(lpIFace))->cRef = 0  \
      )

#if defined( _DEBUG )
#define OleDbgQueryInterfaceMethod(lpUnk)   \
      ((lpUnk) != NULL ? ((LPINTERFACEIMPL)(lpUnk))->cRef++ : 0)
#define OleDbgAddRefMethod(lpThis, iface)   \
      ((LPINTERFACEIMPL)(lpThis))->cRef++

#if _DEBUGLEVEL >= 2
#define OleDbgReleaseMethod(lpThis, iface) \
      (--((LPINTERFACEIMPL)(lpThis))->cRef == 0 ? \
         OleDbgOut("\t" iface "* RELEASED (cRef == 0)\r\n"),1 : \
          (((LPINTERFACEIMPL)(lpThis))->cRef < 0) ? \
            ( \
               DebugBreak(), \
               OleDbgOut(  \
                  "\tERROR: " iface "* RELEASED TOO MANY TIMES\r\n") \
            ),1 : \
            1)

#else       // if _DEBUGLEVEL < 2
#define OleDbgReleaseMethod(lpThis, iface) \
      (--((LPINTERFACEIMPL)(lpThis))->cRef == 0 ? \
         1 : \
          (((LPINTERFACEIMPL)(lpThis))->cRef < 0) ? \
            ( \
               OleDbgOut(  \
                  "\tERROR: " iface "* RELEASED TOO MANY TIMES\r\n") \
      ),1 : \
            1)

#endif      // if _DEBUGLEVEL < 2

#else       // ! defined (_DEBUG)

#define OleDbgQueryInterfaceMethod(lpUnk)
#define OleDbgAddRefMethod(lpThis, iface)
#define OleDbgReleaseMethod(lpThis, iface)

#endif      // if defined( _DEBUG )

#endif      // ! defined(__cplusplus)

/*
 * Some docfiles stuff
 */

#define STGM_DFRALL (STGM_READWRITE | STGM_TRANSACTED | STGM_SHARE_DENY_WRITE)
#define STGM_DFALL (STGM_READWRITE | STGM_TRANSACTED | STGM_SHARE_EXCLUSIVE)
#define STGM_SALL (STGM_READWRITE | STGM_SHARE_EXCLUSIVE)

/*
 * Some Concurrency stuff
 */

/* standard Delay (in msec) to wait before retrying an LRPC call.
**    this value is returned from IMessageFilter::RetryRejectedCall
*/
#define OLESTDRETRYDELAY    (DWORD)5000

/* Cancel the pending outgoing LRPC call.
**    this value is returned from IMessageFilter::RetryRejectedCall
*/
#define OLESTDCANCELRETRY   (DWORD)-1

/*
 * Some Icon support stuff.
 *
 * The following API's are now OBSOLETE because equivalent API's have been
 * added to the OLE2.DLL library
 *      GetIconOfFile       superceeded by OleGetIconOfFile
 *      GetIconOfClass      superceeded by OleGetIconOfClass
 *      OleUIMetafilePictFromIconAndLabel
 *                          superceeded by OleMetafilePictFromIconAndLabel
 *
 * The following macros are defined for backward compatibility with previous
 * versions of the OLE2UI library. It is recommended that the new Ole* API's
 * should be used instead.
 */
#define GetIconOfFile(hInst, lpszFileName, fUseFileAsLabel) \
   OleGetIconOfFile(lpszFileName, fUseFileAsLabel)

#define GetIconOfClass(hInst, rclsid, lpszLabel, fUseTypeAsLabel) \
   OleGetIconOfClass(rclsid, lpszLabel, fUseTypeAsLabel)

#define OleUIMetafilePictFromIconAndLabel(hIcon,pszLabel,pszSourceFile,iIcon)\
   OleMetafilePictFromIconAndLabel(hIcon, pszLabel, pszSourceFile, iIcon)

/*
 * Some Clipboard Copy/Paste & Drag/Drop support stuff
 */

//Macro to set all FormatEtc fields
#define SETFORMATETC(fe, cf, asp, td, med, li)   \
   ((fe).cfFormat=cf, \
    (fe).dwAspect=asp, \
    (fe).ptd=td, \
    (fe).tymed=med, \
    (fe).lindex=li)

//Macro to set interesting FormatEtc fields defaulting the others.
#define SETDEFAULTFORMATETC(fe, cf, med)  \
   ((fe).cfFormat=cf, \
    (fe).dwAspect=DVASPECT_CONTENT, \
    (fe).ptd=NULL, \
    (fe).tymed=med, \
    (fe).lindex=-1)

// Macro to test if two FormatEtc structures are an exact match
#define IsEqualFORMATETC(fe1, fe2)  \
   (OleStdCompareFormatEtc(&(fe1), &(fe2))==0)

// Clipboard format strings
#define CF_EMBEDSOURCE      TEXT("Embed Source")
#define CF_EMBEDDEDOBJECT   TEXT("Embedded Object")
#define CF_LINKSOURCE       TEXT("Link Source")
#define CF_CUSTOMLINKSOURCE TEXT("Custom Link Source")
#define CF_OBJECTDESCRIPTOR TEXT("Object Descriptor")
#define CF_LINKSRCDESCRIPTOR TEXT("Link Source Descriptor")
#define CF_OWNERLINK        TEXT("OwnerLink")
#define CF_FILENAME         TEXT("FileName")

#define OleStdQueryOleObjectData(lpformatetc)   \
   (((lpformatetc)->tymed & TYMED_ISTORAGE) ?    \
         NOERROR : ResultFromScode(DV_E_FORMATETC))

#define OleStdQueryLinkSourceData(lpformatetc)   \
   (((lpformatetc)->tymed & TYMED_ISTREAM) ?    \
         NOERROR : ResultFromScode(DV_E_FORMATETC))

#define OleStdQueryObjectDescriptorData(lpformatetc)    \
   (((lpformatetc)->tymed & TYMED_HGLOBAL) ?    \
         NOERROR : ResultFromScode(DV_E_FORMATETC))

#define OleStdQueryFormatMedium(lpformatetc, tymd)  \
   (((lpformatetc)->tymed & tymd) ?    \
         NOERROR : ResultFromScode(DV_E_FORMATETC))

// Make an independent copy of a MetafilePict
#define OleStdCopyMetafilePict(hpictin, phpictout)  \
   (*(phpictout) = OleDuplicateData(hpictin,CF_METAFILEPICT,GHND|GMEM_SHARE))

// REVIEW: these need to be added to OLE2.H
#if !defined( DD_DEFSCROLLINTERVAL )
#define DD_DEFSCROLLINTERVAL    50
#endif

#if !defined( DD_DEFDRAGDELAY )
#define DD_DEFDRAGDELAY         200
#endif

#if !defined( DD_DEFDRAGMINDIST )
#define DD_DEFDRAGMINDIST       2
#endif

/* OleStdGetDropEffect
** -------------------
**
** Convert a keyboard state into a DROPEFFECT.
**
** returns the DROPEFFECT value derived from the key state.
**    the following is the standard interpretation:
**          no modifier -- Default Drop     (NULL is returned)
**          CTRL        -- DROPEFFECT_COPY
**          SHIFT       -- DROPEFFECT_MOVE
**          CTRL-SHIFT  -- DROPEFFECT_LINK
**
**    Default Drop: this depends on the type of the target application.
**    this is re-interpretable by each target application. a typical
**    interpretation is if the drag is local to the same document
**    (which is source of the drag) then a MOVE operation is
**    performed. if the drag is not local, then a COPY operation is
**    performed.
*/
#define OleStdGetDropEffect(grfKeyState)    \
   ( (grfKeyState & MK_CONTROL) ?          \
      ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_LINK : DROPEFFECT_COPY ) :  \
      ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_MOVE : 0 ) )

#define OLESTDDROP_NONE         0
#define OLESTDDROP_DEFAULT      1
#define OLESTDDROP_NONDEFAULT   2

/*
 * Some misc stuff
 */

#define EMBEDDINGFLAG "Embedding"     // Cmd line switch for launching a srvr

#define HIMETRIC_PER_INCH   2540      // number HIMETRIC units per inch
#define PTS_PER_INCH        72        // number points (font size) per inch

#define MAP_PIX_TO_LOGHIM(x,ppli)   MulDiv(HIMETRIC_PER_INCH, (x), (ppli))
#define MAP_LOGHIM_TO_PIX(x,ppli)   MulDiv((ppli), (x), HIMETRIC_PER_INCH)

// Returns TRUE if all fields of the two Rect's are equal, else FALSE.
#define AreRectsEqual(lprc1, lprc2)     \
   (((lprc1->top == lprc2->top) &&     \
     (lprc1->left == lprc2->left) &&   \
     (lprc1->right == lprc2->right) && \
     (lprc1->bottom == lprc2->bottom)) ? TRUE : FALSE)

#define LSTRCPYN(lpdst, lpsrc, cch) \
(\
   (lpdst)[(cch)-1] = '\0', \
   ((cch)>1 ? _fstrncpy(lpdst, lpsrc, (cch)-1) : 0)\
)

/****** DEBUG Stuff *****************************************************/

#ifdef _DEBUG

#if !defined( _DBGTRACE )
#define _DEBUGLEVEL 2
#else
#define _DEBUGLEVEL _DBGTRACE
#endif

#if defined( NOASSERT )

#define OLEDBGASSERTDATA
#define OleDbgAssert(a)
#define OleDbgAssertSz(a, b)
#define OleDbgVerify(a)
#define OleDbgVerifySz(a, b)

#else   // ! NOASSERT

#define OLEDBGASSERTDATA    \
      static char _based(_segname("_CODE")) _szAssertFile[]= __FILE__;

#define OleDbgAssert(a) \
      (!(a) ? FnAssert(#a, NULL, _szAssertFile, __LINE__) : (HRESULT)1)

#define OleDbgAssertSz(a, b)    \
      (!(a) ? FnAssert(#a, b, _szAssertFile, __LINE__) : (HRESULT)1)

#define OleDbgVerify(a) \
      OleDbgAssert(a)

#define OleDbgVerifySz(a, b)    \
      OleDbgAssertSz(a, b)

#endif  // ! NOASSERT

#define OLEDBGDATA_MAIN(szPrefix)   \
      char near g_szDbgPrefix[] = szPrefix;    \
      OLEDBGASSERTDATA
#define OLEDBGDATA  \
      extern char near g_szDbgPrefix[];    \
      OLEDBGASSERTDATA

#define OLEDBG_BEGIN(lpsz) \
      OleDbgPrintAlways(g_szDbgPrefix,lpsz,1);

#define OLEDBG_END  \
      OleDbgPrintAlways(g_szDbgPrefix,"End\r\n",-1);

#define OleDbgOut(lpsz) \
      OleDbgPrintAlways(g_szDbgPrefix,lpsz,0)

#define OleDbgOutNoPrefix(lpsz) \
      OleDbgPrintAlways("",lpsz,0)

#define OleDbgOutRefCnt(lpsz,lpObj,refcnt)      \
      OleDbgPrintRefCntAlways(g_szDbgPrefix,lpsz,lpObj,(ULONG)refcnt)

#define OleDbgOutRect(lpsz,lpRect)      \
      OleDbgPrintRectAlways(g_szDbgPrefix,lpsz,lpRect)

#define OleDbgOutHResult(lpsz,hr)   \
      OleDbgPrintScodeAlways(g_szDbgPrefix,lpsz,GetScode(hr))

#define OleDbgOutScode(lpsz,sc) \
      OleDbgPrintScodeAlways(g_szDbgPrefix,lpsz,sc)

#define OleDbgOut1(lpsz)    \
      OleDbgPrint(1,g_szDbgPrefix,lpsz,0)

#define OleDbgOutNoPrefix1(lpsz)    \
      OleDbgPrint(1,"",lpsz,0)

#define OLEDBG_BEGIN1(lpsz)    \
      OleDbgPrint(1,g_szDbgPrefix,lpsz,1);

#define OLEDBG_END1 \
      OleDbgPrint(1,g_szDbgPrefix,"End\r\n",-1);

#define OleDbgOutRefCnt1(lpsz,lpObj,refcnt)     \
      OleDbgPrintRefCnt(1,g_szDbgPrefix,lpsz,lpObj,(ULONG)refcnt)

#define OleDbgOutRect1(lpsz,lpRect)     \
      OleDbgPrintRect(1,g_szDbgPrefix,lpsz,lpRect)

#define OleDbgOut2(lpsz)    \
      OleDbgPrint(2,g_szDbgPrefix,lpsz,0)

#define OleDbgOutNoPrefix2(lpsz)    \
      OleDbgPrint(2,"",lpsz,0)

#define OLEDBG_BEGIN2(lpsz)    \
      OleDbgPrint(2,g_szDbgPrefix,lpsz,1);

#define OLEDBG_END2 \
      OleDbgPrint(2,g_szDbgPrefix,"End\r\n",-1);

#define OleDbgOutRefCnt2(lpsz,lpObj,refcnt)     \
      OleDbgPrintRefCnt(2,g_szDbgPrefix,lpsz,lpObj,(ULONG)refcnt)

#define OleDbgOutRect2(lpsz,lpRect)     \
      OleDbgPrintRect(2,g_szDbgPrefix,lpsz,lpRect)

#define OleDbgOut3(lpsz)    \
      OleDbgPrint(3,g_szDbgPrefix,lpsz,0)

#define OleDbgOutNoPrefix3(lpsz)    \
      OleDbgPrint(3,"",lpsz,0)

#define OLEDBG_BEGIN3(lpsz)    \
      OleDbgPrint(3,g_szDbgPrefix,lpsz,1);

#define OLEDBG_END3 \
      OleDbgPrint(3,g_szDbgPrefix,"End\r\n",-1);

#define OleDbgOutRefCnt3(lpsz,lpObj,refcnt)     \
      OleDbgPrintRefCnt(3,g_szDbgPrefix,lpsz,lpObj,(ULONG)refcnt)

#define OleDbgOutRect3(lpsz,lpRect)     \
      OleDbgPrintRect(3,g_szDbgPrefix,lpsz,lpRect)

#define OleDbgOut4(lpsz)    \
      OleDbgPrint(4,g_szDbgPrefix,lpsz,0)

#define OleDbgOutNoPrefix4(lpsz)    \
      OleDbgPrint(4,"",lpsz,0)

#define OLEDBG_BEGIN4(lpsz)    \
      OleDbgPrint(4,g_szDbgPrefix,lpsz,1);

#define OLEDBG_END4 \
      OleDbgPrint(4,g_szDbgPrefix,"End\r\n",-1);

#define OleDbgOutRefCnt4(lpsz,lpObj,refcnt)     \
      OleDbgPrintRefCnt(4,g_szDbgPrefix,lpsz,lpObj,(ULONG)refcnt)

#define OleDbgOutRect4(lpsz,lpRect)     \
      OleDbgPrintRect(4,g_szDbgPrefix,lpsz,lpRect)

#else   //  !_DEBUG

#define OLEDBGDATA_MAIN(szPrefix)
#define OLEDBGDATA
#define OleDbgAssert(a)
#define OleDbgAssertSz(a, b)
#define OleDbgVerify(a)         (a)
#define OleDbgVerifySz(a, b)    (a)
#define OleDbgOutHResult(lpsz,hr)
#define OleDbgOutScode(lpsz,sc)
#define OLEDBG_BEGIN(lpsz)
#define OLEDBG_END
#define OleDbgOut(lpsz)
#define OleDbgOut1(lpsz)
#define OleDbgOut2(lpsz)
#define OleDbgOut3(lpsz)
#define OleDbgOut4(lpsz)
#define OleDbgOutNoPrefix(lpsz)
#define OleDbgOutNoPrefix1(lpsz)
#define OleDbgOutNoPrefix2(lpsz)
#define OleDbgOutNoPrefix3(lpsz)
#define OleDbgOutNoPrefix4(lpsz)
#define OLEDBG_BEGIN1(lpsz)
#define OLEDBG_BEGIN2(lpsz)
#define OLEDBG_BEGIN3(lpsz)
#define OLEDBG_BEGIN4(lpsz)
#define OLEDBG_END1
#define OLEDBG_END2
#define OLEDBG_END3
#define OLEDBG_END4
#define OleDbgOutRefCnt(lpsz,lpObj,refcnt)
#define OleDbgOutRefCnt1(lpsz,lpObj,refcnt)
#define OleDbgOutRefCnt2(lpsz,lpObj,refcnt)
#define OleDbgOutRefCnt3(lpsz,lpObj,refcnt)
#define OleDbgOutRefCnt4(lpsz,lpObj,refcnt)
#define OleDbgOutRect(lpsz,lpRect)
#define OleDbgOutRect1(lpsz,lpRect)
#define OleDbgOutRect2(lpsz,lpRect)
#define OleDbgOutRect3(lpsz,lpRect)
#define OleDbgOutRect4(lpsz,lpRect)

#endif  //  _DEBUG

/*************************************************************************
** Function prototypes
*************************************************************************/

//OLESTD.C
STDAPI_(int) SetDCToAnisotropic(HDC hDC, LPRECT lprcPhysical, LPRECT lprcLogical, LPRECT lprcWindowOld, LPRECT lprcViewportOld);
STDAPI_(int) SetDCToDrawInHimetricRect(HDC, LPRECT, LPRECT, LPRECT, LPRECT);
STDAPI_(int) ResetOrigDC(HDC, int, LPRECT, LPRECT);

STDAPI_(int)        XformWidthInHimetricToPixels(HDC, int);
STDAPI_(int)        XformWidthInPixelsToHimetric(HDC, int);
STDAPI_(int)        XformHeightInHimetricToPixels(HDC, int);
STDAPI_(int)        XformHeightInPixelsToHimetric(HDC, int);

STDAPI_(void) XformRectInPixelsToHimetric(HDC, LPRECT, LPRECT);
STDAPI_(void) XformRectInHimetricToPixels(HDC, LPRECT, LPRECT);
STDAPI_(void) XformSizeInPixelsToHimetric(HDC, LPSIZEL, LPSIZEL);
STDAPI_(void) XformSizeInHimetricToPixels(HDC, LPSIZEL, LPSIZEL);
STDAPI_(int) XformWidthInHimetricToPixels(HDC, int);
STDAPI_(int) XformWidthInPixelsToHimetric(HDC, int);
STDAPI_(int) XformHeightInHimetricToPixels(HDC, int);
STDAPI_(int) XformHeightInPixelsToHimetric(HDC, int);

STDAPI_(void) ParseCmdLine(LPSTR, BOOL FAR *, LPSTR);

STDAPI_(BOOL) OleStdIsOleLink(LPUNKNOWN lpUnk);
STDAPI_(LPUNKNOWN) OleStdQueryInterface(LPUNKNOWN lpUnk, REFIID riid);
STDAPI_(LPSTORAGE) OleStdCreateRootStorage(LPOLESTR lpszStgName, DWORD grfMode);
STDAPI_(LPSTORAGE) OleStdOpenRootStorage(LPOLESTR lpszStgName, DWORD grfMode);
STDAPI_(LPSTORAGE) OleStdOpenOrCreateRootStorage(LPOLESTR lpszStgName, DWORD grfMode);
STDAPI_(LPSTORAGE) OleStdCreateChildStorage(LPSTORAGE lpStg, LPOLESTR lpszStgName);
STDAPI_(LPSTORAGE) OleStdOpenChildStorage(LPSTORAGE lpStg, LPOLESTR lpszStgName, DWORD grfMode);
STDAPI_(BOOL) OleStdCommitStorage(LPSTORAGE lpStg);
STDAPI OleStdDestroyAllElements(LPSTORAGE lpStg);

STDAPI_(LPSTORAGE) OleStdCreateStorageOnHGlobal(
      HANDLE hGlobal,
      BOOL fDeleteOnRelease,
      DWORD dwgrfMode
);
STDAPI_(LPSTORAGE) OleStdCreateTempStorage(BOOL fUseMemory, DWORD grfMode);
STDAPI OleStdDoConvert(LPSTORAGE lpStg, REFCLSID rClsidNew);
STDAPI_(BOOL) OleStdGetTreatAsFmtUserType(
      REFCLSID        rClsidApp,
      LPSTORAGE       lpStg,
      CLSID FAR*      lpclsid,
      CLIPFORMAT FAR* lpcfFmt,
      LPOLESTR FAR*      lplpszType
);
STDAPI OleStdDoTreatAsClass(LPOLESTR lpszUserType, REFCLSID rclsid, REFCLSID rclsidNew);
STDAPI_(BOOL) OleStdSetupAdvises(LPOLEOBJECT lpOleObject, DWORD dwDrawAspect,
               LPOLESTR lpszContainerApp, LPOLESTR lpszContainerObj,
               LPADVISESINK lpAdviseSink, BOOL fCreate);
STDAPI OleStdSwitchDisplayAspect(
      LPOLEOBJECT             lpOleObj,
      LPDWORD                 lpdwCurAspect,
      DWORD                   dwNewAspect,
      HGLOBAL                 hMetaPict,
      BOOL                    fDeleteOldAspect,
      BOOL                    fSetupViewAdvise,
      LPADVISESINK            lpAdviseSink,
      BOOL FAR*               lpfMustUpdate
);
STDAPI OleStdSetIconInCache(LPOLEOBJECT lpOleObj, HGLOBAL hMetaPict);
STDAPI_(HGLOBAL) OleStdGetData(
      LPDATAOBJECT        lpDataObj,
      CLIPFORMAT          cfFormat,
      DVTARGETDEVICE FAR* lpTargetDevice,
      DWORD               dwAspect,
      LPSTGMEDIUM         lpMedium
);
STDAPI_(void) OleStdMarkPasteEntryList(
      LPDATAOBJECT        lpSrcDataObj,
      LPOLEUIPASTEENTRY   lpPriorityList,
      int                 cEntries
);
STDAPI_(int) OleStdGetPriorityClipboardFormat(
      LPDATAOBJECT        lpSrcDataObj,
      LPOLEUIPASTEENTRY   lpPriorityList,
      int                 cEntries
);
STDAPI_(BOOL) OleStdIsDuplicateFormat(
      LPFORMATETC         lpFmtEtc,
      LPFORMATETC         arrFmtEtc,
      int                 nFmtEtc
);
STDAPI_(void) OleStdRegisterAsRunning(LPUNKNOWN lpUnk, LPMONIKER lpmkFull, DWORD FAR* lpdwRegister);
STDAPI_(void) OleStdRevokeAsRunning(DWORD FAR* lpdwRegister);
STDAPI_(void) OleStdNoteFileChangeTime(LPOLESTR lpszFileName, DWORD dwRegister);
STDAPI_(void) OleStdNoteObjectChangeTime(DWORD dwRegister);
STDAPI OleStdGetOleObjectData(
      LPPERSISTSTORAGE    lpPStg,
      LPFORMATETC         lpformatetc,
      LPSTGMEDIUM         lpMedium,
      BOOL                fUseMemory
);
STDAPI OleStdGetLinkSourceData(
      LPMONIKER           lpmk,
      LPCLSID             lpClsID,
      LPFORMATETC         lpformatetc,
      LPSTGMEDIUM         lpMedium
);
STDAPI_(HGLOBAL) OleStdGetObjectDescriptorData(
      CLSID               clsid,
      DWORD               dwAspect,
      SIZEL               sizel,
      POINTL              pointl,
      DWORD               dwStatus,
      LPOLESTR            lpszFullUserTypeName,
      LPOLESTR            lpszSrcOfCopy
);
STDAPI_(HGLOBAL) OleStdGetObjectDescriptorDataFromOleObject(
      LPOLEOBJECT         lpOleObj,
      LPOLESTR            lpszSrcOfCopy,
      DWORD               dwAspect,
      POINTL              pointl,
      LPSIZEL             lpSizelHim
);
STDAPI_(HGLOBAL) OleStdFillObjectDescriptorFromData(
      LPDATAOBJECT       lpDataObject,
      LPSTGMEDIUM        lpmedium,
      CLIPFORMAT FAR*    lpcfFmt
);
STDAPI_(HANDLE) OleStdGetMetafilePictFromOleObject(
      LPOLEOBJECT         lpOleObj,
      DWORD               dwDrawAspect,
      LPSIZEL             lpSizelHim,
      DVTARGETDEVICE FAR* ptd
);

STDAPI_(void) OleStdCreateTempFileMoniker(LPOLESTR lpszPrefixString, UINT FAR* lpuUnique, LPOLESTR lpszName, LPMONIKER FAR* lplpmk);
STDAPI_(LPMONIKER) OleStdGetFirstMoniker(LPMONIKER lpmk);
STDAPI_(ULONG) OleStdGetLenFilePrefixOfMoniker(LPMONIKER lpmk);
STDAPI OleStdMkParseDisplayName(
      REFCLSID        rClsid,
      LPBC            lpbc,
      LPOLESTR        lpszUserName,
      ULONG FAR*      lpchEaten,
      LPMONIKER FAR*  lplpmk
);
STDAPI_(LPVOID) OleStdMalloc(ULONG ulSize);
STDAPI_(LPVOID) OleStdRealloc(LPVOID pmem, ULONG ulSize);
STDAPI_(void) OleStdFree(LPVOID pmem);
STDAPI_(ULONG) OleStdGetSize(LPVOID pmem);
STDAPI_(void) OleStdFreeString(LPOLESTR lpsz, LPMALLOC lpMalloc);
STDAPI_(LPOLESTR) OleStdCopyString(LPOLESTR lpszSrc, LPMALLOC lpMalloc);
STDAPI_(ULONG) OleStdGetItemToken(LPOLESTR lpszSrc, LPOLESTR lpszDst,int nMaxChars);

STDAPI_(UINT)     OleStdIconLabelTextOut(HDC        hDC,
                               HFONT      hFont,
                               int        nXStart,
                               int        nYStart,
                               UINT       fuOptions,
                               RECT FAR * lpRect,
                               LPOLESTR   lpszString,
                               UINT       cchString,
                               int FAR *  lpDX);

// registration database query functions
STDAPI_(UINT)     OleStdGetAuxUserType(REFCLSID rclsid,
                             WORD   wAuxUserType,
                             LPOLESTR  lpszAuxUserType,
                             int    cch,
                             HKEY   hKey);

STDAPI_(UINT)     OleStdGetUserTypeOfClass(REFCLSID rclsid,
                                 LPOLESTR lpszUserType,
                                 UINT cch,
                                 HKEY hKey);

STDAPI_(BOOL) OleStdGetMiscStatusOfClass(REFCLSID, HKEY, DWORD FAR *);
STDAPI_(CLIPFORMAT) OleStdGetDefaultFileFormatOfClass(
      REFCLSID        rclsid,
      HKEY            hKey
);

STDAPI_(void) OleStdInitVtbl(LPVOID lpVtbl, UINT nSizeOfVtbl);
STDMETHODIMP OleStdNullMethod(LPUNKNOWN lpThis);
STDAPI_(BOOL) OleStdCheckVtbl(LPVOID lpVtbl, UINT nSizeOfVtbl, LPOLESTR lpszIface);
STDAPI_(ULONG) OleStdVerifyRelease(LPUNKNOWN lpUnk, LPOLESTR lpszMsg);
STDAPI_(ULONG) OleStdRelease(LPUNKNOWN lpUnk);

STDAPI_(HDC) OleStdCreateDC(DVTARGETDEVICE FAR* ptd);
STDAPI_(HDC) OleStdCreateIC(DVTARGETDEVICE FAR* ptd);
STDAPI_(DVTARGETDEVICE FAR*) OleStdCreateTargetDevice(LPPRINTDLG lpPrintDlg);
STDAPI_(BOOL) OleStdDeleteTargetDevice(DVTARGETDEVICE FAR* ptd);
STDAPI_(DVTARGETDEVICE FAR*) OleStdCopyTargetDevice(DVTARGETDEVICE FAR* ptdSrc);
STDAPI_(BOOL) OleStdCopyFormatEtc(LPFORMATETC petcDest, LPFORMATETC petcSrc);
STDAPI_(int) OleStdCompareFormatEtc(FORMATETC FAR* pFetcLeft, FORMATETC FAR* pFetcRight);
STDAPI_(BOOL) OleStdCompareTargetDevice
   (DVTARGETDEVICE FAR* ptdLeft, DVTARGETDEVICE FAR* ptdRight);

HRESULT HrGetClassFileA(LPCSTR, CLSID*);

STDAPI_(void) OleDbgPrint(
      int     nDbgLvl,
      LPSTR   lpszPrefix,
      LPSTR   lpszMsg,
      int     nIndent
);
STDAPI_(void) OleDbgPrintAlways(LPSTR lpszPrefix, LPSTR lpszMsg, int nIndent);
STDAPI_(void) OleDbgSetDbgLevel(int nDbgLvl);
STDAPI_(int) OleDbgGetDbgLevel( void );
STDAPI_(void) OleDbgIndent(int n);
STDAPI_(void) OleDbgPrintRefCnt(
      int         nDbgLvl,
      LPSTR       lpszPrefix,
      LPSTR       lpszMsg,
      LPVOID      lpObj,
      ULONG       refcnt
);
STDAPI_(void) OleDbgPrintRefCntAlways(
      LPSTR       lpszPrefix,
      LPSTR       lpszMsg,
      LPVOID      lpObj,
      ULONG       refcnt
);
STDAPI_(void) OleDbgPrintRect(
      int         nDbgLvl,
      LPSTR       lpszPrefix,
      LPSTR       lpszMsg,
      LPRECT      lpRect
);
STDAPI_(void) OleDbgPrintRectAlways(
      LPSTR       lpszPrefix,
      LPSTR       lpszMsg,
      LPRECT      lpRect
);
STDAPI_(void) OleDbgPrintScodeAlways(LPSTR lpszPrefix, LPSTR lpszMsg, SCODE sc);

STDAPI_(LPENUMFORMATETC)
  OleStdEnumFmtEtc_Create(ULONG nCount, LPFORMATETC lpEtc);

STDAPI_(LPENUMSTATDATA)
  OleStdEnumStatData_Create(ULONG nCount, LPSTATDATA lpStat);

STDAPI_(BOOL)
  OleStdCopyStatData(LPSTATDATA pDest, LPSTATDATA pSrc);

STDAPI_(HPALETTE)
  OleStdCreateStandardPalette(void);

#if defined( OBSOLETE )

/*************************************************************************
** The following API's have been converted into macros:
**          OleStdQueryOleObjectData
**          OleStdQueryLinkSourceData
**          OleStdQueryObjectDescriptorData
**          OleStdQueryFormatMedium
**          OleStdCopyMetafilePict
**          AreRectsEqual
**          OleStdGetDropEffect
**
**    These macros are defined above
*************************************************************************/
STDAPI_(BOOL) AreRectsEqual(LPRECT lprc1, LPRECT lprc2);
STDAPI_(BOOL) OleStdCopyMetafilePict(HANDLE hpictin, HANDLE FAR* phpictout);
STDAPI OleStdQueryOleObjectData(LPFORMATETC lpformatetc);
STDAPI OleStdQueryLinkSourceData(LPFORMATETC lpformatetc);
STDAPI OleStdQueryObjectDescriptorData(LPFORMATETC lpformatetc);
STDAPI OleStdQueryFormatMedium(LPFORMATETC lpformatetc, TYMED tymed);
STDAPI_(DWORD) OleStdGetDropEffect ( DWORD grfKeyState );
#endif  // OBSOLETE

// END OF OLD OLESTD.H FILE

#define UPDATELINKS_STARTDELAY 2000 // delay before first link

#ifdef __TURBOC__
#define _getcwd getcwd
#define _itoa   itoa
#define __max   max
#define _find_t find_t
#endif // __TURBOC__

#ifdef WIN32
#define _fmemset memset
#define _fmemcpy memcpy
#define _fmemcmp memcmp
#define _fstrcpy strcpy
#define _fstrncpy strncpy
#define _fstrlen strlen
#define _fstrrchr strrchr
#define _fstrtok strtok

// BUGBUG32: isspace function does not seem to work properly
#undef isspace
#define isspace(j) (j==' ' || j=='\t' || j=='\n')
#endif  // WIN32

#if !defined( EXPORT )
#ifdef WIN32
#define EXPORT
#else
#define EXPORT  __export
#endif  // WIN32
#endif  // !EXPORT

/*
 * Initialization / Uninitialization routines.  OleStdInitialize
 * must be called prior to using any functions in OLESTD.LIB.
 */

STDAPI_(BOOL) OleStdInitialize(HINSTANCE hInstance);
STDAPI_(void) OleStdUninitialize(void);

// object count, used to support DllCanUnloadNow and OleUICanUnloadNow
extern DWORD g_dwObjectCount;

STDAPI OleUICanUnloadNow(void);
STDAPI OleUILockLibrary(BOOL fLock);

//Dialog Identifiers as passed in Help messages to identify the source.
#define IDD_FILEOPEN            32253

// The following Dialogs are message dialogs used by OleUIPromptUser API

// Stringtable identifers
#define IDS_OLE2UIUNKNOWN       32300
#define IDS_OLE2UILINK          32301
#define IDS_OLE2UIOBJECT        32302
#define IDS_OLE2UIEDIT          32303
#define IDS_OLE2UICONVERT       32304
#define IDS_OLE2UIEDITLINKCMD_1VERB     32305
#define IDS_OLE2UIEDITOBJECTCMD_1VERB   32306
#define IDS_OLE2UIEDITLINKCMD_NVERB     32307
#define IDS_OLE2UIEDITOBJECTCMD_NVERB   32308
#define IDS_OLE2UIEDITNOOBJCMD  32309
// def. icon label (usu. "Document")
#define IDS_DEFICONLABEL        32310
#define IDS_OLE2UIPASTELINKEDTYPE  32311

#define IDS_FILTERS             32320
#define IDS_ICONFILTERS         32321
#define IDS_BROWSE              32322

//Resource identifiers for bitmaps
#define IDB_RESULTSEGA                  32325
#define IDB_RESULTSVGA                  32326
#define IDB_RESULTSHIRESVGA             32327

//Missing from windows.h
#ifndef PVOID
typedef VOID *PVOID;
#endif

//Hook type used in all structures.
typedef UINT (CALLBACK *LPFNOLEUIHOOK)(HWND, UINT, WPARAM, LPARAM);

//Strings for registered messages
#define SZOLEUI_MSG_FILEOKSTRING        "OLEUI_MSG_FILEOKSTRING"

//Help Button Identifier
#define ID_OLEUIHELP                    99

// Static text control (use this instead of -1 so things work correctly for
// localization
#define  ID_STATIC                      98

//Maximum key size we read from the RegDB.
#define OLEUI_CCHKEYMAX                 256  // make any changes to this in geticon.c too

//Maximum verb length and length of Object menu
#define OLEUI_CCHVERBMAX                32
#define OLEUI_OBJECTMENUMAX             256

//Maximum MS-DOS pathname.
#define OLEUI_CCHPATHMAX                256 // make any changes to this in geticon.c too
#define OLEUI_CCHFILEMAX                13

//Icon label length
#define OLEUI_CCHLABELMAX               40  // make any changes to this in geticon.c too

//Length of the CLSID string
#define OLEUI_CCHCLSIDSTRING            39

//Metafile utility functions
WINOLEAPI_(HGLOBAL) OleUIMetafilePictFromIconAndLabel(HICON, LPOLESTR, LPOLESTR, UINT);
STDAPI_(void)    OleUIMetafilePictIconFree(HGLOBAL);
STDAPI_(BOOL)    OleUIMetafilePictIconDraw(HDC, LPRECT, HGLOBAL, BOOL);
STDAPI_(UINT)    OleUIMetafilePictExtractLabel(HGLOBAL, LPOLESTR, UINT, LPDWORD);

STDAPI_(HICON)   OleUIMetafilePictExtractIcon(HGLOBAL);
STDAPI_(BOOL)    OleUIMetafilePictExtractIconSource(HGLOBAL,LPOLESTR,UINT FAR *);

/*************************************************************************
** INSERT OBJECT DIALOG
*************************************************************************/

//Insert Object Dialog identifiers
#define ID_IO_CREATENEW                 2100
#define ID_IO_CREATEFROMFILE            2101
#define ID_IO_LINKFILE                  2102
#define ID_IO_OBJECTTYPELIST            2103
#define ID_IO_DISPLAYASICON             2104
#define ID_IO_CHANGEICON                2105
#define ID_IO_FILE                      2106
#define ID_IO_FILEDISPLAY               2107
#define ID_IO_RESULTIMAGE               2108
#define ID_IO_RESULTTEXT                2109
#define ID_IO_ICONDISPLAY               2110
#define ID_IO_OBJECTTYPETEXT            2111
#define ID_IO_FILETEXT                  2112
#define ID_IO_FILETYPE                  2113

// Strings in OLE2UI resources
#define IDS_IORESULTNEW                 32400
#define IDS_IORESULTNEWICON             32401
#define IDS_IORESULTFROMFILE1           32402
#define IDS_IORESULTFROMFILE2           32403
#define IDS_IORESULTFROMFILEICON2       32404
#define IDS_IORESULTLINKFILE1           32405
#define IDS_IORESULTLINKFILE2           32406
#define IDS_IORESULTLINKFILEICON1       32407
#define IDS_IORESULTLINKFILEICON2       32408

/*************************************************************************
** PASTE SPECIAL DIALOG
*************************************************************************/

//Paste Special Dialog identifiers
#define ID_PS_PASTE                    500
#define ID_PS_PASTELINK                501
#define ID_PS_SOURCETEXT               502
#define ID_PS_PASTELIST                503
#define ID_PS_PASTELINKLIST            504
#define ID_PS_DISPLAYLIST              505
#define ID_PS_DISPLAYASICON            506
#define ID_PS_ICONDISPLAY              507
#define ID_PS_CHANGEICON               508
#define ID_PS_RESULTIMAGE              509
#define ID_PS_RESULTTEXT               510
#define ID_PS_RESULTGROUP              511
#define ID_PS_STXSOURCE                512
#define ID_PS_STXAS                    513

// Paste Special String IDs
#define IDS_PSPASTEDATA                32410
#define IDS_PSPASTEOBJECT              32411
#define IDS_PSPASTEOBJECTASICON        32412
#define IDS_PSPASTELINKDATA            32413
#define IDS_PSPASTELINKOBJECT          32414
#define IDS_PSPASTELINKOBJECTASICON    32415
#define IDS_PSNONOLE                   32416
#define IDS_PSUNKNOWNTYPE              32417
#define IDS_PSUNKNOWNSRC               32418
#define IDS_PSUNKNOWNAPP               32419

/*************************************************************************
** EDIT LINKS DIALOG
*************************************************************************/

// Edit Links Dialog identifiers
#define ID_EL_CHANGESOURCE             201
#define ID_EL_AUTOMATIC                202
#define ID_EL_CLOSE                    208
#define ID_EL_CANCELLINK               209
#define ID_EL_UPDATENOW                210
#define ID_EL_OPENSOURCE               211
#define ID_EL_MANUAL                   212
#define ID_EL_LINKSOURCE               216
#define ID_EL_LINKTYPE                 217
#define ID_EL_UPDATE                   218
#define ID_EL_NULL                     -1
#define ID_EL_LINKSLISTBOX             206
#define ID_EL_COL1                     220
#define ID_EL_COL2                     221
#define ID_EL_COL3                     222

/*************************************************************************
** CHANGE ICON DIALOG
*************************************************************************/

//Change Icon Dialog identifiers
#define ID_GROUP                    120
#define ID_CURRENT                  121
#define ID_CURRENTICON              122
#define ID_DEFAULT                  123
#define ID_DEFAULTICON              124
#define ID_FROMFILE                 125
#define ID_FROMFILEEDIT             126
#define ID_ICONLIST                 127
#define ID_LABEL                    128
#define ID_LABELEDIT                129
#define ID_BROWSE                   130
#define ID_RESULTICON               132
#define ID_RESULTLABEL              133

// Stringtable defines for Change Icon
#define IDS_CINOICONSINFILE         32430
#define IDS_CIINVALIDFILE           32431
#define IDS_CIFILEACCESS            32432
#define IDS_CIFILESHARE             32433
#define IDS_CIFILEOPENFAIL          32434

/*************************************************************************
** CONVERT DIALOG
*************************************************************************/

//Convert Dialog identifiers
#define IDCV_OBJECTTYPE             150
#define IDCV_DISPLAYASICON          152
#define IDCV_CHANGEICON             153
#define IDCV_ACTIVATELIST           154
#define IDCV_CONVERTTO              155
#define IDCV_ACTIVATEAS             156
#define IDCV_RESULTTEXT             157
#define IDCV_CONVERTLIST            158
#define IDCV_ICON                   159
#define IDCV_ICONLABEL1             160
#define IDCV_ICONLABEL2             161
#define IDCV_STXCURTYPE             162
#define IDCV_GRPRESULT              163
#define IDCV_STXCONVERTTO           164

// String IDs for Convert dialog
#define IDS_CVRESULTCONVERTLINK     32440
#define IDS_CVRESULTCONVERTTO       32441
#define IDS_CVRESULTNOCHANGE        32442
#define IDS_CVRESULTDISPLAYASICON   32443
#define IDS_CVRESULTACTIVATEAS      32444
#define IDS_CVRESULTACTIVATEDIFF    32445

/*************************************************************************
** BUSY DIALOG
*************************************************************************/

// Busy dialog identifiers
#define IDBZ_RETRY                      600
#define IDBZ_ICON                       601
#define IDBZ_MESSAGE1                   602
#define IDBZ_SWITCHTO                   604

// Busy dialog stringtable defines
#define IDS_BZRESULTTEXTBUSY            32447
#define IDS_BZRESULTTEXTNOTRESPONDING   32448

// Links dialog stringtable defines
#define IDS_LINK_AUTO           32450
#define IDS_LINK_MANUAL         32451
#define IDS_LINK_UNKNOWN        32452
#define IDS_LINKS               32453
#define IDS_FAILED              32454
#define IDS_CHANGESOURCE        32455
#define IDS_INVALIDSOURCE       32456
#define IDS_ERR_GETLINKSOURCE   32457
#define IDS_ERR_GETLINKUPDATEOPTIONS    32458
#define IDS_ERR_ADDSTRING       32459
#define IDS_CHANGEADDITIONALLINKS   32460
#define IDS_CLOSE               32461

/*************************************************************************
** PROMPT USER DIALOGS
*************************************************************************/
#define ID_PU_LINKS             900
#define ID_PU_TEXT              901
#define ID_PU_CONVERT           902
#define ID_PU_BROWSE            904
#define ID_PU_METER             905
#define ID_PU_PERCENT           906
#define ID_PU_STOP              907

// used for -1 ids in dialogs:
#define ID_DUMMY    999

/* inside ole2ui.c */
#ifdef __cplusplus
extern "C"
#endif

/*************************************************************************
** OLE OBJECT FEEDBACK EFFECTS
*************************************************************************/

#define OLEUI_HANDLES_USEINVERSE    0x00000001L
#define OLEUI_HANDLES_NOBORDER      0x00000002L
#define OLEUI_HANDLES_INSIDE        0x00000004L
#define OLEUI_HANDLES_OUTSIDE       0x00000008L

#define OLEUI_SHADE_FULLRECT        1
#define OLEUI_SHADE_BORDERIN        2
#define OLEUI_SHADE_BORDEROUT       3

/* objfdbk.c function prototypes */
STDAPI_(void) OleUIDrawHandles(LPRECT lpRect, HDC hdc, DWORD dwFlags, UINT cSize, BOOL fDraw);
STDAPI_(void) OleUIDrawShading(LPRECT lpRect, HDC hdc, DWORD dwFlags, UINT cWidth);
STDAPI_(void) OleUIShowObject(LPCRECT lprc, HDC hdc, BOOL fIsLink);

/*************************************************************************
** Hatch window definitions and prototypes                              **
*************************************************************************/
#define DEFAULT_HATCHBORDER_WIDTH   4

STDAPI_(BOOL) RegisterHatchWindowClass(HINSTANCE hInst);
STDAPI_(HWND) CreateHatchWindow(HWND hWndParent, HINSTANCE hInst);
STDAPI_(UINT) GetHatchWidth(HWND hWndHatch);
STDAPI_(void) GetHatchRect(HWND hWndHatch, LPRECT lpHatchRect);
STDAPI_(void) SetHatchRect(HWND hWndHatch, LPRECT lprcHatchRect);
STDAPI_(void) SetHatchWindowSize(
      HWND        hWndHatch,
      LPRECT      lprcIPObjRect,
      LPRECT      lprcClipRect,
      LPPOINT     lpptOffset
);

#define OLEUI_VERSION_MAGIC 0x4D42

#endif // _OLESTD_H_


OLEUTL.C

/*
 * OLEUTL.C
 *
 * Miscellaneous utility functions for OLE 2.0 Applications:
 *
 *  Function                      Purpose
 *  -------------------------------------------------------------------
 *  SetDCToDrawInHimetricRect     Sets up an HIMETRIC mapping mode in a DC.
 *  ResetOrigDC                   Performs the opposite of
 *                                SetDCToDrawInHimetricRect
 *  XformWidthInPixelsToHimetric  Converts an int width into HiMetric units
 *  XformWidthInHimetricToPixels  Converts an int width from HiMetric units
 *  XformHeightInPixelsToHimetric Converts an int height into HiMetric units
 *  XformHeightInHimetricToPixels Converts an int height from HiMetric units
 *  XformRectInPixelsToHimetric   Converts a rect into HiMetric units
 *  XformRectInHimetricToPixels   Converts a rect from HiMetric units
 *  XformSizeInPixelsToHimetric   Converts a SIZEL into HiMetric units
 *  XformSizeInHimetricToPixels   Converts a SIZEL from HiMetric units
 *  AreRectsEqual                 Compares to Rect's
 *
 *  ParseCmdLine                  Determines if -Embedding exists
 *  OpenOrCreateRootStorage       Creates a root docfile for OLE storage
 *  CommitStorage                 Commits all changes in a docfile
 *  CreateChildStorage            Creates child storage in another storage
 *  OpenChildStorage              Opens child storage in another storage
 *
 *
 * Copyright (c)1992-1996 Microsoft Corporation, All Right Reserved
 */

#define STRICT  1
#include "olestd.h"
#include <stdlib.h>
#include <ctype.h>

//Internal function to this module
static LPSTR GetWord(LPSTR lpszSrc, LPSTR lpszDst);

/*
 * SetDCToAnisotropic
 *
 * Purpose:
 *  Setup the correspondence between the rect in device unit (Viewport) and
 *  the rect in logical unit (Window) so that the proper scaling of
 *  coordinate systems will be calculated. set up both the Viewport and
 *  the window as follows:
 *
 *      1) ------------------ ( 2
 *      |                     |
 *      |                     |
 *      |                     |
 *      |                     |
 *      |                     |
 *      3) ------------------ ( 4
 *
 *      Origin   = P3
 *      X extent = P2x - P3x
 *      Y extent = P2y - P3y
 *
 * Parameters:
 *  hDC             HDC to affect
 *  lprcPhysical    LPRECT containing the physical (device) extents of DC
 *  lprcLogical     LPRECT containing the logical extents
 *  lprcWindowOld   LPRECT in which to preserve the window for ResetOrigDC
 *  lprcViewportOld LPRECT in which to preserver the viewport for ResetOrigDC
 *
 * Return Value:
 *  int             The original mapping mode of the DC.
 */

STDAPI_(int) SetDCToAnisotropic(
                HDC hDC,
                LPRECT lprcPhysical, LPRECT lprcLogical,
                LPRECT lprcWindowOld, LPRECT lprcViewportOld)
{
        int     nMapModeOld=SetMapMode(hDC, MM_ANISOTROPIC);

        SetWindowOrgEx(hDC, lprcLogical->left, lprcLogical->bottom, (LPPOINT)&lprcWindowOld->left);
        SetWindowExtEx(hDC, (lprcLogical->right-lprcLogical->left), (lprcLogical->top-lprcLogical->bottom), (LPSIZE)&lprcWindowOld->right);
        SetViewportOrgEx(hDC, lprcPhysical->left, lprcPhysical->bottom, (LPPOINT)&lprcViewportOld->left);
        SetViewportExtEx(hDC, (lprcPhysical->right-lprcPhysical->left), (lprcPhysical->top-lprcPhysical->bottom), (LPSIZE)&lprcViewportOld->right);

        return nMapModeOld;
}

/*
 * SetDCToDrawInHimetricRect
 *
 * Purpose:
 *  Setup the correspondence between the rect in pixels (Viewport) and
 *  the rect in HIMETRIC (Window) so that the proper scaling of
 *  coordinate systems will be calculated. set up both the Viewport and
 *  the window as follows:
 *
 *      1) ------------------ ( 2
 *      |                     |
 *      |                     |
 *      |                     |
 *      |                     |
 *      |                     |
 *      3) ------------------ ( 4
 *
 *      Origin   = P3
 *      X extent = P2x - P3x
 *      Y extent = P2y - P3y
 *
 * Parameters:
 *  hDC             HDC to affect
 *  lprcPix         LPRECT containing the pixel extents of DC
 *  lprcHiMetric    LPRECT to receive the himetric extents
 *  lprcWindowOld   LPRECT in which to preserve the window for ResetOrigDC
 *  lprcViewportOld LPRECT in which to preserver the viewport for ResetOrigDC
 *
 * Return Value:
 *  int             The original mapping mode of the DC.
 */
STDAPI_(int) SetDCToDrawInHimetricRect(
        HDC hDC,
        LPRECT lprcPix, LPRECT lprcHiMetric,
        LPRECT lprcWindowOld, LPRECT lprcViewportOld)
        {
        int     nMapModeOld=SetMapMode(hDC, MM_ANISOTROPIC);
        BOOL    fSystemDC  =FALSE;

        if (NULL==hDC)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        XformRectInPixelsToHimetric(hDC, lprcPix, lprcHiMetric);

        SetWindowOrgEx(hDC, lprcHiMetric->left, lprcHiMetric->bottom, (LPPOINT)&lprcWindowOld->left);
        SetWindowExtEx(hDC, (lprcHiMetric->right-lprcHiMetric->left), (lprcHiMetric->top-lprcHiMetric->bottom), (LPSIZE)&lprcWindowOld->right);
        SetViewportOrgEx(hDC, lprcPix->left, lprcPix->bottom, (LPPOINT)&lprcViewportOld->left);
        SetViewportExtEx(hDC, (lprcPix->right-lprcPix->left), (lprcPix->top-lprcPix->bottom), (LPSIZE)&lprcViewportOld->right);

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return nMapModeOld;
        }

/*
 * ResetOrigDC
 *
 * Purpose:
 *  Restores a DC set to draw in himetric from SetDCToDrawInHimetricRect.
 *
 * Parameters:
 *  hDC             HDC to restore
 *  nMapModeOld     int original mapping mode of hDC
 *  lprcWindowOld   LPRECT filled in SetDCToDrawInHimetricRect
 *  lprcViewportOld LPRECT filled in SetDCToDrawInHimetricRect
 *
 * Return Value:
 *  int             Same as nMapModeOld.
 */

STDAPI_(int) ResetOrigDC(
        HDC hDC, int nMapModeOld,
        LPRECT lprcWindowOld, LPRECT lprcViewportOld)
        {
        POINT     pOld;

        SetMapMode(hDC, nMapModeOld);

        SetWindowOrgEx(hDC,   lprcWindowOld->left,    lprcWindowOld->top,      (LPPOINT)&pOld);
        SetWindowExtEx(hDC,   lprcWindowOld->right,   lprcWindowOld->bottom,   (LPSIZE)&pOld);
        SetViewportOrgEx(hDC, lprcViewportOld->left,  lprcViewportOld->top,    (LPPOINT)&pOld);
        SetViewportExtEx(hDC, lprcViewportOld->right, lprcViewportOld->bottom, (LPSIZE)&pOld);

        return nMapModeOld;
        }

/*
 * XformWidthInPixelsToHimetric
 * XformWidthInHimetricToPixels
 * XformHeightInPixelsToHimetric
 * XformHeightInHimetricToPixels
 *
 * Functions to convert an int between a device coordinate system and
 * logical HiMetric units.
 *
 * Parameters:
 *  hDC             HDC providing reference to the pixel mapping.  If
 *                  NULL, a screen DC is used.
 *
 *  Size Functions:
 *  lpSizeSrc       LPSIZEL providing the structure to convert.  This
 *                  contains pixels in XformSizeInPixelsToHimetric and
 *                  logical HiMetric units in the complement function.
 *  lpSizeDst       LPSIZEL providing the structure to receive converted
 *                  units.  This contains pixels in
 *                  XformSizeInPixelsToHimetric and logical HiMetric
 *                  units in the complement function.
 *
 *  Width Functions:
 *  iWidth          int containing the value to convert.
 *
 * Return Value:
 *  Size Functions:     None
 *  Width Functions:    Converted value of the input parameters.
 *
 * NOTE:
 *  When displaying on the screen, Window apps display everything enlarged
 *  from its actual size so that it is easier to read. For example, if an
 *  app wants to display a 1in. horizontal line, that when printed is
 *  actually a 1in. line on the printed page, then it will display the line
 *  on the screen physically larger than 1in. This is described as a line
 *  that is "logically" 1in. along the display width. Windows maintains as
 *  part of the device-specific information about a given display device:
 *      LOGPIXELSX -- no. of pixels per logical in along the display width
 *      LOGPIXELSY -- no. of pixels per logical in along the display height
 *
 *  The following formula converts a distance in pixels into its equivalent
 *  logical HIMETRIC units:
 *
 *      DistInHiMetric = (HIMETRIC_PER_INCH * DistInPix)
 *                       -------------------------------
 *                           PIXELS_PER_LOGICAL_IN
 *
 */
STDAPI_(int) XformWidthInPixelsToHimetric(HDC hDC, int iWidthInPix)
        {
        int     iXppli;     //Pixels per logical inch along width
        int     iWidthInHiMetric;
        BOOL    fSystemDC=FALSE;

        if (NULL==hDC)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        iXppli = GetDeviceCaps (hDC, LOGPIXELSX);

        //We got pixel units, convert them to logical HIMETRIC along the display
        iWidthInHiMetric = MAP_PIX_TO_LOGHIM(iWidthInPix, iXppli);

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return iWidthInHiMetric;
        }

STDAPI_(int) XformWidthInHimetricToPixels(HDC hDC, int iWidthInHiMetric)
        {
        int     iXppli;     //Pixels per logical inch along width
        int     iWidthInPix;
        BOOL    fSystemDC=FALSE;

        if (NULL==hDC)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        iXppli = GetDeviceCaps (hDC, LOGPIXELSX);

        //We got logical HIMETRIC along the display, convert them to pixel units
        iWidthInPix = MAP_LOGHIM_TO_PIX(iWidthInHiMetric, iXppli);

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return iWidthInPix;
        }

STDAPI_(int) XformHeightInPixelsToHimetric(HDC hDC, int iHeightInPix)
        {
        int     iYppli;     //Pixels per logical inch along height
        int     iHeightInHiMetric;
        BOOL    fSystemDC=FALSE;

        if (NULL==hDC)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        iYppli = GetDeviceCaps (hDC, LOGPIXELSY);

        //* We got pixel units, convert them to logical HIMETRIC along the display
        iHeightInHiMetric = MAP_PIX_TO_LOGHIM(iHeightInPix, iYppli);

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return iHeightInHiMetric;
        }

STDAPI_(int) XformHeightInHimetricToPixels(HDC hDC, int iHeightInHiMetric)
        {
        int     iYppli;     //Pixels per logical inch along height
        int     iHeightInPix;
        BOOL    fSystemDC=FALSE;

        if (NULL==hDC)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        iYppli = GetDeviceCaps (hDC, LOGPIXELSY);

        //* We got logical HIMETRIC along the display, convert them to pixel units
        iHeightInPix = MAP_LOGHIM_TO_PIX(iHeightInHiMetric, iYppli);

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return iHeightInPix;
        }

/*
 * XformRectInPixelsToHimetric
 * XformRectInHimetricToPixels
 *
 * Purpose:
 *  Convert a rectangle between pixels of a given hDC and HIMETRIC units
 *  as manipulated in OLE.  If the hDC is NULL, then a screen DC is used
 *  and assumes the MM_TEXT mapping mode.
 *
 * Parameters:
 *  hDC             HDC providing reference to the pixel mapping.  If
 *                  NULL, a screen DC is used.
 *  lprcSrc         LPRECT providing the rectangle to convert.  This
 *                  contains pixels in XformRectInPixelsToHimetric and
 *                  logical HiMetric units in the complement function.
 *  lprcDst         LPRECT providing the rectangle to receive converted units.
 *                  This contains pixels in XformRectInPixelsToHimetric and
 *                  logical HiMetric units in the complement function.
 *
 * Return Value:
 *  None
 *
 * NOTE:
 *  When displaying on the screen, Window apps display everything enlarged
 *  from its actual size so that it is easier to read. For example, if an
 *  app wants to display a 1in. horizontal line, that when printed is
 *  actually a 1in. line on the printed page, then it will display the line
 *  on the screen physically larger than 1in. This is described as a line
 *  that is "logically" 1in. along the display width. Windows maintains as
 *  part of the device-specific information about a given display device:
 *      LOGPIXELSX -- no. of pixels per logical in along the display width
 *      LOGPIXELSY -- no. of pixels per logical in along the display height
 *
 *  The following formula converts a distance in pixels into its equivalent
 *  logical HIMETRIC units:
 *
 *      DistInHiMetric = (HIMETRIC_PER_INCH * DistInPix)
 *                      -------------------------------
 *                            PIXELS_PER_LOGICAL_IN
 *
 * Rect in Pixels (MM_TEXT):
 *
 *              0---------- X
 *              |
 *              |       1) ------------------ ( 2   P1 = (rc.left, rc.top)
 *              |       |                     |     P2 = (rc.right, rc.top)
 *              |       |                     |     P3 = (rc.left, rc.bottom)
 *              |       |                     |     P4 = (rc.right, rc.bottom)
 *                      |                     |
 *              Y       |                     |
 *                      3) ------------------ ( 4
 *
 *              NOTE:   Origin   = (P1x, P1y)
 *                      X extent = P4x - P1x
 *                      Y extent = P4y - P1y
 *
 *
 * Rect in Himetric (MM_HIMETRIC):
 *
 *
 *                      1) ------------------ ( 2   P1 = (rc.left, rc.top)
 *              Y       |                     |     P2 = (rc.right, rc.top)
 *                      |                     |     P3 = (rc.left, rc.bottom)
 *              |       |                     |     P4 = (rc.right, rc.bottom)
 *              |       |                     |
 *              |       |                     |
 *              |       3) ------------------ ( 4
 *              |
 *              0---------- X
 *
 *              NOTE:   Origin   = (P3x, P3y)
 *                      X extent = P2x - P3x
 *                      Y extent = P2y - P3y
 *
 *
 */

STDAPI_(void) XformRectInPixelsToHimetric(
        HDC hDC, LPRECT lprcPix, LPRECT lprcHiMetric)
        {
        int     iXppli;     //Pixels per logical inch along width
        int     iYppli;     //Pixels per logical inch along height
        int     iXextInPix=(lprcPix->right-lprcPix->left);
        int     iYextInPix=(lprcPix->bottom-lprcPix->top);
        BOOL    fSystemDC=FALSE;

        if (NULL==hDC ||
            GetDeviceCaps(hDC, TECHNOLOGY) == DT_METAFILE ||
            GetDeviceCaps(hDC, LOGPIXELSX) == 0)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        iXppli = GetDeviceCaps (hDC, LOGPIXELSX);
        iYppli = GetDeviceCaps (hDC, LOGPIXELSY);

        //We got pixel units, convert them to logical HIMETRIC along the display
        lprcHiMetric->right = MAP_PIX_TO_LOGHIM(iXextInPix, iXppli);
        lprcHiMetric->top   = MAP_PIX_TO_LOGHIM(iYextInPix, iYppli);

        lprcHiMetric->left    = 0;
        lprcHiMetric->bottom  = 0;

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return;
        }

STDAPI_(void) XformRectInHimetricToPixels(
        HDC hDC, LPRECT lprcHiMetric, LPRECT lprcPix)
        {
        int     iXppli;     //Pixels per logical inch along width
        int     iYppli;     //Pixels per logical inch along height
        int     iXextInHiMetric=(lprcHiMetric->right-lprcHiMetric->left);
        int     iYextInHiMetric=(lprcHiMetric->bottom-lprcHiMetric->top);
        BOOL    fSystemDC=FALSE;

        if (NULL==hDC ||
            GetDeviceCaps(hDC, TECHNOLOGY) == DT_METAFILE ||
            GetDeviceCaps(hDC, LOGPIXELSX) == 0)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        iXppli = GetDeviceCaps (hDC, LOGPIXELSX);
        iYppli = GetDeviceCaps (hDC, LOGPIXELSY);

        //We got pixel units, convert them to logical HIMETRIC along the display
        lprcPix->right = MAP_LOGHIM_TO_PIX(iXextInHiMetric, iXppli);
        lprcPix->top   = MAP_LOGHIM_TO_PIX(iYextInHiMetric, iYppli);

        lprcPix->left  = 0;
        lprcPix->bottom= 0;

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return;
        }

/*
 * XformSizeInPixelsToHimetric
 * XformSizeInHimetricToPixels
 *
 * Functions to convert a SIZEL structure (Size functions) or
 * an int (Width functions) between a device coordinate system and
 * logical HiMetric units.
 *
 * Parameters:
 *  hDC             HDC providing reference to the pixel mapping.  If
 *                  NULL, a screen DC is used.
 *
 *  Size Functions:
 *  lpSizeSrc       LPSIZEL providing the structure to convert.  This
 *                  contains pixels in XformSizeInPixelsToHimetric and
 *                  logical HiMetric units in the complement function.
 *  lpSizeDst       LPSIZEL providing the structure to receive converted
 *                  units.  This contains pixels in
 *                  XformSizeInPixelsToHimetric and logical HiMetric
 *                  units in the complement function.
 *
 *  Width Functions:
 *  iWidth          int containing the value to convert.
 *
 * Return Value:
 *  Size Functions:     None
 *  Width Functions:    Converted value of the input parameters.
 *
 * NOTE:
 *  When displaying on the screen, Window apps display everything enlarged
 *  from its actual size so that it is easier to read. For example, if an
 *  app wants to display a 1in. horizontal line, that when printed is
 *  actually a 1in. line on the printed page, then it will display the line
 *  on the screen physically larger than 1in. This is described as a line
 *  that is "logically" 1in. along the display width. Windows maintains as
 *  part of the device-specific information about a given display device:
 *      LOGPIXELSX -- no. of pixels per logical in along the display width
 *      LOGPIXELSY -- no. of pixels per logical in along the display height
 *
 *  The following formula converts a distance in pixels into its equivalent
 *  logical HIMETRIC units:
 *
 *      DistInHiMetric = (HIMETRIC_PER_INCH * DistInPix)
 *                       -------------------------------
 *                           PIXELS_PER_LOGICAL_IN
 *
 */

STDAPI_(void) XformSizeInPixelsToHimetric(
        HDC hDC, LPSIZEL lpSizeInPix, LPSIZEL lpSizeInHiMetric)
        {
        int     iXppli;     //Pixels per logical inch along width
        int     iYppli;     //Pixels per logical inch along height
        BOOL    fSystemDC=FALSE;

        if (NULL==hDC ||
            GetDeviceCaps(hDC, TECHNOLOGY) == DT_METAFILE ||
            GetDeviceCaps(hDC, LOGPIXELSX) == 0)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        iXppli = GetDeviceCaps (hDC, LOGPIXELSX);
        iYppli = GetDeviceCaps (hDC, LOGPIXELSY);

        //We got pixel units, convert them to logical HIMETRIC along the display
        lpSizeInHiMetric->cx = (long)MAP_PIX_TO_LOGHIM((int)lpSizeInPix->cx, iXppli);
        lpSizeInHiMetric->cy = (long)MAP_PIX_TO_LOGHIM((int)lpSizeInPix->cy, iYppli);

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return;
        }

STDAPI_(void) XformSizeInHimetricToPixels(
        HDC hDC, LPSIZEL lpSizeInHiMetric, LPSIZEL lpSizeInPix)
        {
        int     iXppli;     //Pixels per logical inch along width
        int     iYppli;     //Pixels per logical inch along height
        BOOL    fSystemDC=FALSE;

        if (NULL==hDC ||
            GetDeviceCaps(hDC, TECHNOLOGY) == DT_METAFILE ||
            GetDeviceCaps(hDC, LOGPIXELSX) == 0)
                {
                hDC=GetDC(NULL);
                fSystemDC=TRUE;
                }

        iXppli = GetDeviceCaps (hDC, LOGPIXELSX);
        iYppli = GetDeviceCaps (hDC, LOGPIXELSY);

        //We got logical HIMETRIC along the display, convert them to pixel units
        lpSizeInPix->cx = (long)MAP_LOGHIM_TO_PIX((int)lpSizeInHiMetric->cx, iXppli);
        lpSizeInPix->cy = (long)MAP_LOGHIM_TO_PIX((int)lpSizeInHiMetric->cy, iYppli);

        if (fSystemDC)
                ReleaseDC(NULL, hDC);

        return;
        }

#if defined( OBSOLETE )
// This function has been converted to a macro

/* AreRectsEqual
** -------------
*/
STDAPI_(BOOL) AreRectsEqual(LPRECT lprc1, LPRECT lprc2)
{
        if ((lprc1->top == lprc2->top) &&
                (lprc1->left == lprc2->left) &&
                (lprc1->right == lprc2->right) &&
                (lprc1->bottom == lprc2->bottom))
                return TRUE;

        return FALSE;
}
#endif  // OBSOLETE

/*
 * ParseCmdLine
 *
 * Parses the Windows command line which was passed to WinMain.
 * This function determines if the -Embedding switch has been given.
 *
 */

STDAPI_(void) ParseCmdLine(
        LPSTR lpszLine,
        BOOL FAR* lpfEmbedFlag,
        LPSTR szFileName)
{
        int i=0;
        char szBuf[256];

        if(lpfEmbedFlag)
                *lpfEmbedFlag = FALSE;
        szFileName[0]='\0';             // NULL string

        // skip blanks
        while(isspace(*lpszLine)) lpszLine++;

        if(!*lpszLine)   // No filename or options, so start a fresh document.
                return;

        // Check for "-Embedding" or "/Embedding" and set fEmbedding.
        if(lpfEmbedFlag && (*lpszLine == '-' || *lpszLine == '/')) {
                lpszLine++;
                lpszLine = GetWord(lpszLine, szBuf);
                *lpfEmbedFlag = !lstrcmp(szBuf, EMBEDDINGFLAG);
        }

        // skip blanks
        while(isspace(*lpszLine)) lpszLine++;

        // set szFileName to argument
        while(lpszLine[i]) {
                szFileName[i]=lpszLine[i];
                i++;
        }
        szFileName[i]='\0';
}

/* GetWord
 * -------
 *
 * LPSTR lpszSrc - Pointer to a source string
 * LPSTR lpszDst - Pointer to destination buffer
 *
 * Will copy one space-terminated or null-terminated word from the source
 * string to the destination buffer.
 * returns: pointer to next character following the word.
 */
static LPSTR GetWord(LPSTR lpszSrc, LPSTR lpszDst)
{
        while (*lpszSrc && !isspace(*lpszSrc))
                *lpszDst++ = *lpszSrc++;

        *lpszDst = '\0';
        return lpszSrc;
}


PREINC.H

/*
 *PREINC.H
 *
 *DGreen
 */

#ifdef DEBUG
// define MEMCHECK to enable memory checking code
#define MEMCHECK
#endif

#ifdef MEMCHECK
// define INSTRUMENT_MEMMAN here to enable memory manager statistics
//#define INSTRUMENT_MEMMAN
#endif

#define BEGIN_CODESPACE_DATA data_seg(".text")
#define END_CODESPACE_DATA data_seg()

#ifdef DEBUG
#define MINTEST
#endif

#define LOCAL

#ifdef DLL
#define _DLL
#endif

#ifdef UNICODE
#define _UNICODE
#endif

#define DOSWIN32

#define STRICT

#define _INC_OLE

#define NOSHELLDEBUG //disables asserts in shell.h

#define MAPI_DIM

#ifdef DBCS
#define DBCS_EDIT RICHEDIT_CLASS
#else
#define DBCS_EDIT "Edit"
#endif


ANSIAPI.H

/*
 * ansiapi.h
 * This file contains prototypes of ANSI version of OLE apis
 * and mapping fooA to foo
 */
#ifndef _ANSIAPI_INCL
#define _ANSIAPI_INCL

#define UNICODEOLE32

#ifdef WIN32S
#if !defined(UNICODEOLE32)
#pragma message("Compiling for 32bit ANSI Ole!\n");
#endif
#endif

#define WASANSI() {                                                           \
                   OutputDebugString("\n\r\t\tUnicode string was ANSI!\r\n"); \
                   Assert(0);                                                 \
                   _asm { int 3 }                                             \
                  }

#ifndef UNICODEOLE32

#define W2A(w, a, cb)     lstrcpy (a, w)
#define A2W(a, w, cb)     lstrcpy (w, a)

#define FREELOCALSTRING(p)
#define MAKE_STR_LOCAL_COPYW2A(s, l)  l = s
#define MAKE_STR_LOCAL_COPYA2W(s, l)  l = s

#define OLESTRCPY                lstrcpy
#define OLESTRCAT                lstrcat
#define OLESTRLEN                lstrlen
#define OLESTRCMP                lstrcmp

#define DeleteFile_AW            DeleteFile
#define _lopen_AW                _lopen
#define _lcreat_AW               _lcreat
#define GlobalAddAtom_AW         GlobalAddAtom
#define GlobalGetAtomName_AW     GlobalGetAtomName
#define RegOpenKey_AW            RegOpenKey

#define CoLoadLibraryA   CoLoadLibrary

#define StringFromCLSID2A    StringFromCLSID2
#define StringFromIID2A      StringFromIID2
#define StringFromGUID2A     StringFromGUID2
#define CLSIDFromProgIDA     CLSIDFromProgID
#define CLSIDFromStringA     CLSIDFromString
#define ProgIDFromCLSIDA     ProgIDFromCLSID
#define StringFromCLSIDA     StringFromCLSID

#define UtDupStringA2W       UtDupString

// Storage APIs
#define StgOpenStorageA      StgOpenStorage

// IPersistFile ansi translation

#define IPersistFile_LoadA(pf, file, mode) (pf)->Load(file, mode)

// IMoniker ansi translation

#define IMoniker_GetDisplayNameA(pm, p1, p2, p3) (pm)->GetDisplayName(p1,p2,p3)

#define CreateFileMonikerA   CreateFileMoniker

#else  // UNICODEOLE32

#define W2A(w, a, cb)     WideCharToMultiByte(                              \
                                               CP_ACP,                      \
                                               0,                           \
                                               w,                           \
                                               -1,                          \
                                               a,                           \
                                               cb,                          \
                                               NULL,                        \
                                               NULL)

#define A2W(a, w, cb)     MultiByteToWideChar(                              \
                                               CP_ACP,                      \
                                               0,                           \
                                               a,                           \
                                               -1,                          \
                                               w,                           \
                                               cb)

#define FREELOCALSTRING(p)      delete (p)
#define MAKE_STR_LOCAL_COPYW2A(s, l)  {                                   \
                                      l = UtDupStringW2A(s);              \
                                      if (!l) {                           \
                                         return ResultFromScode(S_OOM);   \
                                      }                                   \
                                   }

#define MAKE_STR_LOCAL_COPYA2W(s, l)  {                                   \
                                      l = UtDupStringA2W(s);              \
                                      if (!l) {                           \
                                         return ResultFromScode(S_OOM);   \
                                      }                                   \
                                   }

#define OLESTRCPY                wcscpy
#define OLESTRCAT                wcscat
#define OLESTRLEN                wcslen
#define OLESTRCMP                wcscmp

#define DeleteFile_AW            DeleteFileW
#define _lopen_AW                _lopenW
#define _lcreat_AW               _lcreatW
#define GlobalAddAtom_AW         GlobalAddAtomW
#define GlobalGetAtomName_AW     GlobalGetAtomNameW
#define RegOpenKey_AW            RegOpenKeyW

//STDAPI_(HINSTANCE) CoLoadLibraryA(LPSTR lpszLibName, BOOL bAutoFree);

#define StringFromCLSID2A(rclsid, lpsz, cbMax) \
    StringFromGUID2A(rclsid, lpsz, cbMax)

#define StringFromIID2A(riid, lpsz, cbMax) \
    StringFromGUID2A(riid, lpsz, cbMax)

//STDAPI_(int)  StringFromGUID2A(REFGUID rguid, LPSTR lpsz, int cbMax);
//STDAPI        CLSIDFromProgIDA(LPCSTR szProgID, LPCLSID pclsid);
//STDAPI        CLSIDFromStringA(LPSTR lpsz, LPCLSID lpclsid);
//STDAPI        StringFromCLSIDA(REFCLSID rclsid, LPSTR FAR* lplpsz);
//STDAPI        ProgIDFromCLSIDA (REFCLSID clsid, LPSTR FAR* lplpszProgID);

//LPWSTR UtDupStringA2W(LPCSTR pSrc);
//LPSTR  UtDupStringW2A(LPCWSTR pSrc);

// Storage APIs
//STDAPI StgOpenStorageA(LPCSTR pwcsName,IStorage FAR *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage FAR * FAR *ppstgOpen);

// IPersistFile ansi translation

//HRESULT IPersistFile_LoadA(LPPERSISTFILE pIPF, LPSTR szFile, DWORD dwMode);

// IMoniker ansi translation

//HRESULT IMoniker_GetDisplayNameA(LPMONIKER pm, LPBC p1, LPMONIKER p2, LPSTR FAR *p3);

//OLEAPI CreateFileMonikerA ( LPSTR lpszPathName, LPMONIKER FAR * ppmk );

#endif // !UNICODEOLE32

#endif //  _ANSIAPI_INCL


REGUID.H

#ifdef WIN32
# ifndef __based
#  define __based(a)
# endif
#endif

#define DECLARE_REGUID(name, b) \
EXTERN_C const GUID CDECL FAR name

// Comment out declaration of IID_IRichEditOleCallback
//  because it is already defined in richole.h
//
// DECLARE_REGUID(IID_IRichEditOleCallback,0x03);
//


REITP.C

/*
 *reitp.c
 *
 *Purpose:
 *Test program for RichEdit.  Excercises main RichEdit
 *functionality while providing a good replacement for notepad.
 *
 *Owner:
 *
 *  Copyright 1997 - 1998 Microsoft Coporation
 */

#include "preinc.h"

#include <windows.h>
#include <windowsx.h>

#include <commdlg.h>
#include <cderr.h>
#include <ctype.h>
#include <stdio.h>
#include <tchar.h>

// for the benefit of the outside world,
// richedit.h uses cpMax instead of cpMost
// I highly prefer cpMost
#ifdef cpMax
#error "cpMax hack won't work"
#endif// cpMax
#define cpMax cpMost
#include <richedit.h>
#undef cpMax

#include "olestd.h"
#include "reguid.h"
#include "dbugit.h"
#include <richole.h>

#include "reitp.h"
#include "reitp.rh"
#include "frmtbar.h"
#include <oledlg.h>

ASSERTDATA

#define SUNKENRICHEDIT

// define to use LiFormatRange() directly instead of via blitting
//#define NOBLIT

// used to make life simpler while testing, should not normally be defined
//#define NO_SAVE_PROMPT

struct _itpcall;

typedef struct _pp
{
BOOL fDone;
HWND hwndDlg;
REDOC *predoc;
LONG cchText;
LONG dxPage;
LONG dyPage;
RECT rc;
FORMATRANGE fr;
#ifndef NOBLIT
HBITMAP hbmp;
HBITMAP hbmpOld;
INT dxBmp;
INT dyBmp;
#endif// !NOBLIT
INT ipage;
INT cpage;
INT cpageMost;
LONG *rgcpPages;
} PRINTPREVIEW;

// number of pages to allocate at once in PRINTPREVIEW.rgcpPages
#define cpageChunk 4

#define szFmtPrintPreview TEXT("Print Preview - Page %d")

typedef struct _assensz
{
WORD wNotification;
LPCSTR szDescription;
} ASSENSZ;

// added to the client rect to get the RichEdit size
#ifdef SUNKENRICHEDIT
#define dxRESize -GetSystemMetrics(SM_CXBORDER)
#define dyRESize -GetSystemMetrics(SM_CYBORDER)
#else// SUNKENRICHEDIT
#define dxRESize 0
#define dyRESize 0
#endif// SUNKENRICHEDIT, else

// Control ID for the format bar
#defineFBR_FormatBar217

#define HinstFromHwnd(_hwnd) ((HINSTANCE) GetWindowLong(_hwnd, GWL_HINSTANCE))

#define uiMFDisabled MF_DISABLED | MF_GRAYED
#define uiMFEnabled MF_ENABLED

// Maximum text to be corrected under PenWin
#define cchMaxCorrectText4096

#define szClosedName szAppName

static TCHAR szAppName[] = TEXT("REITP");
static const TCHAR szClassRE[] = TEXT(RICHEDIT_CLASS);

static const TCHAR szFmtTitle[] = TEXT("REITP - %s");
static const TCHAR szUntitled[] = TEXT("[Untitled]");
static const TCHAR szRTFSig[] = TEXT("{\\rtf");
#define cchRTFSig 5
static TCHAR szRegKey[] = TEXT("software\\microsoft\\reitp");
static TCHAR szRegValue[] = TEXT("placement");
static const POINT ptMinTrackSize = {300, 90};
// Make sure that these strings match the order of SF_*
static TCHAR szFilterLoad[] = TEXT("Text and Rich Text\0*.TXT;*.RTF\0Text Files (*.TXT)\0*.TXT\0Rich Text Format (*.RTF)\0*.RTF\0All Files\0*.*\0\0");
static TCHAR szFilterSave[] = TEXT("Text Files (*.TXT)\0*.TXT\0Rich Text Format (*.RTF)\0*.RTF\0RTF w/o Objects (*.RTF)\0*.RTF\0Textized\0*.TXT\0\0");

static HMODULE hmod = 0;// Flag for FreeLibrary()
HWND hwndMain = 0;
static HMENU hmenuLoaded = 0;
HMENU hmenuFull = 0;
static PRINTDLG pdDef = {0};
static BOOL fPrint = fFalse;
static BOOL fWysiwygDefault = fFalse;
static BOOL fWrapDefault = fTrue;
static UINT msgFindReplace = 0;
static HWND hwndFR = 0;// find/replace dialog
HINSTANCE hinst = 0;

static LONG cchTextMost = 0;
static CLIPFORMAT cfTxtObj = 0;
static BOOL fErrSpace = fFalse;

static CHARFORMAT cfDefault =
{
sizeof(CHARFORMAT),
CFM_EFFECTS | CFM_PROTECTED | CFM_SIZE | CFM_OFFSET | CFM_COLOR | CFM_CHARSET | CFM_FACE,
CFE_AUTOCOLOR,// effects
200,// height, 200 twips == 10 points
0,// offset
0,// color (not used since CFE_AUTOCOLOR is specified)
ANSI_CHARSET,
FF_ROMAN,// pitch and family
"Arial"// face name
};

static ASSENSZ rgassenszErrors[] =
{
{EN_ERRSPACE, "Out of memory. Exit some other applications and try again."},
{EN_MAXTEXT, "The maximum text length has been reached."},
{0, NULL}
};

/*
 *ITPOLEINPLACEFRAME
 *
 *Purpose:
 *Frame window support for in place editing
 */
typedef struct _itpoleinplaceframe
{
IOleInPlaceFrameVtbl * lpVtbl;// Virtual table
ULONG cRef;// Reference count
REDOC * predoc;// Document
LPOLEINPLACEACTIVEOBJECT pipaobj;// Current active object
}
ITPOLEINPLACEFRAME;

#define PipframeFromPunk(_p) ((ITPOLEINPLACEFRAME *) (_p))

// Functions for ITP in place frame
ITPOLEINPLACEFRAME * ITPOLEINPLACEFRAME_New(REDOC * predoc);
STDMETHODIMP ITPOLEINPLACEFRAME_QueryInterface(LPUNKNOWN punk, REFIID riid,
   LPUNKNOWN * ppvObj);
STDMETHODIMP_(ULONG) ITPOLEINPLACEFRAME_AddRef(LPUNKNOWN punk);
STDMETHODIMP_(ULONG) ITPOLEINPLACEFRAME_Release(LPUNKNOWN punk);
STDMETHODIMP ITPOLEINPLACEFRAME_GetWindow(ITPOLEINPLACEFRAME * pipframe,
  HWND * phwnd);
STDMETHODIMP ITPOLEINPLACEFRAME_ContextSensitiveHelp(ITPOLEINPLACEFRAME *pipframe,
 BOOL fEnterMode);
STDMETHODIMP ITPOLEINPLACEFRAME_GetBorder(ITPOLEINPLACEFRAME * pipframe,
  LPRECT prcBorder);
STDMETHODIMP ITPOLEINPLACEFRAME_RequestBorderSpace(ITPOLEINPLACEFRAME * pipframe,
   LPCBORDERWIDTHS pbw);
STDMETHODIMP ITPOLEINPLACEFRAME_SetBorderSpace(ITPOLEINPLACEFRAME * pipframe,
   LPCBORDERWIDTHS pbw);
STDMETHODIMP ITPOLEINPLACEFRAME_SetActiveObject(ITPOLEINPLACEFRAME * pipframe,
    LPOLEINPLACEACTIVEOBJECT pipaobj,
    LPCSTR szObjName);
STDMETHODIMP ITPOLEINPLACEFRAME_InsertMenus(ITPOLEINPLACEFRAME * pipframe,
    HMENU hmenuShared,
    LPOLEMENUGROUPWIDTHS pmgw);
STDMETHODIMP ITPOLEINPLACEFRAME_SetMenu(ITPOLEINPLACEFRAME * pipframe,
    HMENU hmenuShared, HOLEMENU holemenu,
    HWND hwndActiveObject);
STDMETHODIMP ITPOLEINPLACEFRAME_RemoveMenus(ITPOLEINPLACEFRAME * pipframe,
    HMENU hmenuShared);
STDMETHODIMP ITPOLEINPLACEFRAME_SetStatusText(ITPOLEINPLACEFRAME * pipframe,
  LPCSTR szStatusText);
STDMETHODIMP ITPOLEINPLACEFRAME_EnableModeless(ITPOLEINPLACEFRAME * pipframe,
   BOOL fEnable);
STDMETHODIMP ITPOLEINPLACEFRAME_TranslateAccelerator(ITPOLEINPLACEFRAME *pipframe,
 LPMSG pmsg, WORD wID);

// Virtual table for ole in place frame interface
IOleInPlaceFrameVtbl ITPOLEINPLACEFRAME_Vtbl =
{
(LPVOID) ITPOLEINPLACEFRAME_QueryInterface,
(LPVOID) ITPOLEINPLACEFRAME_AddRef,
(LPVOID) ITPOLEINPLACEFRAME_Release,
(LPVOID) ITPOLEINPLACEFRAME_GetWindow,
(LPVOID) ITPOLEINPLACEFRAME_ContextSensitiveHelp,
(LPVOID) ITPOLEINPLACEFRAME_GetBorder,
(LPVOID) ITPOLEINPLACEFRAME_RequestBorderSpace,
(LPVOID) ITPOLEINPLACEFRAME_SetBorderSpace,
(LPVOID) ITPOLEINPLACEFRAME_SetActiveObject,
(LPVOID) ITPOLEINPLACEFRAME_InsertMenus,
(LPVOID) ITPOLEINPLACEFRAME_SetMenu,
(LPVOID) ITPOLEINPLACEFRAME_RemoveMenus,
(LPVOID) ITPOLEINPLACEFRAME_SetStatusText,
(LPVOID) ITPOLEINPLACEFRAME_EnableModeless,
(LPVOID) ITPOLEINPLACEFRAME_TranslateAccelerator
};

/*
 *ITPCALL
 *
 *Purpose:
 *Callbacks from the Rich Edit OLE support
 */

typedef struct _itpcall
{
IRichEditOleCallbackVtbl * lpVtbl;// Virtual table
ULONG cRef;    // Reference count
REDOC * predoc;// Document
ITPOLEINPLACEFRAME * pipframe;// In place frame object
}
ITPCALL;

#define PitpcallFromPunk(_p) ((ITPCALL *) (_p))

// Functions for ITP callbacks
ITPCALL * ITPCALL_New(REDOC * predoc);
STDMETHODIMP ITPCALL_QueryInterface(LPUNKNOWN punk, REFIID riid,
    LPUNKNOWN * ppvObj);
STDMETHODIMP_(ULONG) ITPCALL_AddRef(LPUNKNOWN punk);
STDMETHODIMP_(ULONG) ITPCALL_Release(LPUNKNOWN punk);
STDMETHODIMP ITPCALL_GetNewStorage(ITPCALL * pitpcall, LPSTORAGE FAR * ppstg);
STDMETHODIMP ITPCALL_GetInPlaceContext(ITPCALL * pitpcall,
   LPOLEINPLACEFRAME FAR * ppipframe,
   LPOLEINPLACEUIWINDOW FAR* ppipuiDoc,
   LPOLEINPLACEFRAMEINFO pipfinfo);
STDMETHODIMP ITPCALL_ShowContainerUI(ITPCALL * pitpcall, BOOL fShow);
STDMETHODIMP ITPCALL_QueryInsertObject(ITPCALL * pitpcall, LPCLSID pclsid,
LPSTORAGE pstg, LONG cp);
STDMETHODIMP ITPCALL_DeleteObject(ITPCALL * pitpcall, LPOLEOBJECT poleobj);
STDMETHODIMP ITPCALL_QueryAcceptData(ITPCALL * pitpcall, LPDATAOBJECT pdataobj,
CLIPFORMAT *pcfFormat, DWORD reco, BOOL fReally,
HGLOBAL hMetaPict);
STDMETHODIMP ITPCALL_ContextSensitiveHelp(ITPCALL * pitpcall, BOOL fEnterMode);
STDMETHODIMP ITPCALL_GetClipboardData(ITPCALL *pitpcall, CHARRANGE *pchrg,
DWORD reco, LPDATAOBJECT *ppdataobj);
STDMETHODIMP ITPCALL_GetDragDropEffect(ITPCALL *pitpcall, BOOL fDrag,
DWORD grfKeyState, LPDWORD pdwEffect);
STDMETHODIMP ITPCALL_GetContextMenu(ITPCALL *pitpcall, WORD seltype,
LPOLEOBJECT poleobj, CHARRANGE * pchrg, HMENU * phmenu);

// Virtual table for ITP callbacks
IRichEditOleCallbackVtbl ITPCALL_Vtbl =
{
(LPVOID) ITPCALL_QueryInterface,
(LPVOID) ITPCALL_AddRef,
(LPVOID) ITPCALL_Release,
(LPVOID) ITPCALL_GetNewStorage,
(LPVOID) ITPCALL_GetInPlaceContext,
(LPVOID) ITPCALL_ShowContainerUI,
(LPVOID) ITPCALL_QueryInsertObject,
(LPVOID) ITPCALL_DeleteObject,
(LPVOID) ITPCALL_QueryAcceptData,
(LPVOID) ITPCALL_ContextSensitiveHelp,
(LPVOID) ITPCALL_GetClipboardData,
(LPVOID) ITPCALL_GetDragDropEffect,
(LPVOID) ITPCALL_GetContextMenu
};

LOCAL VOID ParseArguments(LPSTR *pszDoc, LPCSTR szCmdLine);
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
LOCAL LRESULT HandleCreate(REDOC *predoc, LPCSTR szCmdLine);
LOCAL VOID ResizeRedoc(REDOC *predoc, RECT rc);
LOCAL VOID SetupMenu(REDOC *predoc, int iMenu, HMENU hmenu);
LOCAL LRESULT HandleCommand(REDOC *predoc, HWND hwnd, WPARAM wparam,
LPARAM lparam);
LOCAL BOOL ToggleCheck(REDOC *predoc, UINT uiMenuid);
LOCAL BOOL QueryCheck(REDOC *predoc, UINT uiMenuid);
LOCAL LRESULT NewREDoc(REDOC *predoc, BOOL fPrompt, BOOL fUpdateUI);
LOCAL LONG CheckSave(REDOC *predoc);
LOCAL INT CheckRevert(REDOC *predoc);
LOCAL LRESULT CloseREDoc(REDOC *predoc, BOOL fPrompt, BOOL fUpdateUI);
DWORD CALLBACK MyRead(DWORD dwCookie, LPBYTE pbBuffer, LONG cb, LONG *pcb);
LOCAL LRESULT OpenREDoc(REDOC *predoc, BOOL fInsert);
LOCAL LRESULT RevertREDoc(REDOC *predoc);
LOCAL DWORD ReadREDoc(REDOC *predoc, LPCSTR szFile, LPCSTR szTitle,
DWORD dwFormat, BOOL fInsert);
DWORD CALLBACK MyWrite(DWORD dwCookie, LPBYTE pbBuffer, LONG cb, LONG *pcb);
LOCAL LRESULT SaveREDoc(REDOC *predoc);
LOCAL LRESULT SaveREDocAs(REDOC *predoc, BOOL fSelect);
LOCAL LRESULT InsertObject(REDOC *predoc);
LOCAL VOID SelectCharFormat(REDOC *predoc);
LOCAL VOID SaveWindowPos(HWND hwnd);
LOCAL BOOL FRestoreWindowPos(WINDOWPLACEMENT *pwndpl);
LOCAL VOID SetAlignment(REDOC *predoc, WORD wAlignment);
LOCAL VOID IndentFirst(REDOC *predoc, BOOL fIndent);
LOCAL VOID ProtectSelection(REDOC *predoc);
LOCAL VOID SetWordWrap(REDOC *predoc, BOOL fWysiwyg, BOOL fWrap);
LOCAL VOID SetupWordWrapMenu(REDOC *predoc);
LOCAL VOID SetOffset(REDOC *predoc, BOOL fSuperscript);
LRESULT CALLBACK PPDlgProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam);
LOCAL BOOL FPPInit(PRINTPREVIEW *ppp);
LOCAL VOID PPInitDialogSize(PRINTPREVIEW *ppp);
LOCAL VOID PPChangePage(PRINTPREVIEW *ppp, BOOL fPrev);
LOCAL VOID PPPaint(PRINTPREVIEW *ppp);
VOID DoVerb(REDOC * predoc, INT ioleverb);
LOCAL VOID FindReplace(REDOC *predoc, BOOL fReplace);
LOCAL VOID ProcessFindReplace(REDOC *predoc, FINDREPLACE *pfr);
LOCAL BOOL FEnablePaste(VOID);
LOCAL VOID UpdateFormatBar(REDOC *predoc);
LOCAL VOID ShowMargins(REDOC *predoc);

int PASCAL
WinMain(HINSTANCE hinstCurr, HINSTANCE hinstPrev, LPSTR szCmdLine, int nCmdShow)
{
SCODE sc;
MSG msg;
WNDCLASS wndclass;
WINDOWPLACEMENT wndpl;
TCHAR *szDoc = NULL;
REDOC *predoc = NULL;
HACCEL hAccels;
#ifndef NO_OLE
LPOLEINPLACEACTIVEOBJECT pipaobj;
#endif// NO_OLE

    LoadLibrary("RichEd32.Dll"); // Load the RichEdit DLL to activate the
 // RichEdit classes

    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = WndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = sizeof(REDOC *);
    wndclass.hInstance     = hinstCurr;
    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);
    wndclass.hIcon       = LoadIcon(hinstCurr, TEXT("IconApp"));
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = szAppName;

RegisterClass(&wndclass);
hinst = hinstCurr;

#ifdef TEST_OLE_INITIALIZE
OleInitialize(NULL);
OleUninitialize();
OleInitialize(NULL);
OleUninitialize();
#endif// TEST_OLE_INITIALIZE

OleInitialize(NULL);
sc = OleInitialize(NULL);
if (FAILED(sc))
{
TraceError("OleInitialize failed", sc);
goto done;// If OLE can't be initialized abort.
}
// OleStdInitialize();

// Initialize the format bar class
if(!FInitFormatBarClass(hinst))
{
MessageBoxA(NULL, "Unable to register format bar", NULL,
MB_ICONSTOP | MB_OK);
goto done;
}

hmenuLoaded = LoadMenu(hinstCurr, TEXT("LoadedMenu"));
hmenuFull = LoadMenu(hinstCurr, TEXT("FullMenu"));

msgFindReplace = RegisterWindowMessage(TEXT("commdlg_FindReplace"));

cfTxtObj = RegisterClipboardFormat(TEXT(CF_RETEXTOBJ));

ParseArguments(&szDoc, szCmdLine);

if(fPrint && !szDoc)
goto done;

hwndMain = CreateWindow(szAppName, szAppName,
            WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
            0, 0, 750, 500, NULL, NULL, hinstCurr, szDoc);
if(!hwndMain)
{
MessageBoxA(NULL, "Unable to create main window", NULL, MB_ICONSTOP | MB_OK);
goto done;
}
SideAssert(predoc = (REDOC *) GetWindowLong(hwndMain, 0));

if(!fPrint)
{
if(FRestoreWindowPos(&wndpl))
SetWindowPlacement(hwndMain, &wndpl);
ShowWindow(hwndMain, nCmdShow);
}

hAccels = LoadAccelerators(hinstCurr, "WARPKEYS");
Assert(hAccels);
while(GetMessage(&msg, NULL, 0, 0))
{
# ifndef NO_OLE
// Translate accelerators for possible in place objects
if(predoc->pitpcall && (pipaobj = predoc->pitpcall->pipframe->pipaobj))
pipaobj->lpVtbl->TranslateAccelerator(pipaobj, &msg);
# endif// NO_OLE

if(predoc->hwndRE && TranslateAccelerator(hwndMain, hAccels, &msg))
continue;

if(!hwndFR || !IsDialogMessage(hwndFR, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

done:
if(szDoc)
GlobalFreePtr(szDoc);
if(hwndMain)
DestroyWindow(hwndMain);
if(hmenuLoaded)
DestroyMenu(hmenuLoaded);
if(hmenuFull)
DestroyMenu(hmenuFull);

// OleStdUninitialize();

return msg.wParam;
}

LOCAL VOID ParseArguments(LPSTR *pszDoc, LPCSTR szCmdLine)
{
const TCHAR *pch;

*pszDoc = NULL;
pch = szCmdLine - 1;
while(pch)
{
pch++;
if(*pch == TEXT('/') || *pch == '-')
{
pch++;
switch(*pch)
{
case TEXT('p'):
case TEXT('P'):
fPrint = fTrue;
break;

case TEXT('w'):
case TEXT('W'):
pch++;
if(*pch == 'p' || *pch == 'P')
{
pch++;
fWrapDefault = fTrue;
fWysiwygDefault = fTrue;
}
else if(*pch == 'n' || *pch == 'N')
{
pch++;
fWrapDefault = fFalse;
fWysiwygDefault = fFalse;
}
else
{
fWrapDefault = fTrue;
fWysiwygDefault = fFalse;
}
break;

default:
if(*pch >= TEXT('0') && *pch <= TEXT('9') && !cchTextMost)
{
while(*pch >= TEXT('0') && *pch <= TEXT('9'))
{
cchTextMost *= 10;
cchTextMost += *pch++ - TEXT('0');
}
}
break;
}
}
else if(!*pszDoc)
{
while(*pch == TEXT(' '))
pch++;
if(*pch)
{
INT cch;
const TCHAR *pchT;

pchT = _tcschr(pch, TEXT(' '));
if(pchT)
cch = pchT - pch;
else
cch = lstrlen(pch);
*pszDoc = GlobalAllocPtr(GHND, cch + 1);
if(!*pszDoc)
break;
lstrcpyn(*pszDoc, (LPCSTR) pch, cch + 1);
(*pszDoc)[cch] = '\0';
}
}
pch = _tcschr(pch, TEXT(' '));
}
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
REDOC *predoc = (REDOC *) GetWindowLong(hwnd, 0);
#ifndef NO_OLE
LPOLEINPLACEACTIVEOBJECT pipaobj;
#endif// !NO_OLE

    switch(msg)
    {
case WM_NCCREATE:
predoc = (REDOC *) GlobalAllocPtr(GHND, sizeof(REDOC));
if(!predoc)
return 0;
SetWindowLong(hwnd, 0, (LONG) predoc);
predoc->hwndParent = hwnd;
#ifndef MAC
predoc->pd.lStructSize = sizeof(PRINTDLG);
predoc->pd.hwndOwner = hwnd;
predoc->pd.Flags = PD_RETURNDEFAULT;

if(!PrintDlg(&predoc->pd) && CommDlgExtendedError())
memset(&predoc->pd, 0, sizeof(PRINTDLG));
#endif
break;

case WM_NCDESTROY:
if(predoc)
{
if(predoc->pfr)
GlobalFreePtr(predoc->pfr);
GlobalFreePtr(predoc);
predoc = NULL;
SetWindowLong(hwnd, 0, 0);
}
break;

case WM_CREATE:
hwndMain = hwnd;// a bit of a cheat...

return HandleCreate(predoc,
(LPCSTR) ((CREATESTRUCT *) lparam)->lpCreateParams);
    case WM_DESTROY:
PostQuitMessage(0);
        return 0l;

case WM_ACTIVATEAPP:
#ifndef NO_OLE
// Notify in place active object of frame activation
if(predoc->pitpcall && (pipaobj = predoc->pitpcall->pipframe->pipaobj))
{
TraceTag(tagInPlace, "OnFrameWindowActivate(%d)", !!wparam);
pipaobj->lpVtbl->OnFrameWindowActivate(pipaobj, !!wparam);
}
#endif// !NO_OLE
break;

case WM_INITMENU:
if(predoc->hwndRE)
predoc->fUpdateEditMenu = TRUE;
break;

case WM_INITMENUPOPUP:
if(predoc->hwndRE)
SetupMenu(predoc, (int) lparam, (HMENU) wparam);
return 0;

case WM_GETMINMAXINFO:
((MINMAXINFO *) lparam)->ptMinTrackSize = ptMinTrackSize;
return 0;

case WM_COMMAND:
return HandleCommand(predoc, hwnd, wparam, lparam);

case WM_CLOSE:
if(predoc && predoc->hwndRE && CloseREDoc(predoc, fTrue, fFalse) < 0)
return 0;
if(!fPrint)
SaveWindowPos(hwnd);
DestroyWindow(hwnd);
return 0;

case WM_ACTIVATE:
if(predoc->hwndRE && LOWORD(wparam))
{
SetFocus(predoc->hwndRE);
return 0;
}
break;

case WM_SIZE:
if(predoc->hwndFormatBar)
SendMessage(predoc->hwndFormatBar, WM_SIZE, 0, 0);
if(predoc->hwndRE)
{
RECT rc;

GetClientRect(hwnd, &rc);
ResizeRedoc(predoc, rc);
#ifdef DEBUG
SendMessage(predoc->hwndRE, EM_REQUESTRESIZE, 0, 0);
#endif// DEBUG
}
break;

case WM_NOTIFY:
switch(((NMHDR *) lparam)->code)
{
case EN_SELCHANGE:
if(predoc->hwndFormatBar)
UpdateFormatBar(predoc);
return 0;

case EN_PROTECTED:
{
ENPROTECTED *penprotected = (ENPROTECTED *) lparam;

// allow change of protected attribute
if(penprotected->msg == EM_SETCHARFORMAT &&
((CHARFORMAT *) penprotected->lParam)->dwMask & CFM_PROTECTED)
{
return 0;
}
}
MessageBeep(0);
return 1;

case EN_CORRECTTEXT:
{
ENCORRECTTEXT *pct = (ENCORRECTTEXT *) lparam;

if(pct->chrg.cpMost - pct->chrg.cpMin > cchMaxCorrectText)
{
MessageBox(hwndMain,
"You are trying to correct too much text. "
"Decrease the size of your selection.",
"REITP: Correct Text", MB_OK);
return 0;
}
}
return 1;

#ifndef NO_OLE
case EN_DROPFILES:
# ifdef DEBUG
if(!QueryCheck(predoc, IDM_IGNOREDROPS))
return fFalse;
TraceTag(tagGeneral, "Ignoring drop of %d file(s) at position %d",
DragQueryFile(((ENDROPFILES *) lparam)->hDrop, (UINT) -1,
NULL, 0),
((ENDROPFILES *) lparam)->cp);
// ignore file drops
return fTrue;
# else// DEBUG
return ((ENDROPFILES *) lparam)->fProtected;
# endif// DEBUG, else
#endif// !NO_OLE

#ifdef DEBUG
case EN_MSGFILTER:
{
MSGFILTER *pmsgfilter = (MSGFILTER *) lparam;

// eat left button downs and 'e's
return (pmsgfilter->msg == WM_LBUTTONDOWN) ||
((pmsgfilter->msg == WM_CHAR) &&
(pmsgfilter->wParam == 'e'));
}

case EN_REQUESTRESIZE:
ResizeRedoc(predoc, ((REQRESIZE *) lparam)->rc);
return 0;
#endif// DEBUG
}
break;

default:
if(msg != msgFindReplace)
break;

if(((FINDREPLACE *) lparam)->Flags & FR_DIALOGTERM)
{
hwndFR = 0;
SendMessage(predoc->hwndRE, EM_HIDESELECTION, fTrue, fTrue);
return 0;
}
ProcessFindReplace(predoc, (FINDREPLACE *) lparam);
return 0;
    }

return DefWindowProc(hwnd, msg, wparam, lparam);
}

LOCAL LRESULT HandleCreate(REDOC *predoc, LPCSTR szFile)
{
DWORD dwError;

if(!szFile || !*szFile)
goto no_file;

if((dwError = ReadREDoc(predoc, szFile, NULL, 0, FALSE)))
{
TCHAR szErr[300];

wsprintf(szErr, TEXT("Error opening %s [%ld]"), szFile, dwError);
MessageBoxA(hwndMain, szErr, NULL, MB_OK);
goto no_file;
}

if(fPrint)
{
PrintREDoc(predoc);
PostQuitMessage(0);
}
else
{
TCHAR szT[256];

wsprintf(szT, szFmtTitle, predoc->szTitle);
SetWindowText(predoc->hwndParent, szT);

SetMenu(hwndMain, hmenuFull);
SetupWordWrapMenu(predoc);
DrawMenuBar(hwndMain);
}

#ifdef TIME_OPEN
PostMessage(hwndMain, WM_CLOSE, 0, 0);
#endif// TIME_OPEN

return 0;

no_file:
if(NewREDoc(predoc, fFalse, fTrue) < 0)
{
SetMenu(hwndMain, hmenuLoaded);
}
else
{
SetMenu(hwndMain, hmenuFull);
SetupWordWrapMenu(predoc);
}
DrawMenuBar(hwndMain);

return 0;
}

LOCAL VOID ResizeRedoc(REDOC *predoc, RECT rc)
{
RECT rcParent;
#ifndef NO_OLE
LPOLEINPLACEACTIVEOBJECT pipaobj = NULL;
#endif// NO_OLE

GetClientRect(predoc->hwndParent, &rcParent);
InflateRect(&rcParent, dxRESize, dyRESize);

// If we have a format bar, take it into account
if(predoc->hwndFormatBar)
{
RECT rcFmtBar;

GetClientRect(predoc->hwndFormatBar, &rcFmtBar);
rcParent.top += rcFmtBar.bottom;
}
rc.top = max(rcParent.top, rc.top);
rc.left = max(rcParent.left, rc.left);
rc.right = min(rcParent.right, rc.right);
rc.bottom = max(rc.bottom, rc.top + ptMinTrackSize.y);
rc.bottom = min(rcParent.bottom, rc.bottom);

MoveWindow(predoc->hwndRE, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, fTrue);

if(GetMenuState(hmenuFull, IDM_SHOWMARGINS, MF_BYCOMMAND) & MF_CHECKED)
{
rc.bottom -= rc.top;
rc.right -= rc.left;

rc.top = 20;
rc.bottom -= 30;
if(rc.bottom < rc.top)
rc.bottom = rc.top;
rc.left = 30;
rc.right -= 30;
if(rc.right < rc.left)
rc.right = rc.left;
SendMessage(predoc->hwndRE, EM_SETRECT, 0, (LPARAM) &rc);
}

// Notify in place active object of resize
//
// NOTE : Do not change the following line so that it uses an &&
// operation, this causes the Mac to lock up since the compiler
// apparently checks the second half of an AND regardless of the
// initial half's value.

#ifndef NO_OLE
if(predoc->pitpcall && (pipaobj = predoc->pitpcall->pipframe->pipaobj))
{
TraceTag(tagInPlace, "ResizeBorder");
GetClientRect(hwndMain, &rc);
# ifdef MAC
pipaobj->lpVtbl->ResizeBorder(pipaobj, (RectPtr) &rc,
  (LPOLEINPLACEUIWINDOW) predoc->pitpcall->pipframe, TRUE);
// Throw out the mem
# else// MAC
pipaobj->lpVtbl->ResizeBorder(pipaobj, &rc,
   (LPOLEINPLACEUIWINDOW) predoc->pitpcall->pipframe, TRUE);
# endif// MAC, else
}
#endif // !NO_OLE
}

LOCAL VOID SetupMenu(REDOC *predoc, int iMenu, HMENU hmenu)
{
DWORD dw;
LPOLEOBJECT poleobj = NULL;

switch(iMenu)
{
case 0:// file
if(!predoc->szFile[0])
dw = 0;
else
dw = (DWORD) SendMessage(predoc->hwndRE, EM_GETMODIFY, 0, 0);
EnableMenuItem(hmenu, IDM_REVERT, dw ? uiMFEnabled : uiMFDisabled);
EnableMenuItem(hmenu, IDM_SAVE, predoc->fReadOnly
? uiMFDisabled : uiMFEnabled);
break;

case 1:// edit

dw = uiMFDisabled;
// see if something can be pasted
// let RichEdit figure out if it's read-only
if(SendMessage(predoc->hwndRE, EM_CANPASTE, 0, 0))
dw = uiMFEnabled;
EnableMenuItem(hmenu, IDM_PASTE, (UINT) dw);

dw = 0;
if(!predoc->fReadOnly)
dw = (DWORD) SendMessage(predoc->hwndRE, EM_CANUNDO, 0, 0);
EnableMenuItem(hmenu, IDM_UNDO, dw ? uiMFEnabled : uiMFDisabled);

//$ REVIEW: use EN_SELCHANGE?
dw = (DWORD) SendMessage(predoc->hwndRE, EM_SELECTIONTYPE, 0, 0);
EnableMenuItem(hmenu, IDM_COPY, dw ? uiMFEnabled : uiMFDisabled);
#ifdef DEBUG
if(predoc->fReadOnly && !QueryCheck(predoc, IDM_ENABLECUTREADONLY))
dw = 0;
EnableMenuItem(hmenu, IDM_CUT, dw ? uiMFEnabled : uiMFDisabled);
if(predoc->fReadOnly)
dw = 0;
#else// DEBUG
if(predoc->fReadOnly)
dw = 0;
EnableMenuItem(hmenu, IDM_CUT, dw ? uiMFEnabled : uiMFDisabled);
#endif// DEBUG, else
EnableMenuItem(hmenu, IDM_DELETE, dw ? uiMFEnabled : uiMFDisabled);

EnableMenuItem(hmenu, IDM_INSOBJ, predoc->fReadOnly
? uiMFDisabled : uiMFEnabled);
EnableMenuItem(hmenu, IDM_REPLACE, predoc->fReadOnly ?
uiMFDisabled : uiMFEnabled);

// That's all we need to do
if(!predoc->fUpdateEditMenu)
break;

#ifndef NO_OLE
// Find out if we have a single object to deal with
if(!(dw ^ SEL_OBJECT))
{
REOBJECT reobj = { 0 };

reobj.cbStruct = sizeof(REOBJECT);
reobj.cp = REO_CP_SELECTION;

if(!predoc->preole->lpVtbl->GetObject(predoc->preole,
REO_IOB_USE_CP, &reobj,
REO_GETOBJ_POLEOBJ))
{
poleobj = reobj.poleobj;
}
}
// Ask OLE to build the menu for us

# ifdef MAC
//$ FUTURE MAC
dw = IDM_OBJECTCONVERT;
#  ifdef NEVER
macMenu1 = CheckoutMenu(hmenu, 2);
if(!macMenu1)
DebugStr("\pCheckoutMenu(macMenu1) failed");
macMenu2 = CheckoutMenu(predoc->hmenuVerbs, 2);
if(!macMenu2)
DebugStr("\pCheckoutMenu(macMenu2) failed");

if(!OleUIAddVerbMenu(poleobj,
NULL,// Ask OLE to look up the name for us
macMenu1, predoc->hmenuVerbs, ipos_Object,
TRUE, (short *) &dw))
{
DebugStr("\pOleUIAddVerbMenu failed!");
}
CheckinMenu(hmenu, 2);
CheckinMenu(predoc->hmenuVerbs, 2);
#  endif// NEVER
# else// MAC
OleUIAddVerbMenu(poleobj,
NULL,// Ask OLE to look up the name for us
hmenu, ipos_Object, IDM_OBJECTMIN,
0, TRUE, IDM_OBJECTCONVERT, &predoc->hmenuVerbs);
# endif// MAC, else

if(poleobj)
poleobj->lpVtbl->Release(poleobj);
#endif // !NO_OLE
// We don't have to do all this hard work again unless the user leaves the
// menu
predoc->fUpdateEditMenu = FALSE;
break;

case 2:// format
{
BOOL fReadOnly = !!predoc->fReadOnly;
UINT uiSuperscript = MF_UNCHECKED;
UINT uiSubscript = MF_UNCHECKED;
UINT uiProtected = MF_UNCHECKED;
UINT uiLeft = MF_UNCHECKED;
UINT uiCenter = MF_UNCHECKED;
UINT uiRight = MF_UNCHECKED;
UINT uiFirstIndent = MF_UNCHECKED;
UINT uiFirstOutdent = MF_UNCHECKED;
CHARFORMAT cf;
PARAFORMAT pf;

cf.cbSize = sizeof(CHARFORMAT);
pf.cbSize = sizeof(PARAFORMAT);

SendMessage(predoc->hwndRE, EM_GETCHARFORMAT, fTrue, (LPARAM) &cf);
if(cf.yOffset > 0)
uiSuperscript = MF_CHECKED;
else if(cf.yOffset < 0)
uiSubscript = MF_CHECKED;
if(cf.dwEffects & CFE_PROTECTED)
uiProtected = MF_CHECKED;

SendMessage(predoc->hwndRE, EM_GETPARAFORMAT, 0, (LPARAM) &pf);
if(!pf.wAlignment || pf.wAlignment == PFA_LEFT)
uiLeft = MF_CHECKED;
else if(pf.wAlignment == PFA_CENTER)
uiCenter = MF_CHECKED;
else
uiRight = MF_CHECKED;
if(pf.dxOffset < 0)
uiFirstIndent = MF_CHECKED;
else if(pf.dxOffset > 0)
uiFirstOutdent = MF_CHECKED;

CheckMenuItem(hmenu, IDM_SUPERSCRIPT, uiSuperscript);
CheckMenuItem(hmenu, IDM_SUBSCRIPT, uiSubscript);
CheckMenuItem(hmenu, IDM_PROTECTED, uiProtected);
CheckMenuItem(hmenu, IDM_ALIGNLEFT, uiLeft);
CheckMenuItem(hmenu, IDM_ALIGNCENTER, uiCenter);
CheckMenuItem(hmenu, IDM_ALIGNRIGHT, uiRight);
CheckMenuItem(hmenu, IDM_INDENTFIRST, uiFirstIndent);
CheckMenuItem(hmenu, IDM_OUTDENTFIRST, uiFirstOutdent);

EnableMenuItem(hmenu, IDM_CHARFORMAT, fReadOnly
? uiMFDisabled : uiMFEnabled);
EnableMenuItem(hmenu, IDM_SUPERSCRIPT, fReadOnly
? uiMFDisabled : uiMFEnabled);
EnableMenuItem(hmenu, IDM_SUBSCRIPT, fReadOnly
? uiMFDisabled : uiMFEnabled);
EnableMenuItem(hmenu, IDM_ALIGNLEFT, fReadOnly
? uiMFDisabled : uiMFEnabled);
EnableMenuItem(hmenu, IDM_ALIGNCENTER, fReadOnly
? uiMFDisabled : uiMFEnabled);
EnableMenuItem(hmenu, IDM_ALIGNRIGHT, fReadOnly
? uiMFDisabled : uiMFEnabled);

EnableMenuItem(hmenu, IDM_INDENTFIRST, fReadOnly
? uiMFDisabled : uiMFEnabled);
EnableMenuItem(hmenu, IDM_OUTDENTFIRST, fReadOnly
? uiMFDisabled : uiMFEnabled);
}
break;

case 3:// options
{
const DWORD eco = SendMessage(predoc->hwndRE, EM_GETOPTIONS, 0, 0);
UINT uiSelBar = MF_UNCHECKED;
UINT uiAutoWordSel = MF_UNCHECKED;
#ifdef DEBUG
UINT uiReadOnly = MF_UNCHECKED;
UINT uiAutoHScroll = MF_UNCHECKED;
UINT uiAutoVScroll = MF_UNCHECKED;
UINT uiAutoHideSel = MF_UNCHECKED;
#endif// DEBUG

if(eco & ECO_SELECTIONBAR)
uiSelBar = MF_CHECKED;
if(eco & ECO_AUTOWORDSELECTION)
uiAutoWordSel = MF_CHECKED;
#ifdef DEBUG
if(eco & ECO_READONLY)
uiReadOnly = MF_CHECKED;
if(eco & ECO_AUTOHSCROLL)
uiAutoHScroll = MF_CHECKED;
if(eco & ECO_AUTOVSCROLL)
uiAutoVScroll = MF_CHECKED;
if(!(eco & ECO_NOHIDESEL))
uiAutoHideSel = MF_CHECKED;
#endif// DEBUG

CheckMenuItem(hmenu, IDM_SELBAR, uiSelBar);
CheckMenuItem(hmenu, IDM_AUTOWORDSEL, uiAutoWordSel);
#ifdef DEBUG
CheckMenuItem(hmenu, IDM_READONLY, uiReadOnly);
CheckMenuItem(hmenu, IDM_AUTOHSCROLL, uiAutoHScroll);
CheckMenuItem(hmenu, IDM_AUTOVSCROLL, uiAutoVScroll);
CheckMenuItem(hmenu, IDM_AUTOHIDESEL, uiAutoHideSel);
#endif// DEBUG
}
break;

}
}

LOCAL LRESULT HandleCommand(REDOC *predoc, HWND hwnd, WPARAM wparam,
LPARAM lparam)
{
LRESULT lres = 0;
INTnID;

if(GET_WM_COMMAND_HWND(wparam, lparam) == predoc->hwndRE && predoc->hwndRE)
{
WORD wNotification = GET_WM_COMMAND_CMD(wparam, lparam);
ASSENSZ *passensz;

switch(wNotification)
{
case EN_CHANGE:
if(predoc->hwndFormatBar)
UpdateFormatBar(predoc);
break;

case EN_ERRSPACE:
if(fErrSpace)
break;
fErrSpace = fTrue;
// fall through to default

default:
for(passensz = rgassenszErrors; passensz->szDescription; passensz++)
{
if(passensz->wNotification == wNotification)
{
MessageBeep(0);
MessageBoxA(hwndMain, passensz->szDescription, NULL,
MB_ICONSTOP | MB_OK);
return 0;
}
}
break;
}
return 0;
}

fErrSpace = fFalse;

nID = GET_WM_COMMAND_ID(wparam, lparam);
switch(nID)
{
#ifdef DEBUG
case IDM_CLSDBG:
ClearDebugScreen();
break;

case IDM_TRACEDLG:
DoTagsDialog();
break;
#endif// DEBUG

case IDM_NEW:
lres = NewREDoc(predoc, fTrue, fTrue);
#ifdef DEBUG
if(predoc->hwndRE)
SendMessage(predoc->hwndRE, EM_REQUESTRESIZE, 0, 0);
#endif// DEBUG
break;

case IDM_OPEN:
lres = OpenREDoc(predoc, FALSE);
break;

case IDM_REVERT:
lres = RevertREDoc(predoc);
break;

case IDM_PRINT:
PrintREDoc(predoc);
break;

case IDM_PRINTPREVIEW:
DialogBoxParam(HinstFromHwnd(hwndMain), TEXT("PRINTPREVIEW"), hwndMain,
(DLGPROC) PPDlgProc, (LPARAM) predoc);
break;

case IDM_PRINTSETUP:
predoc->pd.lStructSize = sizeof(PRINTDLG);
predoc->pd.Flags = PD_PRINTSETUP;
if(PrintDlg(&predoc->pd) && predoc->fWysiwyg)
{
// illegal values to force SetWordWrap() to do something
predoc->fWrap = fFalse;
predoc->fWysiwyg = fTrue;
SetWordWrap(predoc, fTrue, fTrue);
}
break;

case IDM_EXIT:
PostMessage(hwndMain, WM_CLOSE, 0, 0);
return 0;

case IDM_CLOSE:
return CloseREDoc(predoc, fTrue, fTrue);

case IDM_SAVE:
lres = SaveREDoc(predoc);
break;

case IDM_SAVEAS:
lres = SaveREDocAs(predoc, FALSE);
break;

case IDM_UNDO:
lres = SendMessage(predoc->hwndRE, EM_UNDO, 0, 0);
break;

case IDM_CUT:
lres = SendMessage(predoc->hwndRE, WM_CUT, 0, 0);
break;

case IDM_COPY:
lres = SendMessage(predoc->hwndRE, WM_COPY, 0, 0);
break;

case IDM_PASTE:
lres = SendMessage(predoc->hwndRE, WM_PASTE, 0, 0);
break;

case IDM_PASTESPECIAL:
NYI("Paste special");
break;

case IDM_DELETE:
lres = SendMessage(predoc->hwndRE, WM_KEYDOWN,
(WPARAM) VK_DELETE, 0);
break;

case IDM_SELECTALL:
lres = SendMessage(predoc->hwndRE, EM_SETSEL, 0, (LPARAM) -1);
break;

case IDM_FIND:
FindReplace(predoc, fFalse);
break;

case IDM_REPLACE:
FindReplace(predoc, fTrue);
break;

case IDM_OBJECT:
NYI("Object");
break;

case IDM_OBJECTCONVERT:
NYI("Object Convert");
break;

case IDM_INSOBJ:
return InsertObject(predoc);

case IDM_INSFILE:
lres = OpenREDoc(predoc, TRUE);
break;

case IDM_SAVESEL:
lres = SaveREDocAs(predoc, TRUE);
break;

case IDM_CHARFORMAT:
SelectCharFormat(predoc);
break;

case IDM_APPLYTOWORD:
predoc->scf = SCF_SELECTION;
if(ToggleCheck(predoc, IDM_APPLYTOWORD))
predoc->scf |= SCF_WORD;
break;

case IDM_SUPERSCRIPT:
SetOffset(predoc, fTrue);
break;

case IDM_SUBSCRIPT:
SetOffset(predoc, fFalse);
break;

case IDM_ALIGNLEFT:
SetAlignment(predoc, PFA_LEFT);
break;

case IDM_ALIGNCENTER:
SetAlignment(predoc, PFA_CENTER);
break;

case IDM_ALIGNRIGHT:
SetAlignment(predoc, PFA_RIGHT);
break;

case IDM_INDENTFIRST:
IndentFirst(predoc, fTrue);
break;

case IDM_OUTDENTFIRST:
IndentFirst(predoc, fFalse);
break;

case IDM_MARGINS:
NYI("set margins");
break;

case IDM_TABS:
NYI("set tabs");
break;

case IDM_PROTECTED:
ProtectSelection(predoc);
break;

case IDM_NOWRAP:
SetWordWrap(predoc, fFalse, fFalse);
break;

case IDM_WRAP:
SetWordWrap(predoc, fFalse, fTrue);
break;

case IDM_WYSIWYG:
SetWordWrap(predoc, fTrue, fTrue);
break;

case IDM_SELBAR:
SendMessage(predoc->hwndRE, EM_SETOPTIONS, ECOOP_XOR, ECO_SELECTIONBAR);
ToggleCheck(predoc, IDM_SELBAR);
break;

case IDM_AUTOWORDSEL:
SendMessage(predoc->hwndRE, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOWORDSELECTION);
ToggleCheck(predoc, IDM_AUTOWORDSEL);
break;

case IDM_SHOWMARGINS:
ShowMargins(predoc);
break;

#ifdef DEBUG
case IDM_READONLY:
SendMessage(predoc->hwndRE, EM_SETOPTIONS, ECOOP_XOR, ECO_READONLY);
predoc->fReadOnly = !!ToggleCheck(predoc, IDM_READONLY);
EnableWindow(predoc->hwndFormatBar, !predoc->fReadOnly);
break;

case IDM_AUTOHSCROLL:
SendMessage(predoc->hwndRE, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOHSCROLL);
ToggleCheck(predoc, IDM_AUTOHSCROLL);
break;

case IDM_AUTOVSCROLL:
SendMessage(predoc->hwndRE, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOVSCROLL);
ToggleCheck(predoc, IDM_AUTOVSCROLL);
break;

case IDM_AUTOHIDESEL:
SendMessage(predoc->hwndRE, EM_SETOPTIONS, ECOOP_XOR, ECO_NOHIDESEL);
ToggleCheck(predoc, IDM_AUTOHIDESEL);
break;
#endif// DEBUG

case IDM_GOTOCURSOR:
{
CHARRANGE cr;
POINT pt;

GetCursorPos(&pt);
ScreenToClient(predoc->hwndRE, &pt);
cr.cpMin = SendMessage(predoc->hwndRE, EM_CHARFROMPOS, 0,
(LPARAM) &pt);
cr.cpMost = cr.cpMin;
SendMessage(predoc->hwndRE, EM_EXSETSEL, 0, (LPARAM) &cr);
}
break;

case IDM_MOVECURSOR:
{
CHARRANGE cr;
POINT pt;

SendMessage(predoc->hwndRE, EM_EXGETSEL, 0, (LPARAM) &cr);
SendMessage(predoc->hwndRE, EM_POSFROMCHAR, (WPARAM) &pt, cr.cpMin);
ClientToScreen(predoc->hwndRE, &pt);
SetCursorPos(pt.x, pt.y);
}
break;

case TBI_IncreaseIndent:
case TBI_DecreaseIndent:
if(predoc->fReadOnly)
{
MessageBeep(0);
}
else
{
PARAFORMAT pf;

pf.cbSize = sizeof(PARAFORMAT);

if(GetFocus() == predoc->hwndFormatBar)
SetFocus(predoc->hwndRE);
pf.dwMask = PFM_OFFSETINDENT;
pf.dxStartIndent = (nID == TBI_IncreaseIndent)
? cxBulletIndent
: -cxBulletIndent;
if(!SendMessage(predoc->hwndRE, EM_SETPARAFORMAT, FALSE,
(LPARAM) &pf))
{
MessageBeep(0);
}
}
break;

case TBI_Name:
case TBI_Size:
case TBI_Bold:
case TBI_Italic:
case TBI_Underline:
case TBI_Color:
{
CHARFORMAT cf;

cf.cbSize = sizeof(CHARFORMAT);
if(GetFocus() == predoc->hwndFormatBar)
SetFocus(predoc->hwndRE);
SendMessage(predoc->hwndFormatBar, EM_GETCHARFORMAT, 0,
(LPARAM) &cf);
SendMessage(predoc->hwndRE, EM_SETCHARFORMAT, predoc->scf,
(LPARAM) &cf);
}
break;

case TBI_AccelBold:
case TBI_AccelItalic:
case TBI_AccelUnderline:
{
CHARFORMAT cf;
DWORD dwEffects;

cf.cbSize = sizeof(CHARFORMAT);
SendMessage(predoc->hwndFormatBar, EM_GETCHARFORMAT, 0,
(LPARAM) &cf);
switch (nID)
{
case TBI_AccelBold:
dwEffects = CFE_BOLD;
break;
case TBI_AccelItalic:
dwEffects = CFE_ITALIC;
break;
case TBI_AccelUnderline:
dwEffects = CFE_UNDERLINE;
break;
}

cf.dwMask |= dwEffects;
cf.dwEffects ^= dwEffects;

if(GetFocus() == predoc->hwndFormatBar)
SetFocus(predoc->hwndRE);
SendMessage(predoc->hwndFormatBar, EM_SETCHARFORMAT, predoc->scf,
(LPARAM) &cf);
SendMessage(predoc->hwndRE, EM_SETCHARFORMAT, predoc->scf,
(LPARAM) &cf);
}
break;

case TBI_Bullet:
case TBI_Left:
case TBI_Center:
case TBI_Right:
{
PARAFORMAT pf;

pf.cbSize = sizeof(PARAFORMAT);

if(GetFocus() == predoc->hwndFormatBar)
SetFocus(predoc->hwndRE);
SendMessage(predoc->hwndFormatBar, EM_GETPARAFORMAT, 0,
(LPARAM) &pf);
SendMessage(predoc->hwndRE, EM_SETPARAFORMAT, FALSE, (LPARAM) &pf);
}
break;

#ifdef DEBUG
case IDM_DBGPED:
SendMessage(predoc->hwndRE, EM_DBGPED, 0, 0);
break;

case IDM_GETTEXT:
return GetText(predoc);

case IDM_IGNORELEFTCLICK:
IgnoreLeftClick(predoc);
break;

case IDM_EATES:
EatEs(predoc);
break;

case IDM_BOTTOMLESS:
Bottomless(predoc);
break;

case IDM_IGNOREDROPS:
case IDM_TEXTONLY:
case IDM_REFUSEGRAPH:
case IDM_PPMETA:
case IDM_ENABLECUTREADONLY:
case IDM_ENABLEPASTEREADONLY:
case IDM_ENABLEDRAGREADONLY:
case IDM_SWAPDRAGEFFECT:
ToggleCheck(predoc, nID);
break;

case IDM_HIDE:
ShowWindow(predoc->hwndRE, ToggleCheck(predoc, IDM_HIDE) ? SW_HIDE : SW_SHOW);
break;

case IDM_PASTEPLAINTEXT:
lres = SendMessage(predoc->hwndRE, EM_PASTESPECIAL, CF_TEXT, 0);
break;

case IDM_PASTETXTOBJ:
lres = SendMessage(predoc->hwndRE, EM_PASTESPECIAL, cfTxtObj, 0);
break;

case IDM_PASTERTFASTEXT:
lres = SendMessage(predoc->hwndRE, EM_PASTESPECIAL, cfRTFAsText, 0);
break;

case IDM_BIGLIMIT:
SetLimit(predoc);
break;

case IDM_FILLERUP:
FillerUp(predoc);
break;
#endif// DEBUG

default:
// Pass through OLE verbs
if(nID >= IDM_OBJECTMIN)
{
DoVerb(predoc, nID - IDM_OBJECTMIN);
}
return DefWindowProc(hwnd, WM_COMMAND, wparam, lparam);
}

return lres;
}

VOID DoVerb(REDOC * predoc, INT ioleverb)
{
REOBJECT reobj = { 0 };
POINT pt;
RECT rc = { 0 };

reobj.cbStruct = sizeof(REOBJECT);
if(predoc->preole->lpVtbl->GetObject(predoc->preole, REO_IOB_SELECTION,
&reobj,
REO_GETOBJ_POLESITE |
REO_GETOBJ_POLEOBJ))
{
AssertSz(FALSE, "DoVerb without object");
}

SendMessage(predoc->hwndRE, EM_POSFROMCHAR, (WPARAM) &pt, reobj.cp);

XformSizeInHimetricToPixels(NULL, &reobj.sizel, &reobj.sizel);
rc.right = (INT) reobj.sizel.cx;
rc.bottom = (INT) reobj.sizel.cy;
OffsetRect(&rc, pt.x, pt.y);

reobj.poleobj->lpVtbl->DoVerb(reobj.poleobj, ioleverb, NULL,
reobj.polesite, 0, predoc->hwndRE, &rc);
reobj.poleobj->lpVtbl->Release(reobj.poleobj);
reobj.polesite->lpVtbl->Release(reobj.polesite);

}

#ifdef DEBUG

LOCAL VOID IgnoreLeftClick(REDOC *predoc)
{
HMENU hmenu = GetMenu(hwndMain);
UINT uiMenuFlags;
DWORD dwMask;

Assert(predoc->hwndRE);

dwMask = SendMessage(predoc->hwndRE, EM_GETEVENTMASK, 0, 0);
uiMenuFlags = GetMenuState(hmenu, IDM_IGNORELEFTCLICK, MF_BYCOMMAND);
if(uiMenuFlags & MF_CHECKED)
dwMask &= ~ENM_MOUSEEVENTS;
else
dwMask |= ENM_MOUSEEVENTS;
uiMenuFlags ^= MF_CHECKED;
CheckMenuItem(hmenu, IDM_IGNORELEFTCLICK, MF_BYCOMMAND |
(uiMenuFlags & MF_CHECKED));
SendMessage(predoc->hwndRE, EM_SETEVENTMASK, 0, dwMask);
}

LOCAL VOID EatEs(REDOC *predoc)
{
HMENU hmenu = GetMenu(hwndMain);
UINT uiMenuFlags;
DWORD dwMask;

Assert(predoc->hwndRE);

dwMask = SendMessage(predoc->hwndRE, EM_GETEVENTMASK, 0, 0);
uiMenuFlags = GetMenuState(hmenu, IDM_EATES, MF_BYCOMMAND);
if(uiMenuFlags & MF_CHECKED)
dwMask &= ~ENM_KEYEVENTS;
else
dwMask |= ENM_KEYEVENTS;
uiMenuFlags ^= MF_CHECKED;
CheckMenuItem(hmenu, IDM_EATES, MF_BYCOMMAND | (uiMenuFlags & MF_CHECKED));
SendMessage(predoc->hwndRE, EM_SETEVENTMASK, 0, dwMask);
}

LOCAL VOID Bottomless(REDOC *predoc)
{
HMENU hmenu = GetMenu(hwndMain);
UINT uiMenuFlags;
DWORD dwMask;

Assert(predoc->hwndRE);

dwMask = SendMessage(predoc->hwndRE, EM_GETEVENTMASK, 0, 0);
uiMenuFlags = GetMenuState(hmenu, IDM_BOTTOMLESS, MF_BYCOMMAND);
if(uiMenuFlags & MF_CHECKED)
dwMask &= ~ENM_REQUESTRESIZE;
else
dwMask |= ENM_REQUESTRESIZE;
uiMenuFlags ^= MF_CHECKED;
CheckMenuItem(hmenu, IDM_BOTTOMLESS,
MF_BYCOMMAND | (uiMenuFlags & MF_CHECKED));
SendMessage(predoc->hwndRE, EM_SETEVENTMASK, 0, dwMask);
if(dwMask & ENM_REQUESTRESIZE)
{
SendMessage(predoc->hwndRE, EM_REQUESTRESIZE, 0, 0);
}
else
{
RECT rc;

GetClientRect(predoc->hwndParent, &rc);
ResizeRedoc(predoc, rc);
}
}

#endif// DEBUG

LOCAL BOOL ToggleCheck(REDOC *predoc, UINT uiMenuid)
{
HMENU hmenu = GetMenu(hwndMain);
UINT uiMenuFlags;

Assert(predoc->hwndRE);

uiMenuFlags = GetMenuState(hmenu, uiMenuid, MF_BYCOMMAND);
uiMenuFlags ^= MF_CHECKED;
CheckMenuItem(hmenu, uiMenuid, MF_BYCOMMAND | (uiMenuFlags & MF_CHECKED));

return !!(uiMenuFlags & MF_CHECKED);
}

LOCAL BOOL QueryCheck(REDOC *predoc, UINT uiMenuid)
{
HMENU hmenu = GetMenu(hwndMain);
UINT uiMenuFlags;

Assert(predoc->hwndRE);
if(!hmenu)
return fFalse;

uiMenuFlags = GetMenuState(hmenu, uiMenuid, MF_BYCOMMAND);

return !!(uiMenuFlags & MF_CHECKED);
}

LOCAL LRESULT NewREDoc(REDOC *predoc, BOOL fPrompt, BOOL fUpdateUI)
{
RECT rc;
DWORD dwStyle;
if(predoc->hwndRE)
{
PARAFORMAT pf = {0};

pf.cbSize = sizeof(PARAFORMAT);

if(fPrompt && CheckSave(predoc) < 0)
return -1;
predoc->szFile[0] = TEXT('\0');
lstrcpy(predoc->szTitle, szUntitled);
SetWindowText(predoc->hwndRE, TEXT(""));
pf.wAlignment = PFA_LEFT;
pf.cTabCount = 1;
pf.rgxTabs[0] = lDefaultTab;
pf.dwMask = PFM_STARTINDENT | PFM_RIGHTINDENT | PFM_OFFSET |
PFM_ALIGNMENT | PFM_TABSTOPS | PFM_NUMBERING;
SendMessage(predoc->hwndRE, EM_SETPARAFORMAT, 0, (LPARAM) &pf);
SendMessage(predoc->hwndRE, EM_SETCHARFORMAT, 0,
(LPARAM) (LPVOID) &cfDefault);
goto done;
}
else
{
predoc->szFile[0] = TEXT('\0');
lstrcpy(predoc->szTitle, szUntitled);
}
GetClientRect(predoc->hwndParent, &rc);
InflateRect(&rc, dxRESize, dyRESize);

// Create and display the format bar
if(predoc->hwndFormatBar = HwndCreateFormatBar(predoc->hwndParent,
FBR_FormatBar, NULL))
{
RECT rcFormatBar;

ShowWindow(predoc->hwndFormatBar, SW_SHOW);
GetClientRect(predoc->hwndFormatBar, &rcFormatBar);
rc.top += rcFormatBar.bottom;
}

#ifdef SUNKENRICHEDIT
dwStyle =
ES_AUTOHSCROLL |
ES_AUTOVSCROLL |
//ES_DISABLENOSCROLL |
ES_MULTILINE |
//ES_NOHIDESEL |
ES_SAVESEL |
ES_SELECTIONBAR |
ES_SUNKEN |
//WS_BORDER |
WS_CHILD |
WS_CLIPCHILDREN |
WS_HSCROLL |
WS_VISIBLE |
WS_VSCROLL |
0;// zero gets or'd with above
predoc->hwndRE = CreateWindow(szClassRE, TEXT(""),
dwStyle,
rc.left, rc.top,
rc.right - rc.left,rc.bottom - rc.top, predoc->hwndParent,
NULL, HinstFromHwnd(predoc->hwndParent), NULL);

#else// SUNKENRICHEDIT
ES_AUTOHSCROLL |
ES_AUTOVSCROLL |
//ES_DISABLENOSCROLL |
ES_MULTILINE |
//ES_NOHIDESEL |
ES_SAVESEL |
ES_SELECTIONBAR |
//ES_SUNKEN |
//WS_BORDER |
WS_CHILD |
WS_CLIPCHILDREN |
WS_HSCROLL |
WS_VISIBLE |
WS_VSCROLL |
0;// zero gets or'd with above
predoc->hwndRE = CreateWindow(szClassRE, TEXT(""),
dwStyle,
rc.left, rc.top,
rc.right - rc.left,rc.bottom - rc.top, predoc->hwndParent,
NULL, HinstFromHwnd(predoc->hwndParent), NULL);
#endif// SUNKENRICHEDIT, else
if(!predoc->hwndRE)
{
MessageBoxA(hwndMain, "Unable to create a new document", NULL, MB_ICONSTOP | MB_OK);
return -1;
}
if(cchTextMost)
SendMessage(predoc->hwndRE, EM_EXLIMITTEXT, 0, cchTextMost);
#ifndef NO_OLE
DragAcceptFiles(predoc->hwndRE, TRUE);
#endif// !NO_OLE

// request EN_SELCHANGE, EN_CHANGE, EN_PROTECTED, and EN_DROPFILES
SendMessage(predoc->hwndRE, EM_SETEVENTMASK, 0,
ENM_SELCHANGE | ENM_CHANGE | ENM_PROTECTED | EN_DROPFILES |
ENM_CORRECTTEXT);

SetFocus(predoc->hwndRE);

predoc->scf = SCF_SELECTION;
if(GetMenu(hwndMain) && QueryCheck(predoc, IDM_APPLYTOWORD))
predoc->scf |= SCF_WORD;

#ifndef NO_OLE
if(!SendMessage(predoc->hwndRE, EM_GETOLEINTERFACE, 0, (LPARAM) &predoc->preole))
{
MessageBoxA(hwndMain, "No OLE interface!", NULL, MB_OK);
DestroyWindow(predoc->hwndRE);
predoc->hwndRE = 0;
return -1;
}

if(!(predoc->pstg = OleStdCreateRootStorage(NULL, STGM_SHARE_EXCLUSIVE)))
{
MessageBoxA(hwndMain, "No Storage!", NULL, MB_OK);
DestroyWindow(predoc->hwndRE);
predoc->hwndRE = 0;
return -1;
}

if(!(predoc->pitpcall = ITPCALL_New(predoc)))
{
MessageBoxA(hwndMain, "No callback object!", NULL, MB_OK);
DestroyWindow(predoc->hwndRE);
predoc->hwndRE = 0;
return -1;
}
SendMessage(predoc->hwndRE, EM_SETOLECALLBACK, 0, (LPARAM) predoc->pitpcall);
#endif// !NO_OLE

done:
if(fUpdateUI)
{
TCHAR szT[64];

wsprintf(szT, szFmtTitle, predoc->szTitle);
SetWindowText(predoc->hwndParent, szT);
SetMenu(hwndMain, hmenuFull);
DrawMenuBar(hwndMain);
}

predoc->fReadOnly = fFalse;
EnableWindow(predoc->hwndFormatBar, TRUE);

// illegal values to force SetWordWrap() to do something
predoc->fWrap = fFalse;
predoc->fWysiwyg = fTrue;
SetWordWrap(predoc, fWysiwygDefault, fWrapDefault);
// if we don't have a default font, use the windows variable width font
// otherwise, use the default font
if(cfDefault.dwMask == 0)
{
SendMessage(predoc->hwndRE, WM_SETFONT,
(WPARAM) GetStockObject(ANSI_VAR_FONT), 0);
SendMessage(predoc->hwndRE, EM_GETCHARFORMAT, FALSE,
(LPARAM) (LPVOID) &cfDefault);
}
else
{
SendMessage(predoc->hwndRE, EM_SETCHARFORMAT, 0,
(LPARAM) (LPVOID) &cfDefault);
}
SendMessage(predoc->hwndRE, EM_SETMODIFY, (WPARAM) fFalse, 0);
SendMessage(predoc->hwndRE, EM_SETREADONLY, (WPARAM) fFalse, 0);
UpdateFormatBar(predoc);

return 0;
}

LOCAL LONG CheckSave(REDOC *predoc)
{
int iMbid;
TCHAR szT[128];

#ifdef NO_SAVE_PROMPT
return 0;
#endif// NO_SAVE_PROMPT

if(!SendMessage(predoc->hwndRE, EM_GETMODIFY, 0, 0))
return 0;

wsprintf(szT, TEXT("Save changes to %s?"), predoc->szTitle);
iMbid = MessageBox(hwndMain, szT, szAppName, MB_APPLMODAL |
MB_ICONQUESTION | MB_YESNOCANCEL);
switch(iMbid)
{
case IDYES:
if(SaveREDoc(predoc) < 0)
return -1;
break;

case IDCANCEL:
return -1;
}
return 0;
}

// returns 0 if no changes have been made
// returns > 0 if revert should continue
// returns < 0 if revert should be aborted
LOCAL INT CheckRevert(REDOC *predoc)
{
int iMbid;
TCHAR szT[128];

if(!SendMessage(predoc->hwndRE, EM_GETMODIFY, 0, 0))
return 0;

#ifdef NO_SAVE_PROMPT
return 1;
#endif// NO_SAVE_PROMPT

wsprintf(szT, TEXT("Revert %s to last saved changes?"), predoc->szTitle);
iMbid = MessageBox(hwndMain, szT, szAppName, MB_APPLMODAL |
MB_ICONQUESTION | MB_YESNO);

return iMbid == IDYES ? 1 : -1;
}

LOCAL LRESULT CloseREDoc(REDOC *predoc, BOOL fPrompt, BOOL fUpdateUI)
{
if(fPrompt && CheckSave(predoc) < 0)
return -1;

if(predoc->hdcTarget)
{
DeleteDC(predoc->hdcTarget);
predoc->hdcTarget = 0;
}
#ifndef NO_OLE
// Deactivate any existing in place object
//$ FUTURE: This might be some generic I'm gonna close you call
predoc->preole->lpVtbl->InPlaceDeactivate(predoc->preole);
ITPCALL_Release((LPUNKNOWN) predoc->pitpcall);
predoc->pitpcall = NULL;
predoc->pstg->lpVtbl->Release(predoc->pstg);
predoc->pstg = NULL;
predoc->preole->lpVtbl->Release(predoc->preole);
predoc->preole = NULL;
predoc->cItem = 0;
#endif// !NO_OLE
DestroyWindow(predoc->hwndRE);
DestroyWindow(predoc->hwndFormatBar);

predoc->hwndRE = 0;
predoc->szFile[0] = TEXT('\0');
lstrcpy(predoc->szTitle, szUntitled);
predoc->dwFormat = SF_TEXT;
predoc->fReadOnly = fFalse;
EnableWindow(predoc->hwndFormatBar, TRUE);
#ifdef DEBUG
CheckMenuItem(hmenuFull, IDM_IGNORELEFTCLICK, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hmenuFull, IDM_EATES, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hmenuFull, IDM_IGNOREDROPS, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hmenuFull, IDM_BOTTOMLESS, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hmenuFull, IDM_TEXTONLY, MF_BYCOMMAND | MF_UNCHECKED);

if(hwndGT)
{
DestroyWindow(hwndGT);
hwndGT = 0;
}
#endif// DEBUG

if(fUpdateUI)
{
SetWindowText(predoc->hwndParent, szClosedName);
SetMenu(hwndMain, hmenuLoaded);
DrawMenuBar(hwndMain);
}

return 1;
}

DWORD CALLBACK MyRead(DWORD dwCookie, LPBYTE pbBuffer, LONG cb, LONG *pcb)
{
HFILEhf = (HFILE) dwCookie;

if(hf == HFILE_ERROR)
return (DWORD) E_FAIL;
*pcb = _lread(hf, pbBuffer, cb);
return (DWORD) (*pcb >= 0 ? NOERROR : (*pcb = 0, E_FAIL));
}

// This function gives the Mac version the ability to open and print
// files via Apple Events.
LRESULT DoOpen(predoc, szT, szTitle, fInsert)
REDOC *predoc;
LPSTR szT, szTitle;
BOOL fInsert;
{
DWORD dwError;

if((dwError = ReadREDoc(predoc, szT, szTitle, 0, fInsert)))
goto err;
wsprintf(szT, szFmtTitle, szTitle);
SetWindowText(predoc->hwndParent, szT);

EnableWindow(predoc->hwndFormatBar, !predoc->fReadOnly);
SendMessage(predoc->hwndRE, EM_SETREADONLY, (WPARAM) predoc->fReadOnly, 0);
SetMenu(hwndMain, hmenuFull);

SetupWordWrapMenu(predoc);
DrawMenuBar(hwndMain);
return 0;

err:
wsprintf(szT, TEXT("Error opening document. [%ld]"), dwError);
MessageBox(hwndMain, szT, NULL, MB_OK);
return -1;
}

LOCAL LRESULT OpenREDoc(REDOC *predoc, BOOL fInsert)
{
DWORD dwError;
OPENFILENAME ofn;
TCHAR szTitle[64];
TCHAR szT[256];

if(predoc->hwndRE && !fInsert && CheckSave(predoc) < 0)
return -1;

if(fInsert && predoc->fReadOnly)
return -1;

ofn.lStructSize= sizeof(ofn);
ofn.hInstance= 0;
ofn.lpstrFilter= szFilterLoad;
ofn.lpstrCustomFilter= NULL;
ofn.nMaxCustFilter= 0;
ofn.nFilterIndex= 0;
ofn.lpstrFileTitle= szTitle;
ofn.nMaxFileTitle= sizeof(szTitle);
ofn.lpstrInitialDir= NULL;
ofn.lpstrTitle= fInsert ? "Insert from File" : NULL;
ofn.nFileOffset= 0;
ofn.nFileExtension= 0;
ofn.lpstrDefExt= NULL;
ofn.lCustData= 0L;
ofn.lpfnHook= NULL;
ofn.lpTemplateName= NULL;
ofn.hwndOwner= hwndMain;
ofn.lpstrFile= szT;
ofn.nMaxFile= sizeof(szT);
ofn.Flags= OFN_FILEMUSTEXIST;

szTitle[0] = TEXT('\0');
szT[0] = TEXT('\0');

// Query user for filename for input
    if(!GetOpenFileName(&ofn))
{
if((dwError = CommDlgExtendedError()) != 0)
{
wsprintf(szT, TEXT("Error opening document. [%ld]"), dwError);
MessageBox(hwndMain, szT, NULL, MB_OK);
return -1;
}
return 0;
}
predoc->fReadOnly = (ofn.Flags & OFN_READONLY) ? fTrue : fFalse;
EnableWindow(predoc->hwndFormatBar, !predoc->fReadOnly);
return(DoOpen(predoc, szT, szTitle, fInsert));
}

LOCAL LRESULT RevertREDoc(REDOC *predoc)
{
DWORD dwError;
TCHAR szT[256];

if(CheckRevert(predoc) <= 0)
return 0;

lstrcpy(szT, predoc->szFile);
dwError = ReadREDoc(predoc, szT, NULL, predoc->dwFormat, FALSE);
if(!dwError)
return 0;

wsprintf(szT, TEXT("Error reading document. [%ld]"), dwError);
MessageBox(hwndMain, szT, NULL, MB_OK);

return -1;
}

LOCAL DWORD ReadREDoc(REDOC *predoc, LPCSTR szFile, LPCSTR szTitle,
DWORD dwFormat, BOOL fInsert)
{
LONG cch;
HCURSOR hcur;
EDITSTREAM es;
TCHAR szType[cchRTFSig + 1];

 hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));

es.dwCookie = (DWORD) _lopen(szFile, OF_READ);
if(es.dwCookie == (DWORD) HFILE_ERROR)
{
return GetLastError();
}

if(!fInsert)
{
// save read-only flag across NewREDoc()
const BOOL fReadOnlySave = predoc->fReadOnly;

if(NewREDoc(predoc, fFalse, fFalse) < 0)
{
_lclose((HFILE) es.dwCookie);
return (DWORD) -1;
}
predoc->fReadOnly = fReadOnlySave;
}
if(dwFormat == 0)// unknown format, figure out what it is
{
UINT cb = cchRTFSig * sizeof(TCHAR);

cb = _lread((HFILE) es.dwCookie, szType, cb);
szType[cb / sizeof(TCHAR)] = TEXT('\0');
if(cb == cchRTFSig * sizeof(TCHAR))
dwFormat = lstrcmpi(szRTFSig, szType) ? SF_TEXT : SF_RTF;
else
dwFormat = SF_TEXT;// not big enough to be RTF, assume text

// move back to the beginning of the file
_llseek((HFILE) es.dwCookie, 0, 0);
}

SendMessage(predoc->hwndRE, WM_SETREDRAW, (WPARAM) fFalse, 0);

es.dwError = 0;
es.pfnCallback = MyRead;

cch = SendMessage(predoc->hwndRE, EM_STREAMIN,
(WPARAM) (fInsert ? dwFormat | SFF_SELECTION : dwFormat),
(LPARAM) &es);
_lclose((HFILE) es.dwCookie);
#ifdef DEBUG
if(predoc->hwndRE)
SendMessage(predoc->hwndRE, EM_REQUESTRESIZE, 0, 0);
#endif// DEBUG

SendMessage(predoc->hwndRE, EM_SETMODIFY, (WPARAM) fFalse, 0);

predoc->dwFormat = dwFormat;
lstrcpy(predoc->szFile, szFile);
if(szTitle)
lstrcpy(predoc->szTitle, szTitle);
else
GetFileTitle(szFile, predoc->szTitle, sizeof(predoc->szTitle));

SendMessage(predoc->hwndRE, WM_SETREDRAW, (WPARAM) fTrue, 0);
InvalidateRect(predoc->hwndRE, NULL, fTrue);
UpdateWindow(predoc->hwndRE);

SetCursor(hcur);

UpdateFormatBar(predoc);
return 0;
}

DWORD CALLBACK MyWrite(DWORD dwCookie, LPBYTE pbBuffer, LONG cb, LONG *pcb)
{
HFILEhf = (HFILE) dwCookie;

if(hf == HFILE_ERROR)
return (DWORD) E_FAIL;

*pcb = _lwrite(hf, pbBuffer, cb);
return (DWORD) (*pcb == cb ? NOERROR : E_FAIL);
}

LOCAL LRESULT SaveREDoc(REDOC *predoc)
{
HCURSOR hcur;
LONG cch;
DWORD dwError;
EDITSTREAM es;

if(!predoc->szFile[0])
return SaveREDocAs(predoc, FALSE);

hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));

es.dwCookie = _lcreat(predoc->szFile, 0);
if(es.dwCookie == (DWORD) HFILE_ERROR)
{
dwError = GetLastError();
goto err;
}
es.dwError = 0;
es.pfnCallback = MyWrite;
cch = SendMessage(predoc->hwndRE, EM_STREAMOUT,(WPARAM) predoc->dwFormat,
(LPARAM) &es);
_lclose((HFILE) es.dwCookie);

SendMessage(predoc->hwndRE, EM_SETMODIFY, (WPARAM) fFalse, 0);
SendMessage(predoc->hwndRE, EM_SETREADONLY, (WPARAM) fFalse, 0);
predoc->fReadOnly = fFalse;
EnableWindow(predoc->hwndFormatBar, TRUE);

SetCursor(hcur);

return 0;

err:
{
TCHAR szT[64];

wsprintf(szT, TEXT("Error saving document. [%ld]"), dwError);
MessageBox(hwndMain, szT, NULL, MB_OK);
}

return -1;
}

LOCAL LRESULT SaveREDocAs(REDOC *predoc, BOOL fSelect)
{
HCURSOR hcur;
LONG cch;
DWORD dwError;
EDITSTREAM es;
OPENFILENAME ofn;
TCHAR szTitle[64];
TCHAR szT[256];

ofn.lStructSize= sizeof(ofn);
ofn.hInstance= 0;
ofn.lpstrFilter= szFilterSave;
ofn.lpstrCustomFilter= NULL;
ofn.nMaxCustFilter= 0;
ofn.nFilterIndex= 0;
ofn.lpstrFileTitle= szTitle;
ofn.nMaxFileTitle= sizeof(szTitle);
ofn.lpstrInitialDir= NULL;
ofn.lpstrTitle= fSelect ? "Save Selection As": NULL;
ofn.nFileOffset= 0;
ofn.nFileExtension= 0;
 ofn.lpstrDefExt= NULL;
ofn.lCustData= 0L;
ofn.lpfnHook= NULL;
ofn.lpTemplateName= NULL;
ofn.hwndOwner= hwndMain;
ofn.lpstrFile= szT;

ofn.nMaxFile= sizeof(szT);
ofn.Flags= OFN_CREATEPROMPT | OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT;

szT[0] = TEXT('\0');
szTitle[0] = TEXT('\0');
// Query user for filename for input
    if(!GetSaveFileName(&ofn))
{
if((dwError = CommDlgExtendedError()) != 0)
goto err;
return -1;
}

hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));

es.dwCookie = _lcreat(szT, 0);
if(es.dwCookie == (DWORD) HFILE_ERROR)
{
dwError = GetLastError();
goto err;
}
es.dwError = 0;
es.pfnCallback = MyWrite;
cch = SendMessage(predoc->hwndRE, EM_STREAMOUT,
(WPARAM) (fSelect ? ofn.nFilterIndex | SFF_SELECTION
  : ofn.nFilterIndex),
(LPARAM) &es);
_lclose((HFILE) es.dwCookie);

// Don't mark the doc as clean, nor remember format if only selection
if(fSelect)
goto Quit;

SendMessage(predoc->hwndRE, EM_SETMODIFY, (WPARAM) fFalse, 0);

predoc->dwFormat = (DWORD) ofn.nFilterIndex;
predoc->fReadOnly = fFalse;
EnableWindow(predoc->hwndFormatBar, TRUE);

lstrcpy(predoc->szFile, szT);
lstrcpy(predoc->szTitle, szTitle);

wsprintf(szT, szFmtTitle, szTitle);
SetWindowText(predoc->hwndParent, szT);

SendMessage(predoc->hwndRE, EM_SETMODIFY, (WPARAM) fFalse, 0);
SendMessage(predoc->hwndRE, EM_SETREADONLY, (WPARAM) fFalse, 0);

Quit:
SetCursor(hcur);

return 0;

err:
wsprintf(szT, TEXT("Error saving document. [%ld]"), dwError);
MessageBox(hwndMain, szT, NULL, MB_OK);

return -1;
}

LOCAL LRESULT InsertObject(REDOC *predoc)
{
#ifdef NO_OLE
MessageBox(NULL, "Not yet implemented", NULL, MB_OK);
return 0;
#else// NO_OLE
const LPRICHEDITOLE preole = predoc->preole;
LPOLECLIENTSITE polesite = NULL;
LPSTORAGE pstgItem = NULL;
OLEUIINSERTOBJECT ouio = { 0 };
REOBJECT reobj = { 0 };
LPOLEOBJECT poleobj = NULL;
CHAR szFileA[OLEUI_CCHPATHMAX];
WCHAR szItemW[OLEUI_CCHPATHMAX];
DWORD dwRet;
RECT rect;

if(!preole)
return 0;

if(preole->lpVtbl->SetHostNames(preole, "REITP", predoc->szTitle))
{
MessageBoxA(hwndMain, "Can't set host name", NULL, MB_OK);
goto error;
}

if(preole->lpVtbl->GetClientSite(preole, (LPOLECLIENTSITE FAR *) &polesite))
{
MessageBoxA(hwndMain, "No client site!", NULL, MB_OK);
goto error;
}

wsprintfW(szItemW, L"REOBJ%ld", ++predoc->cItem);
if(!(pstgItem = OleStdCreateChildStorage(predoc->pstg, szItemW)))
{
MessageBoxW(hwndMain, szItemW, L"No item storage!", MB_OK);
goto error;
}

szFileA[0] = '\0';
ouio.cbStruct = sizeof(ouio);
ouio.dwFlags = IOF_SHOWHELP | IOF_CREATENEWOBJECT | IOF_CREATEFILEOBJECT |
   IOF_CREATELINKOBJECT | IOF_SELECTCREATENEW;
//   IOF_VERIFYSERVERSEXIST;
# ifdef MAC// Mac version doesn't have the same members
ouio.pszFile = szFileA;
# else// MAC
ouio.hWndOwner = hwndMain;
ouio.lpszFile = szFileA;
# endif// MAC, else
ouio.lpszCaption = "Insert Object (REITP)";
ouio.cchFile = OLEUI_CCHPATHMAX;
ouio.iid = IID_IOleObject;
ouio.oleRender = OLERENDER_DRAW;
ouio.lpIOleClientSite = polesite;
ouio.lpIStorage = pstgItem;
ouio.ppvObj = (LPVOID FAR *) &poleobj;
ouio.clsid = CLSID_NULL;

if((dwRet = OleUIInsertObject(&ouio)) != OLEUI_SUCCESS)
{
if(dwRet == OLEUI_CANCEL)
goto error;
if(dwRet == OLEUI_IOERR_SCODEHASERROR)
wsprintfA(szFileA, "OleUIInsertObject scode is %lx", ouio.sc);
else
wsprintfA(szFileA, "OleUIInsertObject returned %ld", dwRet);
MessageBoxA(hwndMain, szFileA, NULL, MB_OK);
goto error;
}

AssertSz(ouio.sc == S_OK, "Object wasn't created properly");

reobj.cbStruct = sizeof(REOBJECT);
reobj.clsid = ouio.clsid;
reobj.cp = REO_CP_SELECTION;
reobj.poleobj = poleobj;
reobj.pstg = pstgItem;
reobj.polesite = polesite;
reobj.dvaspect = DVASPECT_CONTENT;
reobj.dwFlags = REO_RESIZABLE;
reobj.dwUser = 0;

if(ouio.dwFlags & IOF_SELECTCREATENEW)
reobj.dwFlags |= REO_BLANK;

//$ Raid 101: RichEdit doesn't setup advises if reobj.clsid == CLSID_NULL
// Try our darnest to get a CLSID

if(IsEqualCLSID(&reobj.clsid, &CLSID_NULL) &&
HrGetClassFileA(szFileA, &reobj.clsid))
{
MessageBoxA(hwndMain, "No CLSID, but forging on", "Insert Object",
MB_OK);
}

// Do we want an iconized version ?
if(ouio.dwFlags & IOF_CHECKDISPLAYASICON)
{
BOOLfUpdate;// Can't pass in NULL instead of &this

// OLE call will update dvaspect on success
# ifdef MAC
// NOTE : There presently is no Mac equivalent of the <.hMetaPict>
// member of the tagOleUIInsertObject structure. This is certainly
// a problem. For now just pass in NULL since the Mac routines will
// not try to use this member.
DebugStr("\pBefore OleStdSwitchDisplayAspect() call");
if(OleStdSwitchDisplayAspect(poleobj, &reobj.dvaspect,
  DVASPECT_ICON, (Handle) NULL,
  TRUE, FALSE, NULL, &fUpdate))
# else// MAC
if(OleStdSwitchDisplayAspect(poleobj, &reobj.dvaspect,
  DVASPECT_ICON, ouio.hMetaPict,
  TRUE, FALSE, NULL, &fUpdate))
# endif// MAC, else
{
// How much do we care about reporting errors?  Alot!! {Mac will have more}
MessageBoxA(hwndMain, "Object couldn't be displayed as an icon.",
"Insert Object", MB_OK);
}
AssertSz(!fUpdate, "We gave it an hMetaPict, should not need updating");
}

// Put the thing in the edit control
if(preole->lpVtbl->InsertObject(preole, &reobj))
{
MessageBoxA(hwndMain, "Object couldn't be inserted",
"Insert object", MB_OK);
goto error;
}

// Do show verb only on new objects
if(ouio.dwFlags & IOF_SELECTCREATENEW)
{
rect.top = rect.left = 0;
rect.bottom = rect.right = 50;
dwRet = (ULONG) poleobj->lpVtbl->DoVerb(poleobj, OLEIVERB_SHOW, NULL,
polesite, 0, hwndMain,
(LPCRECT) &rect);
}

error:
# ifndef MAC
if(ouio.hMetaPict)
OleUIMetafilePictIconFree(ouio.hMetaPict);
# endif// !MAC
if(polesite)
polesite->lpVtbl->Release(polesite);
if(pstgItem)
pstgItem->lpVtbl->Release(pstgItem);
if(poleobj)
poleobj->lpVtbl->Release(poleobj);

return 0;
#endif// NO_OLE, else
}

#ifdef DEBUG

LOCAL LRESULT GetText(REDOC *predoc)
{
HWND hwndEdit;
LONG cch;
TCHAR *pch;

if(!hwndGT)
{
hwndGT = CreateDialog(HinstFromHwnd(hwndMain), TEXT("GETTEXT"),
hwndMain, (DLGPROC) GTDlgProc);
if(!hwndGT)
{
MessageBoxA(hwndMain, "Unable to create dialog", NULL, MB_ICONSTOP | MB_OK);
return 0;
}
}
hwndEdit = GetDlgItem(hwndGT, GTCTRL);
cch = SendMessage(predoc->hwndRE, WM_GETTEXTLENGTH, 0, 0);
# ifdef WIN16
if(cch > 65000)
{
MessageBoxA(hwndMain, "Text too big, truncating", NULL,
MB_ICONSTOP | MB_OK);
cch = 65000;
}
# endif// WIN16
pch = GlobalAllocPtr(GHND, cch + 1);
if(!pch)
{
MessageBoxA(hwndMain, "Not enough memory to hold text", NULL,
MB_ICONSTOP | MB_OK);
return 0;
}
GetWindowText(predoc->hwndRE, pch, (int) cch + 1);
*pch = toupper(*pch);
SetWindowText(hwndEdit, pch);
GlobalFreePtr(pch);

return 0;
}

LRESULT CALLBACK GTDlgProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_INITDIALOG:
return 1;

case WM_SIZE:
MoveWindow(GetDlgItem(hdlg, GTCTRL), 5, 5, LOWORD(lparam)-10,
   HIWORD(lparam)-10, TRUE);
return 0;

case WM_CLOSE:
EndDialog(hdlg, 0);
hwndGT = NULL;
return 1;
}

return 0;
}

LOCAL VOID ClearDebugScreen(void)
{
HFILE hf;

hf = _lopen("COM1", OF_WRITE);
if(hf)
{
_lwrite(hf, "\x1B[H\x1B[J", 6);
_lclose(hf);
}
}

#endif// DEBUG

LOCAL VOID SelectCharFormat(REDOC *predoc)
{
LOGFONT lf;
CHOOSEFONT csf = {0};
CHARFORMAT cf;
LONG yPerInch;
HDC hdc;

cf.cbSize = sizeof(CHARFORMAT);

hdc = GetDC(hwndMain);
yPerInch = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(hwndMain, hdc);

(void) SendMessage(predoc->hwndRE, EM_GETCHARFORMAT, (WPARAM) fTrue,
(LPARAM) &cf);

csf.lStructSize = sizeof(csf);
csf.hwndOwner = hwndMain;
csf.hDC = 0;
csf.lpLogFont = &lf;
csf.Flags = CF_EFFECTS | CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT |
CF_LIMITSIZE;
csf.nSizeMin = 1;
csf.nSizeMax = yHeightCharPtsMost;
csf.rgbColors = cf.crTextColor;
csf.lpszStyle = NULL;
csf.nFontType = REGULAR_FONTTYPE | SCREEN_FONTTYPE;
lf.lfHeight = -(INT) ((cf.yHeight * yPerInch) / 1440);
lf.lfWidth = 0;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = (cf.dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL;
lf.lfItalic = (cf.dwEffects & CFE_ITALIC) ? fTrue : fFalse;
lf.lfUnderline = (cf.dwEffects & CFE_UNDERLINE) ? fTrue : fFalse;
lf.lfStrikeOut = (cf.dwEffects & CFE_STRIKEOUT) ? fTrue : fFalse;
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfQuality = DRAFT_QUALITY;
lf.lfCharSet = cf.bCharSet;
lf.lfPitchAndFamily = cf.bPitchAndFamily;
_tcscpy(lf.lfFaceName, cf.szFaceName);

if(!ChooseFont(&csf))
return;

cf.cbSize = sizeof(CHARFORMAT);

// don't change read-only bit
cf.dwMask = CFM_SIZE | CFM_EFFECTS | CFM_COLOR | CFM_FACE | CFM_CHARSET;
cf.yHeight = (LONG) csf.iPointSize * 2;
cf.dwEffects = CFM_EFFECTS;
if(lf.lfWeight < FW_BOLD)
cf.dwEffects &= ~CFE_BOLD;
if(!lf.lfItalic)
cf.dwEffects &= ~CFE_ITALIC;
if(!lf.lfUnderline)
cf.dwEffects &= ~CFE_UNDERLINE;
if(!lf.lfStrikeOut)
cf.dwEffects &= ~CFE_STRIKEOUT;
cf.crTextColor = csf.rgbColors;
cf.bCharSet = lf.lfCharSet;
cf.bPitchAndFamily = lf.lfPitchAndFamily;
_tcscpy(cf.szFaceName, lf.lfFaceName);

if(!SendMessage(predoc->hwndRE, EM_SETCHARFORMAT, predoc->scf,
(LPARAM) &cf))
{
MessageBoxA(hwndMain, "Error setting character format", NULL,
MB_ICONSTOP | MB_OK);
}
}

LOCAL VOID SaveWindowPos(HWND hwnd)
{
WINDOWPLACEMENTwndpl;
HKEY hkey;

    wndpl.length = sizeof(wndpl);
    GetWindowPlacement(hwnd, &wndpl);
    if(RegCreateKeyEx(HKEY_CURRENT_USER, szRegKey, 0, szAppName, 0,
KEY_ALL_ACCESS, NULL, &hkey, NULL) == ERROR_SUCCESS)
    {
        RegSetValueEx(hkey, szRegValue, 0, REG_BINARY, (LPBYTE) &wndpl,
sizeof(wndpl));
        RegCloseKey(hkey);
    }
}

LOCAL BOOL FRestoreWindowPos(WINDOWPLACEMENT *pwndpl)
{
BOOL fReturn = fFalse;
DWORD cb;
HKEY hkey = 0;

    if(RegOpenKeyEx(HKEY_CURRENT_USER, szRegKey, 0, KEY_READ, &hkey)
== ERROR_SUCCESS)
{
cb = sizeof(*pwndpl);
if(RegQueryValueEx(hkey, szRegValue, 0, 0, (LPBYTE) pwndpl,
&cb) == ERROR_SUCCESS)
{
fReturn = (cb == sizeof(*pwndpl));
}
RegCloseKey(hkey);
}

return fReturn;
}

LOCAL VOID SetAlignment(REDOC *predoc, WORD wAlignment)
{
PARAFORMAT pf;

pf.cbSize = sizeof(PARAFORMAT);

SendMessage(predoc->hwndRE, EM_GETPARAFORMAT, 0, (LPARAM) &pf);

if(!(pf.dwMask & PFM_ALIGNMENT) || pf.wAlignment != wAlignment)
{
pf.dwMask = PFM_ALIGNMENT;// only change the alignment
pf.wAlignment = wAlignment;
SendMessage(predoc->hwndRE, EM_SETPARAFORMAT, 0, (LPARAM) &pf);
}
}

LOCAL VOID IndentFirst(REDOC *predoc, BOOL fIndent)
{
PARAFORMAT pf;

pf.cbSize = sizeof(PARAFORMAT);

SendMessage(predoc->hwndRE, EM_GETPARAFORMAT, 0, (LPARAM) &pf);

pf.dwMask = PFM_OFFSET | PFM_OFFSETINDENT;
if(fIndent)
{
if(pf.dxOffset < 0)
{
pf.dxStartIndent = pf.dxOffset;
pf.dxOffset = 0;
}
else
{
pf.dxOffset = pf.dxOffset ? -pf.dxOffset : -lDefaultTab;
pf.dxStartIndent = -pf.dxOffset;
}
}
else
{
if(pf.dxOffset < 0)
{
pf.dxStartIndent = pf.dxOffset;
pf.dxOffset = -pf.dxOffset;
}
else
{
pf.dxStartIndent = 0;
pf.dxOffset = pf.dxOffset ? 0 : lDefaultTab;
}
}

SendMessage(predoc->hwndRE, EM_SETPARAFORMAT, 0, (LPARAM) &pf);
}

LOCAL VOID ProtectSelection(REDOC *predoc)
{
CHARFORMAT cf;

cf.cbSize = sizeof(CHARFORMAT);

(void) SendMessage(predoc->hwndRE, EM_GETCHARFORMAT, fTrue, (LPARAM) &cf);
cf.dwMask = CFM_PROTECTED;
cf.dwEffects = cf.dwEffects ^ CFE_PROTECTED;
(void) SendMessage(predoc->hwndRE, EM_SETCHARFORMAT, predoc->scf,
(LPARAM) &cf);
}

LOCAL VOID SetWordWrap(REDOC *predoc, BOOL fWysiwyg, BOOL fWrap)
{
HCURSOR hcur;
HDC hdcDel = predoc->hdcTarget;
LONG xWidth;

if(!fWysiwyg == !predoc->fWysiwyg && !fWrap == !predoc->fWrap)
return;

hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));

if(fWysiwyg)
{
POINT pt;
LPDEVMODE pDevMode;
LPDEVNAMES pDevNames;

if(predoc->pd.lStructSize != sizeof(PRINTDLG))
goto done;// error occured getting default printer

pDevMode = (LPDEVMODE) GlobalLock(predoc->pd.hDevMode);
pDevNames = (LPDEVNAMES) GlobalLock(predoc->pd.hDevNames);
predoc->hdcTarget = CreateIC((LPTSTR) pDevNames +
pDevNames->wDriverOffset,
(LPTSTR) pDevNames + pDevNames->wDeviceOffset,
(LPTSTR) pDevNames + pDevNames->wOutputOffset,
pDevMode);
GlobalUnlock(predoc->pd.hDevNames);
GlobalUnlock(predoc->pd.hDevMode);
if(!predoc->hdcTarget)
goto done;
SetMapMode(predoc->hdcTarget, MM_TEXT);
if(Escape(predoc->hdcTarget, GETPHYSPAGESIZE, 0, NULL, &pt) > 0)
{
const LONG xPerInch = GetDeviceCaps(predoc->hdcTarget, LOGPIXELSX);

xWidth = (pt.x * 1440l) / xPerInch;
// leave 1.25" (1800 twips) margins if that will leave >= 1"
if(xWidth >= 1800 + 1440 + 1800)
xWidth -= 1800 + 1800;
}
else
{
const LONG xPerInch = GetDeviceCaps(predoc->hdcTarget, LOGPIXELSX);

xWidth = (GetDeviceCaps(predoc->hdcTarget, HORZRES) * 1440l) /
xPerInch;
}
}
else
{
predoc->hdcTarget = 0;
xWidth = (LONG) !fWrap;
}
predoc->fWysiwyg = fWysiwyg;
predoc->fWrap = fWrap;

SetupWordWrapMenu(predoc);

SendMessage(predoc->hwndRE, EM_SETTARGETDEVICE,
(WPARAM) predoc->hdcTarget, (LPARAM) xWidth);
if(hdcDel)
DeleteDC(hdcDel);

done:
SetCursor(hcur);
}

LOCAL VOID SetupWordWrapMenu(REDOC *predoc)
{
HMENU hmenu = GetMenu(hwndMain);

CheckMenuItem(hmenu, IDM_NOWRAP,
(!predoc->fWysiwyg && !predoc->fWrap) ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_WRAP,
(!predoc->fWysiwyg && predoc->fWrap) ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_WYSIWYG,
predoc->fWysiwyg ? MF_CHECKED :MF_UNCHECKED);
}

LOCAL VOID SetOffset(REDOC *predoc, BOOL fSuperscript)
{
CHARFORMAT cf;

cf.cbSize = sizeof(CHARFORMAT);

SendMessage(predoc->hwndRE, EM_GETCHARFORMAT, fTrue, (LPARAM) &cf);

cf.dwMask = CFM_OFFSET;// only change the yOffset
if(fSuperscript)
{
if(cf.yOffset > 0)
cf.yOffset = 0;
else
cf.yOffset = 50;
}
else
{
if(cf.yOffset < 0)
cf.yOffset = 0;
else
cf.yOffset = -50;
}

SendMessage(predoc->hwndRE, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
}

VOID PrintREDoc(REDOC *predoc)
{
LONG cchText;
HCURSOR hcur;
POINT pt;
DOCINFO di;
FORMATRANGE fr;
RECT rc;

cchText = SendMessage(predoc->hwndRE, WM_GETTEXTLENGTH, 0, 0);

predoc->pd.nFromPage = 0;
predoc->pd.nToPage = 0;
predoc->pd.nMinPage = 0;
predoc->pd.nMaxPage = 0;
predoc->pd.nCopies = 1;
predoc->pd.Flags =
PD_NOPAGENUMS | PD_NOSELECTION |
PD_USEDEVMODECOPIES | PD_COLLATE | PD_RETURNDC;

if(!PrintDlg(&predoc->pd))
return;

hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));

di.cbSize = sizeof(DOCINFO);
di.lpszDocName = predoc->szTitle;
di.lpszOutput = NULL;

fr.hdc = predoc->pd.hDC;
fr.hdcTarget = 0;

if(Escape(fr.hdc, GETPHYSPAGESIZE, 0, NULL, &pt) > 0)
{
const LONG xPerInch = GetDeviceCaps(fr.hdc, LOGPIXELSX);
const LONG yPerInch = GetDeviceCaps(fr.hdc, LOGPIXELSY);

rc.left = rc.top = 0;
fr.rcPage.left = fr.rcPage.top = 0;
rc.right = (INT) ((pt.x * 1440l) / xPerInch);
fr.rcPage.right = rc.right;
// leave 1.25" (1800 twips) margins if that will leave >= 1"
if(rc.right >= 1800 + 1440 + 1800)
rc.right -= (rc.left = 1800);
rc.bottom = (INT) ((pt.y * 1440l) / yPerInch);
fr.rcPage.bottom = rc.bottom;
// leave 1" (1440 twips) margins if that will leave >= 1"
if(rc.bottom >= 1440 + 1440 + 1440)
rc.bottom -= (rc.top = 1440);
}
else
{
const LONG xPerInch = GetDeviceCaps(fr.hdc, LOGPIXELSX);
const LONG yPerInch = GetDeviceCaps(fr.hdc, LOGPIXELSY);

rc.left = rc.top = 0;
rc.right = (INT) ((GetDeviceCaps(fr.hdc, HORZRES) * 1440l) /
xPerInch);
rc.bottom = (INT) ((GetDeviceCaps(fr.hdc, VERTRES) * 1440l) /
yPerInch);
fr.rcPage = rc;
}

if(StartDoc(fr.hdc, &di) <= 0)
goto err;

// tell RichEdit not to erase before rendering text
SetBkMode(fr.hdc, TRANSPARENT);

fr.chrg.cpMin = 0;
fr.chrg.cpMost = cchText;
do
{
if(StartPage(fr.hdc) <= 0)
goto abort;
fr.rc = rc;
fr.chrg.cpMin = SendMessage(predoc->hwndRE, EM_FORMATRANGE,
(WPARAM) fTrue, (LPARAM) &fr);
if(EndPage(fr.hdc) <= 0)
goto abort;
} while(fr.chrg.cpMin > 0 && fr.chrg.cpMin < fr.chrg.cpMost);

if(fr.chrg.cpMin >= 0 && EndDoc(fr.hdc) > 0)
{
fr.chrg.cpMin = fr.chrg.cpMost = cchText;
// free up RichEdit's format range info
SendMessage(predoc->hwndRE, EM_FORMATRANGE, 0, 0);
(void) SetCursor(hcur);
return;
}

abort:
(void) AbortDoc(fr.hdc);

err:
fr.chrg.cpMin = fr.chrg.cpMost = cchText;
// free up RichEdit's format range info
SendMessage(predoc->hwndRE, EM_FORMATRANGE, 0, 0);
(void) SetCursor(hcur);
MessageBoxA(hwndMain, "An error occurred while printing", NULL,
MB_ICONSTOP | MB_OK);
}

LRESULT CALLBACK PPDlgProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
{
PRINTPREVIEW * ppp = (PRINTPREVIEW *) GetWindowLong(hdlg, DWL_USER);

switch(msg)
{
case WM_INITDIALOG:
// hide the button we use to tell us where the top left is
ShowWindow(GetDlgItem(hdlg, PSB_TopLeft), SW_HIDE);

ppp = (PRINTPREVIEW *) GlobalAllocPtr(GHND, sizeof(PRINTPREVIEW));
SetWindowLong(hdlg, DWL_USER, (LONG) ppp);
if(!ppp)
{
MessageBoxA(hwndMain,
"Not enough memory for Print Preview window.",
NULL, MB_ICONSTOP | MB_OK);
EndDialog(hdlg, 0);
return 0;
}
ppp->hwndDlg = hdlg;
ppp->predoc = (REDOC *) lparam;
AssertSz(ppp->predoc, "print preview: no redoc");
ppp->cchText = -1;
ppp->ipage = -1;
PPChangePage(ppp, fFalse);
if(ppp->ipage < 0)
{
ppp->fDone = fTrue;// prevent any movement
// bail
PostMessage(hdlg, WM_CLOSE, 0, 0);
return 0;
}
return 1;

case WM_PAINT:
PPPaint(ppp);
break;

case WM_SIZE:
// cause complete redraw
InvalidateRect(hdlg, NULL, fTrue);
break;

#if defined(DEBUG) && defined(CLIPMETA)
case WM_LBUTTONDBLCLK:
if(ppp->hmf)
{
HMETAFILE hmf = 0;
HANDLE hMfp = 0;
METAFILEPICT *pmfp;

if(!OpenClipboard(hwndMain))
break;
EmptyClipboard();
hmf = CopyMetaFile(ppp->hmf, NULL);
if(!hmf)
{
MessageBeep(0);
break;
}
hMfp = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(METAFILEPICT));
if(!hMfp)
goto err;
pmfp = (METAFILEPICT *) GlobalLock(hMfp);
pmfp->mm = MM_ANISOTROPIC;
pmfp->xExt = (INT) ppp->dxPage;
pmfp->yExt = (INT) ppp->dyPage;
pmfp->hMF = hmf;
GlobalUnlock(hMfp);
SetClipboardData(CF_METAFILEPICT, hMfp);
CloseClipboard();
TraceTag(tagNull, "clipped");
break;
err:
if(hmf)
DeleteMetaFile(hmf);
if(hMfp)
GlobalFree(hMfp);
CloseClipboard();
}
break;
#endif// DEBUG && CLIPMETA

case WM_COMMAND:
switch(GET_WM_COMMAND_ID(wparam, lparam))
{
case PSB_NextPage:
PPChangePage(ppp, fFalse);
break;
case PSB_PrevPage:
PPChangePage(ppp, fTrue);
break;
case PSB_Close:
PostMessage(hdlg, WM_CLOSE, 0, 0);
break;
}
break;

case WM_CLOSE:
if(ppp)
{
// free up RichEdit's format range info
SendMessage(ppp->predoc->hwndRE, EM_FORMATRANGE, 0, 0);
#ifdef DEBUG
if(ppp->hmf)
DeleteMetaFile(ppp->hmf);
#endif// DEBUG
if(ppp->fr.hdc)
{
#ifndef NOBLIT
SelectObject(ppp->fr.hdc, ppp->hbmpOld);
#endif// !NOBLIT
DeleteDC(ppp->fr.hdc);
}
#ifndef NOBLIT
if(ppp->hbmp)
DeleteObject(ppp->hbmp);
#endif// !NOBLIT
if(ppp->fr.hdcTarget)
DeleteDC(ppp->fr.hdcTarget);
GlobalFreePtr(ppp);
}
SetWindowLong(hdlg, DWL_USER, 0);
EndDialog(hdlg, 0);
return 1;
}

return 0;
}

LOCAL BOOL FPPInit(PRINTPREVIEW *ppp)
{
LPDEVMODE pDevMode;
LPDEVNAMES pDevNames;
POINT pt;

#ifndef NOBLIT
ppp->hbmp = ppp->hbmpOld = 0;
#endif// !NOBLIT
ppp->fr.hdc = 0;
ppp->cchText = SendMessage(ppp->predoc->hwndRE, WM_GETTEXTLENGTH, 0, 0);

if(ppp->predoc->pd.lStructSize != sizeof(PRINTDLG))
{
MessageBoxA(hwndMain, "No printer selected.", NULL,
MB_ICONSTOP | MB_OK);
return fFalse;
}

ppp->cpage = 0;
ppp->ipage = -1;// first increment will display page 0
ppp->cpageMost = cpageChunk;
ppp->rgcpPages = (LONG *) GlobalAllocPtr(GHND, cpageChunk * sizeof(LONG));
if(!ppp->rgcpPages)
ppp->cpageMost = 0;

pDevMode = (LPDEVMODE) GlobalLock(ppp->predoc->pd.hDevMode);
pDevNames = (LPDEVNAMES) GlobalLock(ppp->predoc->pd.hDevNames);
ppp->fr.hdcTarget = CreateIC((LPTSTR) pDevNames + pDevNames->wDriverOffset,
(LPTSTR) pDevNames + pDevNames->wDeviceOffset,
(LPTSTR) pDevNames + pDevNames->wOutputOffset,
pDevMode);
GlobalUnlock(ppp->predoc->pd.hDevNames);
GlobalUnlock(ppp->predoc->pd.hDevMode);
if(!ppp->fr.hdcTarget)
goto err;

// does this matter?
SetMapMode(ppp->fr.hdcTarget, MM_TEXT);

if(Escape(ppp->fr.hdcTarget, GETPHYSPAGESIZE, 0, NULL, &pt) > 0)
{
const LONG xPerInch = GetDeviceCaps(ppp->fr.hdcTarget, LOGPIXELSX);
const LONG yPerInch = GetDeviceCaps(ppp->fr.hdcTarget, LOGPIXELSY);

ppp->rc.left = ppp->rc.top = 0;
ppp->dxPage = (pt.x * 1440l) / xPerInch;
ppp->rc.right = (INT) ppp->dxPage;
ppp->fr.rcPage.left = 0;
ppp->fr.rcPage.right = ppp->rc.right;
// leave 1.25" (1800 twips) margins if that will leave >= 1"
if(ppp->rc.right >= 1800 + 1440 + 1800)
ppp->rc.right -= (ppp->rc.left = 1800);
ppp->dyPage = (pt.y * 1440l) / yPerInch;
ppp->rc.bottom = (INT) ppp->dyPage;
ppp->fr.rcPage.top = 0;
ppp->fr.rcPage.bottom = ppp->rc.bottom;
// leave 1" (1440 twips) margins if that will leave >= 1"
if(ppp->rc.bottom >= 1440 + 1440 + 1440)
ppp->rc.bottom -= (ppp->rc.top = 1440);
}
else
{
const LONG xPerInch = GetDeviceCaps(ppp->fr.hdcTarget, LOGPIXELSX);
const LONG yPerInch = GetDeviceCaps(ppp->fr.hdcTarget, LOGPIXELSY);
const LONG dxRes = GetDeviceCaps(ppp->fr.hdcTarget, HORZRES);
const LONG dyRes = GetDeviceCaps(ppp->fr.hdcTarget, VERTRES);

ppp->rc.left = ppp->rc.top = 0;
ppp->dxPage = (dxRes * 1440l) / xPerInch;
ppp->rc.right = (INT) ppp->dxPage;
ppp->dyPage = (dyRes * 1440l) / yPerInch;
ppp->rc.bottom = (INT) ppp->dyPage;
ppp->fr.rcPage = ppp->rc;
}

PPInitDialogSize(ppp);

#ifdef DEBUG
if(!QueryCheck(ppp->predoc, IDM_PPMETA))
#endif// DEBUG
{
HDC hdcT;
#ifdef NOBLIT

hdcT = GetDC(ppp->hwndDlg);
ppp->fr.hdc = CreateCompatibleDC(hdcT);
ReleaseDC(ppp->hwndDlg, hdcT);
if(!ppp->fr.hdc)
goto err;
#else// NOBLIT
LONG xPerInch;
LONG yPerInch;

hdcT = GetDC(ppp->hwndDlg);
SaveDC(hdcT);
SetMapMode(hdcT, MM_TEXT);
xPerInch = GetDeviceCaps(hdcT, LOGPIXELSX);
yPerInch = GetDeviceCaps(hdcT, LOGPIXELSY);
ppp->dxBmp = (INT) ((ppp->dxPage * xPerInch) / 1440l);
ppp->dyBmp = (INT) ((ppp->dyPage * yPerInch) / 1440l);
ppp->fr.hdc = CreateCompatibleDC(hdcT);
ppp->hbmp = CreateCompatibleBitmap(hdcT, ppp->dxBmp, ppp->dyBmp);
RestoreDC(hdcT, -1);
ReleaseDC(ppp->hwndDlg, hdcT);
if(!ppp->fr.hdc)
{
if(ppp->hbmp)
DeleteObject(ppp->hbmp);
ppp->hbmp = 0;
goto err;
}
if(!ppp->hbmp)
goto err;
ppp->hbmpOld = SelectObject(ppp->fr.hdc, ppp->hbmp);
#endif// NOBLIT, else
SetMapMode(ppp->fr.hdc, MM_TEXT);
}

ppp->fr.chrg.cpMin = 0;
ppp->fr.chrg.cpMost = ppp->cchText;

return fTrue;

err:
if(ppp->fr.hdcTarget)
{
DeleteDC(ppp->fr.hdcTarget);
ppp->fr.hdcTarget = 0;
}
if(ppp->fr.hdc)
{
DeleteDC(ppp->fr.hdc);
ppp->fr.hdc = 0;
}
#ifndef NOBLIT
Assert(!ppp->hbmp);
Assert(!ppp->hbmpOld);
#endif// !NOBLIT

MessageBoxA(hwndMain,
"An error occurred while processing the print preview",
NULL, MB_ICONSTOP | MB_OK);

return fFalse;
}

LOCAL VOID PPInitDialogSize(PRINTPREVIEW *ppp)
{
LONG dx;
LONG dy;
LONG numer;
LONG denom;
RECT rc;

dx = GetSystemMetrics(SM_CXFULLSCREEN) - 2 * GetSystemMetrics(SM_CXBORDER);
dy = GetSystemMetrics(SM_CYFULLSCREEN) - 2 * GetSystemMetrics(SM_CYBORDER);

// select initial scale
if(dy * ppp->dxPage > dx * ppp->dyPage)
{
numer = dx;
denom = ppp->dxPage;
}
else
{
numer = dy;
denom = ppp->dyPage;
}
rc.top = 0;
rc.left = 0;
rc.bottom = (INT) ((numer * ppp->dyPage) / denom);
rc.right = (INT) ((numer * ppp->dxPage) / denom);

rc.right += 2 * GetSystemMetrics(SM_CXFRAME);
rc.bottom += GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);

MoveWindow(ppp->hwndDlg, rc.left, rc.top, rc.right, rc.bottom, fTrue);
ShowWindow(ppp->hwndDlg, SW_SHOW);
}

LOCAL VOID PPChangePage(PRINTPREVIEW *ppp, BOOL fPrev)
{
HCURSOR hcur;
TCHAR szCaption[40];

if(ppp->cchText < 0 && !FPPInit(ppp))
return;

if(fPrev)
{
// no page before page zero or no page array so we can't go back
if(ppp->ipage <= 0 || !ppp->rgcpPages)
{
MessageBeep(0);
return;
}
Assert(ppp->ipage - 1 < ppp->cpage);
// backup a page
ppp->fr.chrg.cpMin = ppp->rgcpPages[ppp->ipage - 1];
}
else if(ppp->ipage + 1 >= ppp->cpage)
{
if(ppp->fDone)
{
MessageBeep(0);
return;
}
if(!ppp->rgcpPages)
goto doit;
if(ppp->ipage + 1 >= ppp->cpageMost)
{
LONG *pcp;

pcp = (LONG *) GlobalReAllocPtr(ppp->rgcpPages,
(ppp->cpageMost + cpageChunk) * sizeof(LONG), GHND);
if(!pcp)
{
GlobalFreePtr(ppp->rgcpPages);
ppp->rgcpPages = NULL;
ppp->cpage = ppp->cpageMost = 0;
goto doit;
}
ppp->rgcpPages = pcp;
ppp->cpageMost += cpageChunk;
}
ppp->cpage++;
ppp->rgcpPages[ppp->ipage + 1] = ppp->fr.chrg.cpMin;
}

doit:
hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));

#ifndef NOBLIT
if(ppp->fr.hdc)
{
RECT rc;

rc.top = rc.left = 0;
rc.bottom = ppp->dyBmp;
rc.right = ppp->dxBmp;
// erase the background
SetBkColor(ppp->fr.hdc, GetSysColor(COLOR_WINDOW));
ExtTextOut(ppp->fr.hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, NULL);
// tell RichEdit not to bother erasing the background
SetBkMode(ppp->fr.hdc, TRANSPARENT);
}
#endif// !NOBLIT

#ifdef DEBUG
if(ppp->hmf)
{
// delete any metafile left around from the last page
DeleteMetaFile(ppp->hmf);
ppp->hmf = NULL;
}
if(QueryCheck(ppp->predoc, IDM_PPMETA))
{
AssertSz(!ppp->fr.hdc, "How'd that get there?");
ppp->fr.hdc = CreateMetaFile(NULL);
if(!ppp->fr.hdc)
goto err;
TraceTag(tagEnumMF, "Window Ext = %d, %d", ppp->dxPage, ppp->dyPage);
}
#endif// DEBUG

ppp->fr.rc = ppp->rc;
ppp->fr.chrg.cpMin = SendMessage(ppp->predoc->hwndRE, EM_FORMATRANGE,
(WPARAM) fTrue, (LPARAM) &ppp->fr);

if(!fPrev && ppp->fr.chrg.cpMin >= ppp->fr.chrg.cpMost)
{
// free up RichEdit's format range info
ppp->fr.chrg.cpMin = ppp->fr.chrg.cpMost = ppp->cchText;
SendMessage(ppp->predoc->hwndRE, EM_FORMATRANGE, 0, 0);
// setup for a prev
ppp->fr.chrg.cpMost = ppp->cchText;
// done with all pages, ppp->cpage is the max # of pages
ppp->fDone = fTrue;
}

#ifdef DEBUG
if(QueryCheck(ppp->predoc, IDM_PPMETA))
{
//$ REVIEW: what happens to ppp->fr.hdc if this fails?
ppp->hmf = CloseMetaFile(ppp->fr.hdc);
ppp->fr.hdc = 0;
if(!ppp->hmf)
goto err;
}
#endif// DEBUG

// update the current page number
ppp->ipage += fPrev ? -1 : 1;

EnableWindow(GetDlgItem(ppp->hwndDlg, PSB_PrevPage), ppp->ipage > 0);
EnableWindow(GetDlgItem(ppp->hwndDlg, PSB_NextPage),
!ppp->fDone || ppp->ipage + 1 < ppp->cpage);

wsprintf(szCaption, szFmtPrintPreview, ppp->ipage + 1);
SetWindowText(ppp->hwndDlg, szCaption);

// cause redraw
InvalidateRect(ppp->hwndDlg, NULL, fTrue);

(void) SetCursor(hcur);

return;

#ifdef DEBUG
err:
ppp->fDone = fTrue;

// free up RichEdit's format range info
ppp->fr.chrg.cpMin = ppp->fr.chrg.cpMost = ppp->cchText;
SendMessage(ppp->predoc->hwndRE, EM_FORMATRANGE, 0, 0);
// setup for a prev
ppp->fr.chrg.cpMost = ppp->cchText;
(void) SetCursor(hcur);
MessageBoxA(hwndMain,
"An error occurred while processing the print preview",
NULL, MB_ICONSTOP | MB_OK);
#endif// DEBUG
}

LOCAL VOID PPPaint(PRINTPREVIEW *ppp)
{
HDC hdc;
RECT rc;
RECT rcClient;
PAINTSTRUCT ps;

// if we're iconic, don't bother
if(IsIconic(ppp->hwndDlg))
return;

hdc = BeginPaint(ppp->hwndDlg, &ps);
SaveDC(hdc);
GetClientRect(ppp->hwndDlg, &rcClient);
GetWindowRect(GetDlgItem(ppp->hwndDlg, PSB_TopLeft), &rc);
MapWindowPoints(GetDesktopWindow(), ppp->hwndDlg, (LPPOINT) &rc, 2);
rc.bottom = rc.top + GetSystemMetrics(SM_CYBORDER);
rc.right = rcClient.right;
SetBkColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rc, "", 0, NULL);
rc.top = rc.bottom;
rc.bottom = rcClient.bottom;
#ifdef DEBUG
if(ppp->hmf)
{
// erase the background
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rc, "", 0, NULL);
// make the window the same logical size as the page
SetMapMode(hdc, MM_ANISOTROPIC);
SetViewportOrgEx(hdc, rc.left, rc.top, NULL);
TraceTag(tagEnumMF, "Viewport Org = %d, %d", rc.left, rc.top);
SetViewportExtEx(hdc, rc.right - rc.left, rc.bottom - rc.top, NULL);
TraceTag(tagEnumMF, "Viewport Ext = %d, %d", rc.right - rc.left, rc.bottom - rc.top);
if(FFromTag(tagEnumMF))
EnumMetaFile(hdc, ppp->hmf, MFEnumCallback, 0);
PlayMetaFile(hdc, ppp->hmf);
}
else
#endif// DEBUG
if(ppp->ipage >= 0)
{
#ifdef NOBLIT
HDC hdcSave = ppp->fr.hdc;
LONG xPerInch = GetDeviceCaps(hdc, LOGPIXELSX);
LONG yPerInch = GetDeviceCaps(hdc, LOGPIXELSY);

// make the window the same logical size as the page
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, (ppp->dxPage * xPerInch) / 1440,
(ppp->dyPage * yPerInch) / 1440, NULL);
SetViewportExtEx(hdc, rc.right - rc.left, rc.bottom - rc.top, NULL);

// erase the background
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rc, "", 0, NULL);

// tell RichEdit not to bother erasing the background
SetBkMode(hdc, TRANSPARENT);

ppp->fr.hdc = hdc;
ppp->fr.rc = ppp->rc;
ppp->fr.chrg.cpMin = ppp->rgcpPages[ppp->ipage];
ppp->fr.chrg.cpMin = SendMessage(ppp->predoc->hwndRE, EM_FORMATRANGE,
(WPARAM) fTrue, (LPARAM) &ppp->fr);
ppp->fr.hdc = hdcSave;
#else// NOBLIT
// convert RECT to x,y,dx,dy
rc.right -= rc.left;
rc.bottom -= rc.top;

StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom,
ppp->fr.hdc, 0, 0, ppp->dxBmp, ppp->dyBmp, SRCCOPY);
#endif// NOBLIT, else
}
RestoreDC(hdc, -1);
EndPaint(ppp->hwndDlg, &ps);
}

#ifdef DEBUG

int CALLBACK MFEnumCallback(HDC hdc, HANDLETABLE *lpHTable, METARECORD *pmfr,
int nObj, LPARAM lparam)
{
switch(pmfr->rdFunction)
{
case META_SETWINDOWORG:
TraceTag(tagEnumMF, "SetWindowOrg(): %d, %d", (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_SETVIEWPORTORG:
TraceTag(tagEnumMF, "SetViewportOrg(): %d, %d", (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_OFFSETWINDOWORG:
TraceTag(tagEnumMF, "OffsetWindowOrg(): %d, %d", (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_OFFSETVIEWPORTORG:
TraceTag(tagEnumMF, "OffsetViewportOrg(): %d, %d", (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_SETWINDOWEXT:
TraceTag(tagEnumMF, "SetWindowExt(): %d, %d", (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_SETVIEWPORTEXT:
TraceTag(tagEnumMF, "SetViewportExt(): %d, %d", (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_SCALEWINDOWEXT:
TraceTag(tagEnumMF, "ScaleWindowExt(): %d, %d, %d, %d", (INT) (SHORT) pmfr->rdParm[3], (INT) (SHORT) pmfr->rdParm[2], (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_SCALEVIEWPORTEXT:
TraceTag(tagEnumMF, "ScaleViewportExt(): %d, %d, %d, %d", (INT) (SHORT) pmfr->rdParm[3], (INT) (SHORT) pmfr->rdParm[2], (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_SAVEDC:
TraceTag(tagEnumMF, "SaveDC()");
break;

case META_RESTOREDC:
TraceTag(tagEnumMF, "RestoreDC()");
break;

case META_SELECTPALETTE:
TraceTag(tagEnumMF, "Select Palette");
break;

case META_SETMAPMODE:
TraceTag(tagEnumMF, "SetMapMode(): %d", (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_EXTTEXTOUT:
TraceTag(tagEnumMF, "ExtTextOut(): %d, %d", (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_TEXTOUT:
TraceTag(tagEnumMF, "TextOut(): %d, %d", (INT) (SHORT) pmfr->rdParm[3], (INT) (SHORT) pmfr->rdParm[2]);
break;

case META_LINETO:
TraceTag(tagEnumMF, "LineTo(): %d, %d", (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;

case META_RECTANGLE:
TraceTag(tagEnumMF, "Rectangle(): %d, %d, %d, %d", (INT) (SHORT) pmfr->rdParm[3], (INT) (SHORT) pmfr->rdParm[2], (INT) (SHORT) pmfr->rdParm[1], (INT) (SHORT) pmfr->rdParm[0]);
break;
}

return fTrue;
}

#endif// DEBUG

/*
 *I T P C A L L   I m p l e m e n t a t i o n
 */

/*
 *ITPCALL_New
 *
 *Purpose:
 *Creates a new ITPCALL.
 *
 *Arguments:
 *REDOC *Pointer to the document information.
 *
 *Returns:
 *ITPCALL *The newly created ITPCALL.
 */
ITPCALL * ITPCALL_New(REDOC * predoc)
{
#ifndef NO_OLE
ITPCALL * pitpcall;

if(!(pitpcall = (ITPCALL *) GlobalAllocPtr(GHND, sizeof(ITPCALL))))
goto Error;
pitpcall->lpVtbl = &ITPCALL_Vtbl;
pitpcall->cRef = 1;// Start with one reference
pitpcall->predoc = predoc;
if(!(pitpcall->pipframe = ITPOLEINPLACEFRAME_New(predoc)))
GlobalFreePtr(pitpcall);
else
return pitpcall;
Error:
#endif// !NO_OLE

return NULL;
}

/*
 *ITPCALL_QueryInterface
 *
 *Purpose:
 *Returns a pointer to the specified site.
 *
 *Arguments:
 *LPUNKNOWN *Object from which we want an interface.
 *REFIIDInterface we want.
 *LPUNKNOWN *Interface we return.
 *
 *Returns:
 *HRESULT Error status.
 */
STDMETHODIMP ITPCALL_QueryInterface(LPUNKNOWN punk, REFIID riid,
   LPUNKNOWN * ppvObj)
{
SCODE sc = S_OK;

if(IsEqualIID(riid, &IID_IUnknown))
ITPCALL_AddRef(*ppvObj = punk);
else if(IsEqualIID(riid, &IID_IRichEditOleCallback))
ITPCALL_AddRef(*ppvObj = punk);
else
sc = E_NOINTERFACE, *ppvObj = NULL;

return sc;
}

/*
 *ITPCALL_AddRef
 *
 *Purpose:
 *Increments reference count on the specified site.
 *
 *Arguments:
 *LPUNKNOWN *Object whose count we want to increment.
 *
 *Returns:
 *ULONGNew value of reference count.
 */
STDMETHODIMP_(ULONG) ITPCALL_AddRef(LPUNKNOWN punk)
{
return ++PitpcallFromPunk(punk)->cRef;
}

/*
 *ITPCALL_Release
 *
 *Purpose:
 *Decrements reference count on the specified site.  If count is
 *decremented to zero, the object is freed.
 *
 *Arguments:
 *LPUNKNOWN *Object whose count we want to decrement.
 *
 *Returns:
 *ULONGNew value of reference count.
 */
STDMETHODIMP_(ULONG) ITPCALL_Release(LPUNKNOWN punk)
{
#ifndef NO_OLE
ITPCALL * pitpcall = PitpcallFromPunk(punk);
ULONG cRef = --pitpcall->cRef;

if(!cRef)
{
// Free stuff we own
if(pitpcall->pipframe)
{
pitpcall->pipframe->lpVtbl->Release((LPOLEINPLACEFRAME)
 pitpcall->pipframe);
}

// Free memory allocated for us
GlobalFreePtr(pitpcall);
TraceTag(tagITPCALL, "ITPCALL_Release: freeing the itpcall");
}

AssertSz(cRef >= 0, "ITPCALL_Release: negative cRef");
return cRef;
#endif// !NO_OLE

return (ULONG) NULL;
}

/*
 *ITPCALL_GetNewStorage
 *
 *Purpose:
 *Gets storage for a new object.
 *
 *Arguments:
 *ITPCALL *Callback object.
 *LPSTORAGE FAR *Where to return storage.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPCALL_GetNewStorage(ITPCALL * pitpcall, LPSTORAGE FAR * ppstg)
{
SCODE sc = S_OK;
WCHAR szItemW[OLEUI_CCHPATHMAX];

wsprintfW(szItemW, L"REOBJ%ld", ++pitpcall->predoc->cItem);
if(!(*ppstg = OleStdCreateChildStorage(pitpcall->predoc->pstg, szItemW)))
{
MessageBoxW(hwndMain, szItemW, L"No item storage!", MB_OK);
sc = E_FAIL;
}

return sc;
}

/*
 *ITPCALL_GetInPlaceContext
 *
 *Purpose:
 *Gets context information for an in place object.
 *
 *Arguments:
 *ITPCALL *Callback object.
 *LPOLEINPLACEFRAME *Frame window object.
 *LPOLEINPLACEUIWINDOW *Document window object.
 *LPOLEINPLACEFRAMEINFOFrame window information.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPCALL_GetInPlaceContext(ITPCALL * pitpcall,
   LPOLEINPLACEFRAME FAR * ppipframe,
   LPOLEINPLACEUIWINDOW FAR* ppipuiDoc,
   LPOLEINPLACEFRAMEINFO pipfinfo)
{
#ifndef NO_OLE
// Return window pointers
*ppipframe = (LPOLEINPLACEFRAME) pitpcall->pipframe;
(VOID) (*ppipframe)->lpVtbl->AddRef(*ppipframe);
*ppipuiDoc = NULL;

// Fill in frame window information
pipfinfo->cb = sizeof(OLEINPLACEFRAMEINFO);
pipfinfo->fMDIApp = FALSE;
pipfinfo->hwndFrame = pitpcall->predoc->hwndParent;
pipfinfo->haccel = (long) NULL;
pipfinfo->cAccelEntries = 0;
#endif// !NO_OLE

return S_OK;
}

/*
 *ITPCALL_ShowContainerUI
 *
 *Purpose:
 *Displays or hides REITP's container UI.
 *
 *Arguments:
 *ITPCALL *Callback object.
 *BOOL
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPCALL_ShowContainerUI(ITPCALL * pitpcall, BOOL fShow)
{
TraceTag(tagITPCALL, "ITPCALL_ShowContainerUI(%d)", fShow);

// If we're displaying our UI, show our menus
if(fShow)
ITPOLEINPLACEFRAME_SetMenu(pitpcall->pipframe, NULL, NULL, NULL);

return S_OK;
}

STDMETHODIMP ITPCALL_QueryInsertObject(ITPCALL * pitpcall, LPCLSID pclsid,
LPSTORAGE pstg, LONG cp)
{
#if !defined(NO_OLE) && defined(DEBUG)
LPTSTR szProgID;

if(HrProgIDFromCLSIDA(pclsid, &szProgID))
{
TraceTag(tagITPCALL, "QueryInsertObject(): unknown class id");
}
else
{
SCODE sc = S_OK;

TraceTag(tagITPCALL, "QueryInsertObject(): %s object", szProgID);
if(QueryCheck(pitpcall->predoc, IDM_REFUSEGRAPH) &&
!lstrcmpi(szProgID, "MSGraph"))
{
TraceTag(tagITPCALL, "Refusing object");
sc = S_FALSE;
}
OleStdFreeString((LPSTR) szProgID, NULL);
return sc;
}
#endif// !NO_OLE && DEBUG

return NOERROR;
}

STDMETHODIMP ITPCALL_DeleteObject(ITPCALL * pitpcall, LPOLEOBJECT poleobj)
{
TraceTag(tagITPCALL, "DeleteObject()");
return NOERROR;
}

STDMETHODIMP ITPCALL_QueryAcceptData(ITPCALL * pitpcall, LPDATAOBJECT pdataobj,
CLIPFORMAT *pcfFormat, DWORD reco, BOOL fReally,
HGLOBAL hMetaPict)
{
#if !defined(NO_OLE) && defined(DEBUG)
TraceTag(tagITPCALL, "QueryAcceptData(%s) for %s", fReally ? "really" : "test", (reco == RECO_DROP ? "drop" : (reco == RECO_PASTE ? "paste" : "unknown op")));
if(pitpcall->predoc->fReadOnly)
{
BOOL fDoIt = fFalse;
HRESULT hr;

switch(reco)
{
case RECO_DROP:
fDoIt = QueryCheck(pitpcall->predoc, IDM_ENABLEDRAGREADONLY);
break;

case RECO_PASTE:
fDoIt = QueryCheck(pitpcall->predoc, IDM_ENABLEPASTEREADONLY);
break;

default:
TraceTag(tagNull, "QAD: invalid RECO %lx", reco);
AssertSz(fFalse, "QAD: invalid RECO");
break;
}
if(fDoIt)
{
if(fReally)
{
LPRICHEDITOLE const preole = pitpcall->predoc->preole;

// import that sucker

hr = preole->lpVtbl->ImportDataObject(preole, pdataobj,
*pcfFormat, hMetaPict);
TraceError("IRichEditOle::ImportDataObject", hr);
if(SUCCEEDED(hr))
hr = S_FALSE;// we did it ourselves
}
else
{
// return that we'll import it ourselves
hr = S_FALSE;
}
}
else
{
hr = E_FAIL;
}
TraceError("IPCALL_QueryAcceptData", hr);
return hr;
}
if(QueryCheck(pitpcall->predoc, IDM_TEXTONLY))
{
TraceTag(tagITPCALL, "QueryAcceptData(): asking for text only");
*pcfFormat = CF_TEXT;// accept only text
}
else
{
TraceTag(tagITPCALL, "QueryAcceptData(): delegating back to RichEdit");
}
#endif// !NO_OLE && DEBUG

return NOERROR;
}

STDMETHODIMP ITPCALL_ContextSensitiveHelp(ITPCALL * pitpcall, BOOL fEnterMode)
{
return NOERROR;
}

STDMETHODIMP ITPCALL_GetClipboardData(ITPCALL *pitpcall, CHARRANGE *pchrg,
DWORD reco, LPDATAOBJECT *ppdataobj)
{
#if !defined(NO_OLE) && defined(DEBUG)
TraceTag(tagITPCALL, "GetClipboardData(%d-%d) for %s", pchrg->cpMin, pchrg->cpMost, reco == RECO_COPY ? "copy" : (reco == RECO_CUT ? "cut" : (reco == RECO_DRAG ? "drag" : "unknown op")));

if(pitpcall->predoc->fReadOnly)
{
BOOL fDoIt = fFalse;

switch(reco)
{
case RECO_COPY:
// prevent assert in default case
break;

case RECO_CUT:
fDoIt = QueryCheck(pitpcall->predoc, IDM_ENABLECUTREADONLY);
break;

case RECO_DRAG:
fDoIt = QueryCheck(pitpcall->predoc, IDM_ENABLEDRAGREADONLY);
break;

default:
TraceTag(tagNull, "GCD: invalid RECO %lx", reco);
AssertSz(fFalse, "GCD: invalid RECO");
break;
}
if(fDoIt)
{
LPRICHEDITOLE const preole = pitpcall->predoc->preole;
HRESULT hr;

// delegate to IRichEditOle::GetClipboardData()

*ppdataobj = NULL;
hr = preole->lpVtbl->GetClipboardData(preole, pchrg, reco,
ppdataobj);
TraceError("IRichEditOle::GetClipboardData", hr);
if(SUCCEEDED(hr) && *ppdataobj)
hr = S_FALSE;// we did it ourselves
TraceError("IPCALL_GetClipboardData", hr);
return hr;
}
// go return E_NOTIMPL & let RichEdit deal with it itself
}
#endif// !NO_OLE && DEBUG

// *ppdataobj isn't used if E_NOTIMPL is returned
// so we don't need to set it

// tell RichEdit to do it itself
TraceTag(tagITPCALL, "GetClipboardData(): delegating back to RichEdit");
return E_NOTIMPL;
}

STDMETHODIMP ITPCALL_GetDragDropEffect(ITPCALL *pitpcall, BOOL fDrag,
DWORD grfKeyState, LPDWORD pdwEffect)
{
TraceTag(tagITPCALL, "GetDragDropEffect(%s)", fDrag ? "drag" : "drop");

#ifdef DEBUG
if(QueryCheck(pitpcall->predoc, IDM_SWAPDRAGEFFECT))
{
DWORD dwT;

// strip off move, copy, and link
dwT = *pdwEffect &
~(DWORD) (DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK);

if(!pitpcall->predoc->fReadOnly ||
QueryCheck(pitpcall->predoc, IDM_ENABLEDRAGREADONLY))
{
if(fDrag)
dwT = DROPEFFECT_MOVE | DROPEFFECT_COPY;
else if(grfKeyState & MK_CONTROL)
dwT = DROPEFFECT_MOVE;
else
dwT = DROPEFFECT_COPY;
}

*pdwEffect = dwT;

return NOERROR;
}
#endif// DEBUG

// delegate back to RichEdit
return E_NOTIMPL;
}

STDMETHODIMP ITPCALL_GetContextMenu(ITPCALL *pitpcall, WORD seltype,
LPOLEOBJECT poleobj, CHARRANGE * pchrg, HMENU * phmenu)
{
HMENU hmenu;
HMENU hmenuParent = NULL;
HMENU hmenuVerbs = NULL;
UINT uiT;

TraceTag(tagITPCALL, "GetContextMenu");

hmenuParent = CreatePopupMenu();
if(!hmenuParent)
goto Cleanup;

// Put the verbs on the menu
if(poleobj)
{
OleUIAddVerbMenu(poleobj, NULL, hmenuParent, 0, IDM_OBJECTMIN,
0, TRUE, IDM_OBJECTCONVERT, &hmenuVerbs);
}

// Determine which menu to pass back to RichEdit
*phmenu = hmenu = hmenuVerbs ? hmenuVerbs : hmenuParent;

if(poleobj)
AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);

// Add the cut, copy, paste verbs

uiT = MF_STRING | MF_BYCOMMAND;
uiT |= pchrg->cpMin == pchrg->cpMost ? uiMFDisabled : uiMFEnabled;
AppendMenu(hmenu, uiT, IDM_CUT, "Cut");
AppendMenu(hmenu, uiT, IDM_COPY, "Copy");

uiT = MF_STRING | MF_BYCOMMAND;
uiT |= SendMessage(pitpcall->predoc->hwndRE, EM_CANPASTE, 0, 0)
? uiMFEnabled : uiMFDisabled;
AppendMenu(hmenu, uiT, IDM_PASTE, "Paste");

Cleanup:
if(hmenu == hmenuVerbs && hmenuParent)
DestroyMenu(hmenuParent);
else if(hmenu == hmenuParent && hmenuVerbs)
DestroyMenu(hmenuVerbs);

return S_OK;
}

/*
 *I T P O L E I N P L A C E F R A M E  I m p l e m e n t a t i o n
 */

/*
 *ITPOLEINPLACEFRAME_New
 *
 *Purpose:
 *Creates a new ITPOLEINPLACEFRAME.
 *
 *Arguments:
 *REDOC *Pointer to the document information.
 *
 *Returns:
 *ITPOLEINPLACEFRAME *The newly created ITPOLEINPLACEFRAME.
 */
ITPOLEINPLACEFRAME * ITPOLEINPLACEFRAME_New(REDOC * predoc)
{
#ifndef NO_OLE
ITPOLEINPLACEFRAME * pipframe;

if(!(pipframe = (ITPOLEINPLACEFRAME *) GlobalAllocPtr(GHND, sizeof(ITPOLEINPLACEFRAME))))
return NULL;
pipframe->lpVtbl = &ITPOLEINPLACEFRAME_Vtbl;
pipframe->cRef = 1;// Start with one reference
pipframe->predoc = predoc;
return pipframe;
#endif// !NO_OLE

return NULL;
}

/*
 *ITPOLEINPLACEFRAME_QueryInterface
 *
 *Purpose:
 *Returns a pointer to the specified site.
 *
 *Arguments:
 *LPUNKNOWN *Object from which we want an interface.
 *REFIIDInterface we want.
 *LPUNKNOWN *Interface we return.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_QueryInterface(LPUNKNOWN punk, REFIID riid,
   LPUNKNOWN * ppvObj)
{
SCODE sc = S_OK;

if(IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IOleWindow) ||
IsEqualIID(riid, &IID_IOleInPlaceUIWindow) ||
IsEqualIID(riid, &IID_IOleInPlaceFrame))
{
ITPOLEINPLACEFRAME_AddRef(*ppvObj = punk);
}
else
{
sc = E_NOINTERFACE, *ppvObj = NULL;
}

return sc;
}

/*
 *ITPOLEINPLACEFRAME_AddRef
 *
 *Purpose:
 *Increments reference count on the specified site.
 *
 *Arguments:
 *LPUNKNOWN *Object whose count we want to increment.
 *
 *Returns:
 *ULONGNew value of reference count.
 */
STDMETHODIMP_(ULONG) ITPOLEINPLACEFRAME_AddRef(LPUNKNOWN punk)
{
return ++PipframeFromPunk(punk)->cRef;
}

/*
 *ITPOLEINPLACEFRAME_Release
 *
 *Purpose:
 *Decrements reference count on the specified site.  If count is
 *decremented to zero, the object is freed.
 *
 *Arguments:
 *LPUNKNOWN *Object whose count we want to decrement.
 *
 *Returns:
 *ULONGNew value of reference count.
 */
STDMETHODIMP_(ULONG) ITPOLEINPLACEFRAME_Release(LPUNKNOWN punk)
{
ITPOLEINPLACEFRAME * pipframe = PipframeFromPunk(punk);
ULONG cRef = --pipframe->cRef;

if(!cRef)
{
// Free memory allocated for us
GlobalFreePtr(pipframe);
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_Release: freeing the ipframe");
}

AssertSz(cRef >= 0, "ITPOLEINPLACEFRAME_Release: negative cRef");

return cRef;
}

/*
 *ITPOLEINPLACEFRAME_GetWindow
 *
 *Purpose:
 *Return the window handle of the app-level window for in place use.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_GetWindow(ITPOLEINPLACEFRAME * pipframe,
  HWND * phwnd)
{
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_GetWindow: NYI");

return E_NOTIMPL;
}

/*
 *ITPOLEINPLACEFRAME_ContextSensitiveHelp
 *
 *Purpose:
 *Notifies the frame that the object has entered Context Help mode.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_ContextSensitiveHelp(ITPOLEINPLACEFRAME *pipframe,
 BOOL fEnterMode)
{
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_ContextSensitiveHelp: NYI");
TraceError("ITPOLEINPLACEFRAME_ContextSensitiveHelp()", E_NOTIMPL);

return E_NOTIMPL;
}

/*
 *ITPOLEINPLACEFRAME_GetBorder
 *
 *Purpose:
 *Returns a RECT structure in which the object can put toolbars and
 *similar controls while an object is active in place.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_GetBorder(ITPOLEINPLACEFRAME * pipframe,
  LPRECT prcBorder)
{
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_GetBorder: NYI");
TraceError("ITPOLEINPLACEFRAME_GetBorder()", E_NOTIMPL);

return E_NOTIMPL;
}

/*
 *ITPOLEINPLACEFRAME_RequestBorderSpace
 *
 *Purpose:
 *Determines whether tools can be installed around the objects
 *window frame while the object is active in place.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_RequestBorderSpace(ITPOLEINPLACEFRAME * pipframe,
   LPCBORDERWIDTHS pbw)
{
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_RequestBorderSpace: NYI");
TraceError("ITPOLEINPLACEFRAME_RequestBorderSpace()", E_NOTIMPL);

return E_NOTIMPL;
}

/*
 *ITPOLEINPLACEFRAME_SetBorderSpace
 *
 *Purpose:
 *Allocates space for the border requested in the call to the
 *RequestBorderSpace member function.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_SetBorderSpace(ITPOLEINPLACEFRAME * pipframe,
   LPCBORDERWIDTHS pbw)
{
//$ FUTURE: If NULL, then put our tools back
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_SetBorderSpace: NYI");
TraceError("ITPOLEINPLACEFRAME_SetBorderSpace()", E_NOTIMPL);

return E_NOTIMPL;
}

/*
 *ITPOLEINPLACEFRAME_SetActiveObject
 *
 *Purpose:
 *Called by the object to provide the frame window a direct channel
 *of communication with the active in-place object.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_SetActiveObject(ITPOLEINPLACEFRAME * pipframe,
    LPOLEINPLACEACTIVEOBJECT pipaobj,
    LPCSTR szObjName)
{
#ifndef NO_OLE
// Free any existing active object
if(pipframe->pipaobj)
pipframe->pipaobj->lpVtbl->Release(pipframe->pipaobj);

// If we're given an object, AddRef it; update our remembered ipaobj
if(pipaobj)
pipaobj->lpVtbl->AddRef(pipaobj);
pipframe->pipaobj = pipaobj;

TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_SetActiveObject: %s", szObjName ? szObjName : "NULL");
#endif// !NO_OLE

return S_OK;
}

/*
 *ITPOLEINPLACEFRAME_InsertMenus
 *
 *Purpose:
 *Called by the object server to allow the container to insert
 *its menu groups in the composite menu that will be used during
 *the in-place session.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_InsertMenus(ITPOLEINPLACEFRAME * pipframe,
    HMENU hmenuShared,
    LPOLEMENUGROUPWIDTHS pmgw)
{
//$ FUTURE: Do something interesting here
// Don't actually put any menus on right now
pmgw->width[0] = pmgw->width[2] = pmgw->width[4] = 0;
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_InsertMenus: S_OK");

return S_OK;
}

/*
 *ITPOLEINPLACEFRAME_SetMenu
 *
 *Purpose:
 *Installs the composite menu into the window frame containing
 *the object that is being activated in place.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_SetMenu(ITPOLEINPLACEFRAME * pipframe,
    HMENU hmenuShared, HOLEMENU holemenu,
    HWND hwndActiveObject)
{
#ifndef NO_OLE
HWND hwnd = pipframe->predoc->hwndParent;

TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_SetMenu: hMenuShared=%08lx holemenu=%08lx", (DWORD) (LPVOID) hmenuShared, (DWORD) (LPVOID) holemenu);

// Did OLE give us a menu, or leave us to ourselves?
if(hmenuShared && holemenu)
{
// Put the merged menu OLE gave us on our menu bar
SetMenu(hwnd, hmenuShared);
}
else
{
// Put our regular menu on the menu bar
SetMenu(hwnd, hmenuFull);
SetupWordWrapMenu(pipframe->predoc);
}
DrawMenuBar(hwnd);
# ifdef MAC
DebugStr("\pTry calling OleSetMenuDescriptor()");
# else// MAC
// Pass the menu descriptor to OLE
return OleSetMenuDescriptor(holemenu, pipframe->predoc->hwndParent,
hwndActiveObject, (LPOLEINPLACEFRAME) pipframe,
pipframe->pipaobj);
# endif// MAC, else
#endif// !NO_OLE

return S_OK;
}

/*
 *ITPOLEINPLACEFRAME_RemoveMenus
 *
 *Purpose:
 *Called by the object server to give the container a chance to
 *remove its menu elements from the in-place composite menu.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_RemoveMenus(ITPOLEINPLACEFRAME * pipframe,
    HMENU hmenuShared)
{
// We didn't put any menus on!
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_RemoveMenus: S_OK");

return S_OK;
}

/*
 *ITPOLEINPLACEFRAME_SetStatusText
 *
 *Purpose:
 *Sets and displays status text about the in-place object in the
 *containers frame window status line.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_SetStatusText(ITPOLEINPLACEFRAME * pipframe,
  LPCSTR szStatusText)
{
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_SetStatusText: NYI");
TraceError("ITPOLEINPLACEFRAME_SetStatusText()", E_NOTIMPL);

return E_NOTIMPL;
}

/*
 *ITPOLEINPLACEFRAME_EnableModeless
 *
 *Purpose:
 *Enables or disables modeless dialogs of the frame.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_EnableModeless(ITPOLEINPLACEFRAME * pipframe,
   BOOL fEnable)
{
TraceTag(tagInPlace, "ITPOLEINPLACEFRAME_EnableModeless: NYI");
TraceError("ITPOLEINPLACEFRAME_EnableModeless()", E_NOTIMPL);

return E_NOTIMPL;
}

/*
 *ITPOLEINPLACEFRAME_TranslateAccelerator
 *
 *Purpose:
 *Translates keystrokes intended for the container frame while
 *an object is active in place.
 *
 *Arguments:
 *ITPOLEINPLACESITE *Client site.
 *
 *Returns:
 *HRESULTError status.
 */
STDMETHODIMP ITPOLEINPLACEFRAME_TranslateAccelerator(ITPOLEINPLACEFRAME *pipframe,
 LPMSG pmsg, WORD wID)
{
// REITP doesn't have any accelerators!
return S_FALSE;
}

LOCAL VOID FindReplace(REDOC *predoc, BOOL fReplace)
{
FINDREPLACE *pfr;

if(hwndFR)
return;

if(!(pfr = predoc->pfr))
{
pfr = predoc->pfr = (FINDREPLACE * ) GlobalAllocPtr(GHND,
sizeof(FINDREPLACE) + 2 * 80 * sizeof(TCHAR));
if(!predoc->pfr)
return;
}
else
{
memset(pfr, 0, sizeof(FINDREPLACE));
}

SendMessage(predoc->hwndRE, EM_HIDESELECTION, fFalse, fTrue);

pfr->lStructSize = sizeof(FINDREPLACE);
pfr->hwndOwner = predoc->hwndParent;
pfr->Flags = FR_HIDEUPDOWN;
pfr->lpstrFindWhat = (LPSTR) (((BYTE *) pfr) + sizeof(FINDREPLACE));
pfr->lpstrReplaceWith = pfr->lpstrFindWhat + 80;
pfr->wFindWhatLen = 80 * sizeof(TCHAR);
pfr->wReplaceWithLen = 80 * sizeof(TCHAR);

hwndFR = fReplace ? ReplaceText(pfr) : FindText(pfr);
}

LOCAL VOID ProcessFindReplace(REDOC *predoc, FINDREPLACE *pfr)
{
HCURSOR hcur;
LONG cszReplaced = 0;
LONG cpMatch;
CHARRANGE chrgSave;
FINDTEXTEX ft;

SendMessage(predoc->hwndRE, EM_EXGETSEL, 0, (LPARAM) &chrgSave);
ft.chrg.cpMost = -1;// search through end of the text
ft.chrg.cpMin = chrgSave.cpMin;
if(!(pfr->Flags & (FR_REPLACE | FR_REPLACEALL)))
ft.chrg.cpMin = chrgSave.cpMost;
ft.lpstrText = pfr->lpstrFindWhat;

if(pfr->Flags & FR_REPLACEALL)
{
hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
SendMessage(predoc->hwndRE, EM_HIDESELECTION, fTrue, fFalse);
}

next:
cpMatch = SendMessage(predoc->hwndRE, EM_FINDTEXTEX,
(WPARAM) (pfr->Flags),
(LPARAM) &ft);
if(cpMatch < 0)
{
if(cszReplaced == 0)
{
MessageBoxA(hwndFR,
"The search text is not found.", szAppName, MB_OK);
}

else
{
char rgch[80];

wsprintfA(rgch, "Replaced %ld occurances.", cszReplaced);
MessageBoxA(hwndFR, rgch, szAppName, MB_OK);
}
SetFocus(hwndFR);
goto done;
}
//ft.chrg.cpMin = cpMatch;
//ft.chrg.cpMost = cpMatch + lstrlen(ft.lpstrText);
ft.chrg.cpMin = ft.chrgText.cpMin;
ft.chrg.cpMost = ft.chrgText.cpMost;
SendMessage(predoc->hwndRE, EM_EXSETSEL, 0, (LPARAM) &ft.chrg);
if(((pfr->Flags & FR_REPLACE) && cszReplaced == 0) ||
(pfr->Flags & FR_REPLACEALL))
{
SendMessage(predoc->hwndRE, EM_REPLACESEL, 0,
(LPARAM) pfr->lpstrReplaceWith);
ft.chrg.cpMin = cpMatch + lstrlen(pfr->lpstrReplaceWith);
ft.chrg.cpMost = -1;// search through end of the text
cszReplaced++;
goto next;
}

done:
if(pfr->Flags & FR_REPLACEALL)
{
SetCursor(hcur);

SendMessage(predoc->hwndRE, EM_HIDESELECTION, fFalse, fFalse);
}
}

VOID UpdateFormatBar(REDOC * predoc)
{
CHARFORMAT cf;
PARAFORMAT pf;

cf.cbSize = sizeof(CHARFORMAT);
pf.cbSize = sizeof(PARAFORMAT);

SendMessage(predoc->hwndRE, EM_GETCHARFORMAT, TRUE, (LPARAM) &cf);
SendMessage(predoc->hwndFormatBar, EM_SETCHARFORMAT, 0, (LPARAM) &cf);
SendMessage(predoc->hwndRE, EM_GETPARAFORMAT, FALSE, (LPARAM) &pf);
SendMessage(predoc->hwndFormatBar, EM_SETPARAFORMAT, 0, (LPARAM) &pf);
}

LOCAL VOID ShowMargins(REDOC *predoc)
{
if(ToggleCheck(predoc, IDM_SHOWMARGINS))
{
RECT rc;

GetClientRect(predoc->hwndParent, &rc);
ResizeRedoc(predoc, rc);
}
else
{
SendMessage(predoc->hwndRE, EM_SETRECT, 0, 0);
}
}

# ifdef DEBUG

LOCAL VOID SetLimit(REDOC *predoc)
{
LONG cchLimit = 32767;

if(ToggleCheck(predoc, IDM_BIGLIMIT))
cchLimit = 1024 * (LONG) 1024 - 1;

SendMessage(predoc->hwndRE, EM_EXLIMITTEXT, 0, cchLimit);
}

static TCHAR szText[] = "Once upon a time there was an edit control.  "
"This was a happy edit control until some mean evil tester "
"(who shall remain nameless) came along and started beating on "
"the poor little helpless edit control.  The edit control bravely "
"faced it's unprovoked attacker, but an edit control can only take "
"so much.  One day when it was being ferociously tortured it thought "
"to itself \"What have I ever done to this poor homely creature to "
"deserve this type of punishment?\".  It stirred up it's self esteem "
"and stood up and said to the nasty creature \"I am an edit control, "
"hear me roar!  I deserve to be treated with dignity and respect!\".  "
"The evil little tester was taken aback by the edit control's sudden "
"show of courage, but being the spawn of hell, he dismissed the edit "
"control's declaration and continued his ghastly attacks.  The edit "
"control decided that nothing would ever reform this hideous beast "
"imparting terror upon it so he decided to do something about it.  "
"The next day when the heathen was preparing to nuke the edit control "
"the edit control jumped out at the beast and ripped it's heart out.  "
"The tester stood shocked for a millisecond and then dropped over dead.  "
"The end.\r\n\r\n";

LOCAL VOID FillerUp(REDOC *predoc)
{
const LONG cch = lstrlen(szText);
LONG cchAdded;
LONG cchText;
DWORD dwTicks;

SetWindowText(predoc->hwndRE, TEXT(""));
cchText = 0;

(void) GetAsyncKeyState(VK_ESCAPE);// reset key state
dwTicks = GetTickCount();
do
{
LONG cchT;

SendMessage(predoc->hwndRE, EM_REPLACESEL, 0, (LPARAM) (LPSTR) szText);
cchT = SendMessage(predoc->hwndRE, WM_GETTEXTLENGTH, 0, 0);
cchAdded = cchT - cchText;
if(cchText % 8192 > cchT % 8192)
{
const DWORD dwT = GetTickCount();

TraceTag(tagGeneral, "%ld characters, %ld ticks", cchT, dwT - dwTicks);
dwTicks = dwT;
}
cchText = cchT;
if(GetAsyncKeyState(VK_ESCAPE) & 0x8001)
{
TraceTag(tagGeneral, "stopping for escape");
break;
}
} while(cchAdded > 0 && !fErrSpace);

TraceTag(tagGeneral, "Stopped at %ld character", cchText);
}

#endif// DEBUG


REITP.H

/* =========================================================================

File: Reitp.h
Function: Header file for Reitp.c. Exports the DoOpen() and PrintREDoc()
functions so the Mac version can support the required OpenDoc and Print
Apple Events.

============================================================================ */

typedef struct _redoc
{
HWND hwndRE;
HWND hwndParent;
DWORD dwFormat;
BOOL fReadOnly;
BOOL fWysiwyg;
BOOL fWrap;
BOOL fUpdateEditMenu;
WPARAM scf;
HMENU hmenuVerbs;
LPRICHEDITOLE preole;
struct _itpcall * pitpcall;
LPSTORAGE pstg;
ULONG cItem;
PRINTDLG pd;
HDC hdcTarget;
HWND hwndFormatBar;
FINDREPLACE *pfr;
TCHAR szFile[256];
TCHAR szTitle[32];
} REDOC;

LRESULT DoOpen(REDOC *predoc, LPSTR  szFile, LPSTR  szTitle, BOOL  fInsert);

VOID PrintREDoc(REDOC *predoc);


UTILITY.H

/*
 * UTILITY.H
 *
 * Miscellaneous prototypes and definitions for OLE UI dialogs.
 *
 * Copyright (c)1992-1996 Microsoft Corporation, All Right Reserved
 */

#ifndef _UTILITY_H_
#define _UTILITY_H_

//Function prototypes
//UTILITY.C
HCURSOR  WINAPI HourGlassOn(void);
void     WINAPI HourGlassOff(HCURSOR);

BOOL     WINAPI Browse(HWND, LPSTR, LPSTR, UINT, UINT, DWORD);
int      WINAPI ReplaceCharWithNull(LPSTR, int);
int      WINAPI ErrorWithFile(HWND, HINSTANCE, UINT, LPSTR, UINT);
HFILE WINAPI DoesFileExist(LPSTR lpszFile, OFSTRUCT FAR* lpOpenBuf);

HICON FAR PASCAL    HIconAndSourceFromClass(REFCLSID, LPSTR, UINT FAR *);
BOOL FAR PASCAL FIconFileFromClass(REFCLSID, LPSTR, UINT, UINT FAR *);
LPSTR FAR PASCAL PointerToNthField(LPSTR, int, char);
BOOL FAR PASCAL GetAssociatedExecutable(LPSTR, LPSTR);
HICON    WINAPI HIconFromClass(LPSTR);
BOOL     WINAPI FServerFromClass(LPSTR, LPSTR, UINT);
UINT     WINAPI UClassFromDescription(LPSTR, LPSTR, UINT);
UINT     WINAPI UDescriptionFromClass(LPSTR, LPSTR, UINT);
BOOL     WINAPI FVerbGet(LPSTR, UINT, LPSTR);
LPSTR    WINAPI ChopText(HWND hwndStatic, int nWidth, LPSTR lpch);
void     WINAPI OpenFileError(HWND hDlg, UINT nErrCode, LPSTR lpszFile);

#endif //_UTILITY_H_


README.TXT

Rebar Control Sample "Rebar"

Rebar is a simple example of implementing a rebar common control in an application.

This example creates a rebar control that has two bands. One contains a ComboBox and the other contains a Pushbutton.

The rebar control can be placed on any of the four sides of the parent window by selecting the desired side from the "View" menu. When the side is changed, the existing rebar control is destroyed and another one is created. The reason for this is that the control statically stores some styles at creation time and they cannot be changed "on the fly".

To close the application, simply select "Exit" from the "File" menu or click the close button on the caption bar.


REBAR.C

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ReBar.c

   Description:   ReBar sample implementation.

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

#define STRICT

/**************************************************************************
   Include Files
**************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "resource.h"

/**************************************************************************
   Local Function Prototypes
**************************************************************************/

int PASCAL WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK About(HWND, UINT, WPARAM, LPARAM);
HWND BuildRebar(HWND);
LRESULT DoNotify(HWND, WPARAM, LPARAM);
void MoveRebar(HWND);
LRESULT HandleMenuPopup(HMENU);
LRESULT HandleCommand(HWND, WPARAM, LPARAM);

/**************************************************************************
   Global Variables
**************************************************************************/

#define ID_REBAR     1000
#define ID_BUTTON    2000
#define ID_COMBOBOX  2001

#define TOP    0x00
#define LEFT   0x01
#define BOTTOM 0x02
#define RIGHT  0x03

HINSTANCE   g_hInst;
WORD        g_wSide;

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

   WinMain

   Parameters:

   Description:

   Returns:

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

int PASCAL WinMain(  HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
MSG      msg;
INITCOMMONCONTROLSEX iccex;

iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_COOL_CLASSES;
InitCommonControlsEx(&iccex);

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

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

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

return msg.wParam;
}

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

   InitApplication

   Parameters:

   Description:

   Returns:

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

BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS  wc;

wc.style          = 0;
wc.lpfnWndProc    = (WNDPROC)MainWndProc;
wc.cbClsExtra     = 0;
wc.cbWndExtra     = 0;
wc.hInstance      = hInstance;
wc.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName   = MAKEINTRESOURCE(IDM_GENERICMENU);
wc.lpszClassName  = "GenericClass";

return RegisterClass(&wc);
}

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

   InitInstance

   Parameters:

   Description:

   Returns:

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

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

g_hInst = hInstance;

hWnd = CreateWindowEx(  0,
                        "GenericClass",
                        "Generic 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;

}

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

   MainWndProc

   Parameters:

   Description:

   Returns:

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

LRESULT CALLBACK MainWndProc( HWND hWnd,
                              UINT uMessage,
                              WPARAM wParam,
                              LPARAM lParam)
{
switch (uMessage)
   {
   case WM_CREATE:
      g_wSide = TOP;

      BuildRebar(hWnd);
      break;

   case WM_NOTIFY:
      return DoNotify(hWnd, wParam, lParam);

   case WM_SIZE:
      MoveRebar(hWnd);
      break;

   case WM_DESTROY:
      PostQuitMessage(0);
      break;

   case WM_INITMENUPOPUP:
      return HandleMenuPopup((HMENU)wParam);

   case WM_COMMAND:
      return HandleCommand(hWnd, wParam, lParam);

   default:
      break;
   }
return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

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

   About

   Parameters:

   Description:

   Returns:

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

BOOL CALLBACK About( HWND hDlg,
                     UINT uMessage,
                     WPARAM wParam,
                     LPARAM lParam)
{
switch (uMessage)
   {
   case WM_INITDIALOG:
      return TRUE;

   case WM_COMMAND:
      switch(wParam)
         {
         case IDOK:
            EndDialog(hDlg, IDOK);
            return TRUE;

         case IDCANCEL:
            EndDialog(hDlg, IDCANCEL);
            return TRUE;
         }
      break;
    }

return FALSE;
}

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

   BuildRebar

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

HWND BuildRebar(HWND hwndParent)
{
HWND     hwndRebar = NULL;
LRESULT  lResult;

hwndRebar = CreateWindowEx(   WS_EX_TOOLWINDOW,
                              REBARCLASSNAME,
                              NULL,
                              WS_VISIBLE |
                                 WS_BORDER |
                                 WS_CHILD |
                                 WS_CLIPCHILDREN |
                                 WS_CLIPSIBLINGS |
                                 RBS_VARHEIGHT |
                                 RBS_BANDBORDERS |
                                 CCS_NODIVIDER |
                                 CCS_NOPARENTALIGN |
                                 ((g_wSide & 0x01) ? CCS_VERT : 0) | //g_wSide is odd if this is a vertical bar
                                 0,
                              0,
                              0,
                              200,
                              100,
                              hwndParent,
                              (HMENU)ID_REBAR,
                              g_hInst,
                              NULL);

if(hwndRebar)
   {
   REBARINFO      rbi;
   HIMAGELIST     himlRebar;
   HICON          hIcon;
   REBARBANDINFO  rbbi;
   HWND           hwndChild;
   RECT           rc;
   TCHAR          szString[64];

   //set up the ReBar
   himlRebar = ImageList_Create(32, 32, ILC_COLORDDB | ILC_MASK, 1, 0);
   hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_ICON1));

   ImageList_AddIcon(himlRebar, hIcon);

   rbi.cbSize  = sizeof(rbi);
   rbi.fMask   = RBIM_IMAGELIST;
   rbi.himl    = himlRebar;
   lResult = SendMessage(hwndRebar, RB_SETBARINFO, 0, (LPARAM)&rbi);

   //add a band that contains a combobox
   hwndChild = CreateWindowEx(   0,
                                 TEXT("combobox"),
                                 NULL,
                                 WS_VISIBLE |
                                    WS_CHILD |
                                    WS_TABSTOP |
                                    WS_VSCROLL |
                                    WS_CLIPCHILDREN |
                                    WS_CLIPSIBLINGS |
                                    CBS_AUTOHSCROLL |
                                    CBS_DROPDOWN |
                                    0,
                                 0,
                                 0,
                                 100,
                                 200,
                                 hwndRebar,
                                 (HMENU)ID_COMBOBOX,
                                 g_hInst,
                                 NULL);

   //add some stuff to the combobox
   {
   int   i;

   SendMessage(hwndChild, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(TRUE, 0));

   for(i = 0; i < 25; i++)
      {
      wsprintf(szString, TEXT("Item %d"), i + 1);
      SendMessage(hwndChild, CB_ADDSTRING, 0, (LPARAM)szString);
      }
   }

   GetWindowRect(hwndChild, &rc);

   ZeroMemory(&rbbi, sizeof(rbbi));
   rbbi.cbSize       = sizeof(REBARBANDINFO);
   rbbi.fMask        = RBBIM_SIZE |
                        RBBIM_CHILD |
                        RBBIM_CHILDSIZE |
                        RBBIM_ID |
                        RBBIM_STYLE |
                        RBBIM_TEXT |
                        RBBIM_BACKGROUND |
                        RBBIM_IMAGE |
                        0;

   rbbi.cxMinChild   = rc.right - rc.left;
   rbbi.cyMinChild   = rc.bottom - rc.top;
   rbbi.cx           = 100;
   rbbi.fStyle       = RBBS_CHILDEDGE |
                        RBBS_FIXEDBMP |
                        RBBS_GRIPPERALWAYS |
                        0;
   rbbi.wID          = ID_COMBOBOX;
   rbbi.hwndChild    = hwndChild;
   rbbi.lpText       = TEXT("ComboBox");
   rbbi.cch          = 2;
   rbbi.hbmBack      = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BACKGROUND));
   rbbi.iImage       = 0;

   lResult = SendMessage(hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)(LPREBARBANDINFO)&rbbi);

   //add a band that contains a button
   hwndChild = CreateWindowEx(   0,
                                 TEXT("button"),
                                 TEXT("Button"),
                                 WS_CHILD |
                                    BS_PUSHBUTTON |
                                    0,
                                 0,
                                 0,
                                 100,
                                 50,
                                 hwndRebar,
                                 (HMENU)ID_BUTTON,
                                 g_hInst,
                                 NULL);

   GetWindowRect(hwndChild, &rc);

   ZeroMemory(&rbbi, sizeof(rbbi));
   rbbi.cbSize       = sizeof(REBARBANDINFO);
   rbbi.fMask        = RBBIM_SIZE |
                        RBBIM_CHILD |
                        RBBIM_CHILDSIZE |
                        RBBIM_ID |
                        RBBIM_STYLE |
                        RBBIM_TEXT |
                        RBBIM_BACKGROUND |
                        0;
   rbbi.cxMinChild   = rc.right - rc.left;
   rbbi.cyMinChild   = rc.bottom - rc.top;
   rbbi.cx           = 100;
   rbbi.fStyle       = RBBS_CHILDEDGE |
                        RBBS_FIXEDBMP |
                        RBBS_GRIPPERALWAYS |
                        0;
   rbbi.wID          = ID_BUTTON;
   rbbi.hwndChild    = hwndChild;
   rbbi.lpText       = TEXT("Button");
   rbbi.hbmBack      = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BACKGROUND));

   lResult = SendMessage(hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)(LPREBARBANDINFO)&rbbi);
   }

MoveRebar(hwndParent);

return hwndRebar;
}

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

   DoNotify

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

LRESULT DoNotify(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
LPNMHDR           lpNM = (LPNMHDR)lParam;

switch(lpNM->code)
   {
   default:
      break;
   }

return FALSE;
}

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

   MoveRebar()

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

void MoveRebar(HWND hWnd)
{
RECT  rc,
      rcRebar;
int   x,
      y,
      cx,
      cy;

GetClientRect(hWnd, &rc);
GetWindowRect(GetDlgItem(hWnd, ID_REBAR), &rcRebar);

switch(g_wSide)
   {
   default:
   case TOP:
      //align the rebar along the top of the window
      x = 0;
      y = 0;
      cx = rc.right - rc.left;
      cy = rc.bottom - rc.top;
      break;

   case LEFT:
      //align the rebar along the left side of the window
      x = 0;
      y = 0;
      cx = rcRebar.right - rcRebar.left;
      cy = rc.bottom - rc.top;
      break;

   case BOTTOM:
      //align the rebar along the bottom of the window
      x = 0;
      y = rc.bottom - (rcRebar.bottom - rcRebar.top);
      cx = rc.right - rc.left;
      cy = rcRebar.bottom - rcRebar.top;
      break;

   case RIGHT:
      //align the coolbar along the right side of the window
      x = rc.right - (rcRebar.right - rcRebar.left);
      y = 0;
      cx = rcRebar.right - rcRebar.left;
      cy = rc.bottom - rc.top;
      break;
   }

MoveWindow( GetDlgItem(hWnd, ID_REBAR),
            x,
            y,
            cx,
            cy,
            TRUE);
}

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

   HandleMenuPopup()

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

LRESULT HandleMenuPopup(HMENU hMenu)
{
UINT  uSelect;

switch(g_wSide)
   {
   default:
   case TOP:
      uSelect = IDM_TOP;
      break;

   case LEFT:
      uSelect = IDM_LEFT;
      break;

   case BOTTOM:
      uSelect = IDM_BOTTOM;
      break;

   case RIGHT:
      uSelect = IDM_RIGHT;
      break;
   }

CheckMenuRadioItem(hMenu, IDM_TOP, IDM_BOTTOM, uSelect, MF_BYCOMMAND);

return 0;
}

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

   HandleCommand()

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

LRESULT HandleCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
switch(GET_WM_COMMAND_ID(wParam, lParam))
   {
   case ID_BUTTON:
      break;

   case ID_COMBOBOX:
      break;

   case IDM_TOP:
      if(g_wSide != TOP)
         {
         //destroy the existing Rebar
         DestroyWindow(GetDlgItem(hWnd, ID_REBAR));

         //change to the new side
         g_wSide = TOP;

         //create the new Rebar
         BuildRebar(hWnd);

         //we have to do this because the rebar will recalculate it's rectangle after the first time it is sized
         MoveRebar(hWnd);
         }
      break;

   case IDM_BOTTOM:
      if(g_wSide != BOTTOM)
         {
         //destroy the existing Rebar
         DestroyWindow(GetDlgItem(hWnd, ID_REBAR));

         g_wSide = BOTTOM;

         //create the new Rebar
         BuildRebar(hWnd);

         //we have to do this because the rebar will recalculate it's rectangle after the first time it is sized
         MoveRebar(hWnd);
         }
      break;

   case IDM_LEFT:
      if(g_wSide != LEFT)
         {
         //destroy the existing Rebar
         DestroyWindow(GetDlgItem(hWnd, ID_REBAR));

         g_wSide = LEFT;

         //create the new Rebar
         BuildRebar(hWnd);

         //we have to do this because the rebar will recalculate it's rectangle after the first time it is sized
         MoveRebar(hWnd);
         }
      break;

   case IDM_RIGHT:
      if(g_wSide != RIGHT)
         {
         //destroy the existing Rebar
         DestroyWindow(GetDlgItem(hWnd, ID_REBAR));

         g_wSide = RIGHT;

         //create the new Rebar
         BuildRebar(hWnd);

         //we have to do this because the rebar will recalculate it's rectangle after the first time it is sized
         MoveRebar(hWnd);
         }
      break;

   case IDM_EXIT:
      DestroyWindow(hWnd);
      break;

   case IDM_ABOUT:
      DialogBox(g_hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
      break;

   }

return TRUE;
}


RESOURCE.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by ReBar.rc
//
#define IDI_ICON1                       100
#define IDM_ABOUT                       100
#define IDD_ABOUTBOX                    101
#define IDM_EXIT                        101
#define IDB_BACKGROUND                  102
#define IDM_TOP                         102
#define IDB_LOGO                        103
#define IDM_LEFT                        103
#define IDM_GENERICMENU                 104
#define IDM_RIGHT                       104
#define IDM_BOTTOM                      105

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         107
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

Virtual ListView Control Sample "VListVw"

VListVw is a simple example of implementing a virtual ListView common control in an application. A virtual ListView is not a separate control, but a standard ListView with the LVS_OWNERDATA style. This example creates a ListView control that has the LVS_OWNERDATA style and "virtually" holds 100,000 items. The items are never actually added. Instead, the virtual ListView control is "told" how many items it contains with the LVM_SETITEMCOUNT message. When an item needs to be drawn, the ListView control queries the parent window for display information with the LVN_GETDISPINFO notification.

The ListView control can be placed in any of the four different view modes by selecting the desired view from the "View" menu. When the view is changed, the sample changes the style "on the fly" using the SetWindowLong API.

To close the application, simply select "Exit" from the "File" menu or click the close button on the caption bar.


RESOURCE.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by VListVw.rc
//
#define IDS_APPTITLE                    1
#define IDD_ABOUT                       100
#define IDI_MAINICON                    101
#define IDM_MAIN_MENU                   102
#define IDM_CONTEXT_MENU                103
#define IDI_DISK                        104
#define IDM_EXIT                        100
#define IDM_ABOUT                       101
#define IDM_LARGE_ICONS                 200
#define IDM_SMALL_ICONS                 201
#define IDM_LIST                        202
#define IDM_REPORT                      203

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         102
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           102
#endif
#endif


VLISTVW.C

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************
   Include Files
**************************************************************************/

#define STRICT

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "VListVw.h"

/**************************************************************************
   Local Function Prototypes
**************************************************************************/

#define ErrorHandler() ErrorHandlerEx(__LINE__, __FILE__)
void ErrorHandlerEx(WORD, LPSTR);

LRESULT ListViewNotify(HWND, LPARAM);
void SwitchView(HWND, DWORD);
BOOL DoContextMenu(HWND, WPARAM, LPARAM);
void UpdateMenu(HWND, HMENU);
BOOL InsertListViewItems(HWND);
void PositionHeader(HWND);

/**************************************************************************
   Global Variables
**************************************************************************/

HANDLE   g_hInst;
TCHAR    g_szClassName[] = TEXT("VListVwClass");

#define ITEM_COUNT   100000

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

   WinMain

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

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

g_hInst = hInstance;

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

//required to use the common controls
InitCommonControls();

/* Perform initializations that apply to a specific instance */

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

/* Acquire and dispatch messages until a WM_QUIT uMessage is received. */

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

return msg.wParam;
}

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

   InitApplication

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

BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASSEX  wcex;
ATOM        aReturn;

wcex.cbSize          = sizeof(WNDCLASSEX);
wcex.style           = 0;
wcex.lpfnWndProc     = (WNDPROC)MainWndProc;
wcex.cbClsExtra      = 0;
wcex.cbWndExtra      = 0;
wcex.hInstance       = hInstance;
wcex.hCursor         = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground   = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName    = MAKEINTRESOURCE(IDM_MAIN_MENU);
wcex.lpszClassName   = g_szClassName;
wcex.hIcon           = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_MAINICON));
wcex.hIconSm         = LoadImage(g_hInst, MAKEINTRESOURCE(IDI_MAINICON), IMAGE_ICON, 16, 16, 0);

aReturn = RegisterClassEx(&wcex);

if(0 == aReturn)
   {
   WNDCLASS wc;

   wc.style          = 0;
   wc.lpfnWndProc    = (WNDPROC)MainWndProc;
   wc.cbClsExtra     = 0;
   wc.cbWndExtra     = 0;
   wc.hInstance      = hInstance;
   wc.hIcon          = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_MAINICON));
   wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
   wc.lpszMenuName   = MAKEINTRESOURCE(IDM_MAIN_MENU);
   wc.lpszClassName  = g_szClassName;

   aReturn = RegisterClass(&wc);
   }

return aReturn;
}

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

   InitInstance

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

BOOL InitInstance(   HINSTANCE hInstance,
                     int nCmdShow)
{
HWND     hWnd;
TCHAR    szTitle[MAX_PATH] = "";

g_hInst = hInstance;

LoadString(g_hInst, IDS_APPTITLE, szTitle, sizeof(szTitle));

/* Create a main window for this application instance.  */
hWnd = CreateWindowEx(  0,
                        g_szClassName,
                        szTitle,
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL,
                        NULL,
                        hInstance,
                        NULL);

/* 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);
UpdateWindow(hWnd);
return TRUE;

}

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

   MainWndProc

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

LRESULT CALLBACK MainWndProc( HWND hWnd,
                              UINT uMessage,
                              WPARAM wParam,
                              LPARAM lParam)
{
static HWND hwndListView;

switch (uMessage)
   {
   case WM_CREATE:
      // create the TreeView control
      hwndListView = CreateListView(g_hInst, hWnd);

      //initialize the TreeView control
      InitListView(hwndListView);

      break;

   case WM_NOTIFY:
      return ListViewNotify(hWnd, lParam);

   case WM_SIZE:
      ResizeListView(hwndListView, hWnd);
      break;

   case WM_INITMENUPOPUP:
      UpdateMenu(hwndListView, GetMenu(hWnd));
      break;

   case WM_CONTEXTMENU:
      if(DoContextMenu(hWnd, wParam, lParam))
         return FALSE;
      break;

   case WM_COMMAND:
      switch (GET_WM_COMMAND_ID(wParam, lParam))
         {
         case IDM_LARGE_ICONS:
            SwitchView(hwndListView, LVS_ICON);
            break;

         case IDM_SMALL_ICONS:
            SwitchView(hwndListView, LVS_SMALLICON);
            break;

         case IDM_LIST:
            SwitchView(hwndListView, LVS_LIST);
            break;

         case IDM_REPORT:
            SwitchView(hwndListView, LVS_REPORT);
            break;

         case IDM_EXIT:
            DestroyWindow(hWnd);
            break;

         case IDM_ABOUT:
            DialogBox(g_hInst, MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutDlgProc);
            break;

         }
      break;

   case WM_DESTROY:
      PostQuitMessage(0);
      break;

   default:
      break;
   }
return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

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

   AboutDlgProc

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

BOOL CALLBACK AboutDlgProc(   HWND hDlg,
                              UINT uMessage,
                              WPARAM wParam,
                              LPARAM lParam)
{
switch (uMessage)
   {
   case WM_INITDIALOG:
      return TRUE;

   case WM_COMMAND:
      switch(wParam)
         {
         case IDOK:
            EndDialog(hDlg, IDOK);
            break;

         case IDCANCEL:
            EndDialog(hDlg, IDOK);
            break;
         }
      return TRUE;
    }

return FALSE;
}

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

   CreateListView

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

HWND CreateListView(HINSTANCE hInstance, HWND hwndParent)
{
DWORD       dwStyle;
HWND        hwndListView;
HIMAGELIST  himlSmall;
HIMAGELIST  himlLarge;
BOOL        bSuccess = TRUE;

dwStyle =   WS_TABSTOP |
            WS_CHILD |
            WS_BORDER |
            WS_VISIBLE |
            LVS_AUTOARRANGE |
            LVS_REPORT |
            LVS_OWNERDATA;

hwndListView = CreateWindowEx(   WS_EX_CLIENTEDGE,          // ex style
                                 WC_LISTVIEW,               // class name - defined in commctrl.h
                                 "",                        // dummy text
                                 dwStyle,                   // style
                                 0,                         // x position
                                 0,                         // y position
                                 0,                         // width
                                 0,                         // height
                                 hwndParent,                // parent
                                 (HMENU)ID_LISTVIEW,        // ID
                                 g_hInst,                   // instance
                                 NULL);                     // no extra data

if(!hwndListView)
   return NULL;

ResizeListView(hwndListView, hwndParent);

//set the image lists
himlSmall = ImageList_Create(16, 16, ILC_COLORDDB | ILC_MASK, 1, 0);
himlLarge = ImageList_Create(32, 32, ILC_COLORDDB | ILC_MASK, 1, 0);

if (himlSmall && himlLarge)
   {
   HICON hIcon;

   //set up the small image list
   hIcon = LoadImage(g_hInst, MAKEINTRESOURCE(IDI_DISK), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
   ImageList_AddIcon(himlSmall, hIcon);

   //set up the large image list
   hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_DISK));
   ImageList_AddIcon(himlLarge, hIcon);

   ListView_SetImageList(hwndListView, himlSmall, LVSIL_SMALL);
   ListView_SetImageList(hwndListView, himlLarge, LVSIL_NORMAL);
   }

return hwndListView;
}

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

   ResizeListView

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

void ResizeListView(HWND hwndListView, HWND hwndParent)
{
RECT  rc;

GetClientRect(hwndParent, &rc);

MoveWindow( hwndListView,
            rc.left,
            rc.top,
            rc.right - rc.left,
            rc.bottom - rc.top,
            TRUE);

//only call this if we want the LVS_NOSCROLL style
//PositionHeader(hwndListView);
}

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

   PositionHeader

   this needs to be called when the ListView is created, resized, the view is
   changed or a WM_SYSPARAMETERCHANGE message is recieved

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

void PositionHeader(HWND hwndListView)
{
HWND  hwndHeader = GetWindow(hwndListView, GW_CHILD);
DWORD dwStyle = GetWindowLong(hwndListView, GWL_STYLE);

/*To ensure that the first item will be visible, create the control without
the LVS_NOSCROLL style and then add it here*/
dwStyle |= LVS_NOSCROLL;
SetWindowLong(hwndListView, GWL_STYLE, dwStyle);

//only do this if we are in report view and were able to get the header hWnd
if(((dwStyle & LVS_TYPEMASK) == LVS_REPORT) && hwndHeader)
   {
   RECT        rc;
   HD_LAYOUT   hdLayout;
   WINDOWPOS   wpos;

   GetClientRect(hwndListView, &rc);
   hdLayout.prc = &rc;
   hdLayout.pwpos = &wpos;

   Header_Layout(hwndHeader, &hdLayout);

   SetWindowPos(  hwndHeader,
                  wpos.hwndInsertAfter,
                  wpos.x,
                  wpos.y,
                  wpos.cx,
                  wpos.cy,
                  wpos.flags | SWP_SHOWWINDOW);

   ListView_EnsureVisible(hwndListView, 0, FALSE);
   }
}

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

   InitListView

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

BOOL InitListView(HWND hwndListView)
{
LV_COLUMN   lvColumn;
int         i;
TCHAR       szString[5][20] = {"Main Column", "Column 1", "Column 2", "Column 3", "Column 4"};

//empty the list
ListView_DeleteAllItems(hwndListView);

//initialize the columns
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 120;
for(i = 0; i < 5; i++)
   {
   lvColumn.pszText = szString[i];
   ListView_InsertColumn(hwndListView, i, &lvColumn);
   }

InsertListViewItems(hwndListView);

return TRUE;
}

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

   InsertListViewItems

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

BOOL InsertListViewItems(HWND hwndListView)
{
//empty the list
ListView_DeleteAllItems(hwndListView);

//set the number of items in the list
ListView_SetItemCount(hwndListView, ITEM_COUNT);

return TRUE;
}

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

   ListViewNotify()

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

LRESULT ListViewNotify(HWND hWnd, LPARAM lParam)
{
LPNMHDR  lpnmh = (LPNMHDR) lParam;
HWND     hwndListView = GetDlgItem(hWnd, ID_LISTVIEW);

switch(lpnmh->code)
   {
   case LVN_GETDISPINFO:
      {
      LV_DISPINFO *lpdi = (LV_DISPINFO *)lParam;
      TCHAR szString[MAX_PATH];

      if(lpdi->item.iSubItem)
         {
         if(lpdi->item.mask & LVIF_TEXT)
            {
            wsprintf(szString, "Item %d - Column %d", lpdi->item.iItem + 1, lpdi->item.iSubItem);
            lstrcpy(lpdi->item.pszText, szString);
            }
         }
      else
         {
         if(lpdi->item.mask & LVIF_TEXT)
            {
            wsprintf(szString, "Item %d", lpdi->item.iItem + 1);
            lstrcpy(lpdi->item.pszText, szString);
            }

         if(lpdi->item.mask & LVIF_IMAGE)
            {
            lpdi->item.iImage = 0;
            }
         }
      }
      return 0;

   case LVN_ODCACHEHINT:
      {
      LPNMLVCACHEHINT   lpCacheHint = (LPNMLVCACHEHINT)lParam;
      /*
      This sample doesn't use this notification, but this is sent when the
      ListView is about to ask for a range of items. On this notification,
      you should load the specified items into your local cache. It is still
      possible to get an LVN_GETDISPINFO for an item that has not been cached,
      therefore, your application must take into account the chance of this
      occurring.
      */
      }
      return 0;

   case LVN_ODFINDITEM:
      {
      LPNMLVFINDITEM lpFindItem = (LPNMLVFINDITEM)lParam;
      /*
      This sample doesn't use this notification, but this is sent when the
      ListView needs a particular item. Return -1 if the item is not found.
      */
      }
      return 0;
   }

return 0;
}

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

   ErrorHandlerEx()

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

void ErrorHandlerEx( WORD wLine, LPSTR lpszFile )
{
LPVOID lpvMessage;
DWORD  dwError;
char szBuffer[256];

// Allow FormatMessage() to look up the error code returned by GetLastError
dwError = FormatMessage(   FORMAT_MESSAGE_ALLOCATE_BUFFER |
                              FORMAT_MESSAGE_FROM_SYSTEM,
                           NULL,
                           GetLastError(),
                           MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
                           (LPTSTR)&lpvMessage,
                           0,
                           NULL);

// Check to see if an error occured calling FormatMessage()
if (0 == dwError)
   {
   wsprintf(   szBuffer,
               "An error occured calling FormatMessage()."
               "Error Code %d",
               GetLastError());
   MessageBox( NULL,
               szBuffer,
               "Generic",
               MB_ICONSTOP | MB_ICONEXCLAMATION);
   return;
   }

// Display the error infromation along with the place the error happened.
wsprintf(szBuffer, "Generic, Line=%d, File=%s", wLine, lpszFile);
MessageBox(NULL, lpvMessage, szBuffer, MB_ICONEXCLAMATION | MB_OK);
}

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

   SwitchView()

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

void SwitchView(HWND hwndListView, DWORD dwView)
{
DWORD dwStyle = GetWindowLong(hwndListView, GWL_STYLE);

SetWindowLong(hwndListView, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | dwView);
ResizeListView(hwndListView, GetParent(hwndListView));
}

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

   DoContextMenu()

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

BOOL DoContextMenu(  HWND hWnd,
                     WPARAM wParam,
                     LPARAM lParam)
{
HWND  hwndListView = (HWND)wParam;
HMENU hMenuLoad,
      hMenu;

if(hwndListView != GetDlgItem(hWnd, ID_LISTVIEW))
   return FALSE;

hMenuLoad = LoadMenu(g_hInst, MAKEINTRESOURCE(IDM_CONTEXT_MENU));
hMenu = GetSubMenu(hMenuLoad, 0);

UpdateMenu(hwndListView, hMenu);

TrackPopupMenu(   hMenu,
                  TPM_LEFTALIGN | TPM_RIGHTBUTTON,
                  LOWORD(lParam),
                  HIWORD(lParam),
                  0,
                  hWnd,
                  NULL);

DestroyMenu(hMenuLoad);

return TRUE;
}

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

   UpdateMenu()

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

void UpdateMenu(HWND hwndListView, HMENU hMenu)
{
UINT  uID;
DWORD dwStyle;

//uncheck all of these guys
CheckMenuItem(hMenu, IDM_LARGE_ICONS,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_SMALL_ICONS,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_LIST,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_REPORT,  MF_BYCOMMAND | MF_UNCHECKED);

//check the appropriate view menu item
dwStyle = GetWindowLong(hwndListView, GWL_STYLE);
switch(dwStyle & LVS_TYPEMASK)
   {
   case LVS_ICON:
      uID = IDM_LARGE_ICONS;
      break;

   case LVS_SMALLICON:
      uID = IDM_SMALL_ICONS;
      break;

   case LVS_LIST:
      uID = IDM_LIST;
      break;

   case LVS_REPORT:
      uID = IDM_REPORT;
      break;
   }
CheckMenuRadioItem(hMenu, IDM_LARGE_ICONS, IDM_REPORT, uID,  MF_BYCOMMAND | MF_CHECKED);

}


VLISTVW.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

#include "resource.h"

#ifndef WIN32

#define GET_WM_COMMAND_ID(wp, lp)               (wp)
#define GET_WM_COMMAND_HWND(wp, lp)             (HWND)(LOWORD(lp))
#define GET_WM_COMMAND_CMD(wp, lp)              HIWORD(lp)

#endif

int PASCAL WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
HWND CreateListView(HINSTANCE, HWND);
void ResizeListView(HWND, HWND);
BOOL InitListView(HWND);

#define ID_LISTVIEW  2000


README.TXT

Console I/O Combined with GUI I/O

SUMMARY
=======

The ConGUI sample demonstrates how a standard console application can take
advantage of some of the graphical capabilities of Windows NT.

Disclaimer:

This sample application illustrates a method by which a console application
can utilize some (but not all) of the graphical user interface (GUI)
capabilities of the Win32 API. It was not the original design intention of
the Console layer of Windows NT to allow it to interact in this manner
with the graphical API, and because of this, it is possible to run into
problems if you try to accomplish too much.

Please use restraint in your usage of the graphical Win32 API in your
console application. If your needs go beyond the simple methods
illustrated here, you should probably consider designing your application
as a full GUI application.

MORE INFORMATION
================

There are many applications that have nothing to gain by implementing full-
fledged GUI with title bars, menus, scrollable client area, and so forth.
However, a console application can take advantage of GUI features.

Take the example of a compiler. A compiler has no need for menus, button
bars, or many of the other UI aspects of a standard Windows application.
However, you may want your compiler to provide on-line assistance with the
command-line switches that the compiler supports.

ConGUI illustrates the usage of a '/dialog' parameter, which brings up a
dialog box that assists the user in selecting parameters for the command
line. To provide a little more information about some of those switches,
allow the user to access a Windows help file. ConGUI shows how easy it is
for a console application to make standard WinHelp calls to access the
Windows help engine.

In addition, ConGUI shows how it is possible for a console application to
figure out how it was launched. It can be useful for a text application to
know that it was being launched into its own window, because when it closes,
the window into which it has written all of its output is closed as well.

Try the following methods of executing ConGUI, and notice how it behaves:

  - Double-click on the CONGUI.EXE from the File Manager or Windows
    Explorer.

  - Add ConGUI to a group in the Program Manager or create a short-cut, and
    double-click on the icon.

  - From a command prompt, type:

      CONGUI

  - From a command prompt, type:

      START CONGUI

  - From a command prompt, type:

      CONGUI > TEMP.TXT


CONGUI.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define T_USAGE   10
#define T_HELP    20
#define T_DIALOG  30


CONSOLE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// CONSOLE.C
// ================================================================
// This module contains the standard console functions for this
// applicaiton. All of the 'graphical' functions are contained in
// GUI.C

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <windows.h>
#include "ConGUI.h"

void usage (void);
static void DoArgs(int *pargc, char **pargv[]);
void ListArgs (int argc, char **argv);

extern int GetDialogArgs (char ***pargv);
extern int DoHelp (char *szHelpTopic);

BOOL bDoHelp = FALSE;
BOOL bDoDialog = FALSE;
BOOL bDoUsage = FALSE;

int main(int argc, char **argv)

{
    char szAppName[80];
    char **dargv;

    HANDLE hConsoleOutput;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    BOOL bLaunched;

    // Lets try a trick to determine if we were 'launched' as a seperate
    // screen, or just running from the command line.
    // We want to do this so that when we exit, we can prompt the user
    // before we shut down if we were 'launched'. Otherwise, any data on
    // the output window will be lost.
    // We will do this by simply getting the current cursor position. It
    // 'should' always be (0,0) on a launch, and something else if we were
    // executed as a command from a console window. The only time I can see
    // this as not working, is if the user executed a CLS and appended
    // our program with the '&' character, as in:
    //   C:\> CLS & ConGUI
    // This will also result in a (0,0) cursor pos, but in this case, the
    // user might also be wanting us to 'prompt' before closeing.
    // We also need to handle the case of:
    //   C:\> ConGUI > output.dat

    hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
    bLaunched = ((csbi.dwCursorPosition.X==0) && (csbi.dwCursorPosition.Y==0));
    if ((csbi.dwSize.X<=0) || (csbi.dwSize.Y <= 0)) bLaunched = FALSE;

//    printf ("Window Size: (%i, %i)\n",csbi.dwSize.X, csbi.dwSize.Y);
//    printf ("Cursor Pos : (%i, %i)\n",csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y);

    if (argc <= 1) { // Nothing on the command line, give brief usage info
        usage();
        if (bLaunched) {
            printf ("------------------------------------------\n");
            printf ("Program Finished - Hit <Enter> to continue");
            getchar();
        }
        return(0);
    }

    // Get the application name off of the command line.
    strcpy (szAppName, argv[0]); // this should always be there.
    argc--;
    argv++;

    // Get the rest of the arguments
    DoArgs (&argc, &argv);

    // And handle anything they told us to do
    if (bDoUsage) {         // Simple Usage Info
        usage();
    } else if (bDoHelp) {   // Robust Help
        DoHelp ("Contents");
    } else if (bDoDialog) { // Dialog
        dargv = (char **)GlobalAlloc (GPTR, 10*sizeof(char*));
        argc = GetDialogArgs(&dargv);
        if (argc > 0) {
            ListArgs (argc, dargv);
            DoArgs (&argc, &dargv);
        } else {
            printf ("\nUser Selected 'Cancel' in the dialog\n");
        }
    }

    if (bLaunched) {
        printf ("------------------------------------------\n");
        printf ("Program Finished - Hit <Enter> to continue");
        getchar();
    }

    return(0);
}

void usage(void)
{
    printf ("\nConGUI Usage information:\n");
    printf ("-------------------------\n\n");
    printf ("    -?       To print usage information\n");
    printf ("    -help    To bring up WinHelp for this application\n");
    printf ("    -dialog  To bring up a dialog to prompt for parameters\n");

}

void ListArgs (int argc, char **argv)
{
    printf ("\nCommand Line:\n    ConGUI ");
    while (argc--) {
        printf ("%s ", *argv);
        argv++;
    }
    printf ("\n");
}

typedef struct tagToken {
    int id;
    char str[25];
} Token;

Token params[] = {
    { T_USAGE,   "?"},
    { T_HELP,    "Help"},
    { T_DIALOG,  "Dialog"},
    { 0, 0}

};

// Check the option entered on the command line against the tokens
// we are expecting. The capital letters of the token represent the
// minimum amount of the token that needs to be provided. The lower
// case letters 'can' be provided, and if provided, must match.
BOOL TokensMatch (char *token, char *mask)
{
    int lToken, lMask, index;
    char *pchToken, *pchMask;

    lToken = strlen (token);
    lMask = strlen (mask);

    if (lToken > lMask) return FALSE;

    pchToken = &token[0];
    pchMask = &mask[0];

    index = 0;
    while (*pchToken) {
        if (toupper(*pchToken) != toupper(*pchMask)) return FALSE;

        pchToken++;
        pchMask++;
        index++;
    }

    if (index == lMask) return TRUE;

    if (islower(*pchMask)) return TRUE;

    return FALSE;

}

// Return the id of the specified token. Return -1 if no match.
int GetTokenID (char *token)
{
    int id = 0;
    int index = 0;

    while (params[index].id) {
        if (TokensMatch (token, params[index].str)) {
            return params[index].id;
        }
        ++index;
    }

    return -1;
}

// Parse the argument list, looking for valid keywords
static void DoArgs(int *pargc, char **pargv[])
{
    int argc, id;
    char **argv;
    char *p;

    argc = *pargc;
    argv = *pargv;

    p = argv[0];

    while (argc) {

        if (*p == '/' || (*p=='-' && *(p+1)!='\0')) {
            id = GetTokenID (&(p[1]));
            switch (id) {
                case -1 :
                    //printf ("Invalid Token: %s\n", &(p[1]));
                    break;

                case T_USAGE:
                    bDoUsage = TRUE;
                    break;

                case T_HELP:
                    bDoHelp = TRUE;
                    break;

                case T_DIALOG:
                    bDoDialog = TRUE;
                    break;

                default:
                    printf ("Found Token: %s\n", &(p[1]));
                    break;
            }
        } else {
            printf ("Unexpected keyword : %s\n", p);

        }

        argc--;
        argv++;
        p = argv[0];
    }

    *pargv = argv;
    *pargc = argc;
}


GUI.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// GUI.C
// ================================================================
// This module contains all of the functions that interface to the
// 'graphical' part of this program. This currently only relates to
// the code that brings up the dialog box, and calls the WinHelp
// engine.

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <windows.h>
#include "ConGUI.h"

int DoHelp (char *szHelpTopic);
int GetDialogArgs (char ***pargv);
BOOL CenterWindow (HWND hwnd);
BOOL APIENTRY CLDlgProc (HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);

// Use WINHELP to bring up the applicaiton help file
int DoHelp (char *szHelpTopic)
{
    WinHelp (GetFocus(), "ConGUI.HLP", HELP_KEY, (DWORD)(LPSTR)szHelpTopic);
    return TRUE;
}

// Bring up the dialog box, and pass back a 'command line' as was
// specified by the user
int GetDialogArgs (char ***pargv)
{
    int ret;
    HANDLE hinst;
    HWND hwnd;
    char szFile[80];

    hinst = GetModuleHandle (NULL);
    hwnd = GetFocus();

    ret = DialogBoxParam (hinst, "CL", NULL, CLDlgProc, (LPARAM)pargv);

    if (-1 == ret) {
        ret = GetLastError();
        printf ("Unable to create dialog: %d\n", ret);
        GetModuleFileName (hinst, szFile, sizeof(szFile));
        printf ("hinst = %d\n", hinst);
        printf ("hwnd = %d\n", hwnd);
        printf ("File = %s\n", szFile);
        return FALSE;

    }
    return ret;
}

// A quick little routine that will center a window on the screen.
// Handy for dialog boxes
BOOL CenterWindow (HWND hwnd)
{
    RECT    rect;
    int     w, h;
    int     wScreen, hScreen, xNew, yNew;
    HDC     hdc;

    GetWindowRect (hwnd, &rect);
    w = rect.right - rect.left;
    h = rect.bottom - rect.top;

    hdc = GetDC (hwnd);
    wScreen = GetDeviceCaps (hdc, HORZRES);
    hScreen = GetDeviceCaps (hdc, VERTRES);
    ReleaseDC (hwnd, hdc);

    xNew = wScreen/2 - w/2;
    yNew = hScreen/2 - h/2;

    return SetWindowPos (hwnd, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}

// Create a data structure that will hold the strings for the combo boxes
// we have in our dialog. This just illustrates 'a' way to do this, not
// necessarily the best.

typedef struct tagDlgCtrls {
    int ctrlId;
    int def;
    char str[25];
    char opt[5];
} DlgCtrls;

DlgCtrls dlgctrls[] = {
    { 415, FALSE, "DOS EXE", "..." },
    { 415, FALSE, "Windows 3.0 EXE", "..."  },
    { 415, FALSE, "Windows 3.0 DLL", "..."  },
    { 415, FALSE, "Windows 3.1 EXE", "..."  },
    { 415, FALSE, "Windows 3.1 DLL", "..."  },
    { 415, TRUE,  "Windows NT EXE", "..."  },
    { 415, FALSE, "Windows NT DLL", "..."  },
    { 415, FALSE, "Windows NT Console App", "..."  },

    { 402, FALSE, "Small", "AS" },
    { 402, FALSE, "Medium", "AM" },
    { 402, FALSE, "Compact", "AC" },
    { 402, TRUE,  "Large", "AL" },
    { 402, FALSE, "Huge", "AH" },
    { 402, FALSE, "Customize", "A?" },

    { 404, FALSE, "8086", "G0" },
    { 404, FALSE, "80186", "G1" },
    { 404, TRUE,  "80286", "G2" },
    { 404, FALSE, "80386", "G3" },
    { 404, FALSE, "80486", "G4" },

    { 406, TRUE,  "stdcall", "Gz" },
    { 406, FALSE, "Pascal", "Gc" },
    { 406, FALSE, "C", "Gd" },

    { 408, FALSE, "Level 0", "W0" },
    { 408, FALSE, "Level 1", "W1" },
    { 408, FALSE, "Level 2", "W2" },
    { 408, TRUE,  "Level 3", "W3" },
    { 408, FALSE, "Level 4", "W4" },

    { 411, FALSE, "None", "" },
    { 411, FALSE, "Line Numbers Only", "Zd" },
    { 411, TRUE,  "Full Information", "Zi" },

    { 418, FALSE, "Ansi C", "Za" },
    { 418, TRUE, "MS Extensions", "Ze" },

    { 413, FALSE, "None", "" },
    { 413, TRUE,  "Protect Mode App", "GA" },
    { 413, FALSE, "Protect Mode DLL", "GD" },

    { 0, 0}  // End Of List
};

BOOL APIENTRY CLDlgProc (HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    int wmId;
    static char ***pargv;
    static char **argv;

    int i, item, index, iCtrl, argc;
    char *cmd;
    char *cmdline;

    switch (msg) {
        case WM_INITDIALOG:
            // We need to initialize stuff in the dialog box...

            pargv = (char ***)lParam;
            argv = *pargv;
            CenterWindow (hdlg);

            iCtrl = i = 0;
            while (dlgctrls[i].ctrlId) {
                if (dlgctrls[i].ctrlId != iCtrl) { // Starting a new list
                    iCtrl = dlgctrls[i].ctrlId;
                }
                index = SendDlgItemMessage (hdlg, iCtrl, CB_ADDSTRING, 0, (DWORD)(LPSTR)dlgctrls[i].str);
                SendDlgItemMessage (hdlg, iCtrl, CB_SETITEMDATA, index, i);
                if (dlgctrls[i].def) {
                    SendDlgItemMessage (hdlg, dlgctrls[i].ctrlId, CB_SETCURSEL, index, 0);
                }
                i++;
            }
            return (TRUE);

        case WM_DESTROY:
            break;

        case WM_COMMAND:
            wmId = LOWORD(wParam);
            switch (wmId) {

                case T_HELP:
                    DoHelp ("Contents");
                    break;

                case IDOK:
                    cmd = cmdline = (char *)GlobalAlloc (GPTR, 128);
                    argv[0] = cmdline;
                    argc = 0;

                    if (cmdline) {
                        iCtrl = i = 0;
                        while (dlgctrls[i].ctrlId) {
                            if (dlgctrls[i].ctrlId != iCtrl) {
                                iCtrl = dlgctrls[i].ctrlId;
                                index = SendDlgItemMessage(hdlg, iCtrl, CB_GETCURSEL, 0, 0);
                                if (index) {
                                    item = SendDlgItemMessage (hdlg, iCtrl, CB_GETITEMDATA, index, 0);
                                    wsprintf ((LPSTR)cmd, "-%s", (LPSTR)dlgctrls[item].opt);
                                    cmd += strlen(cmd);
                                    cmd[0] = 0;
                                    argv[++argc] = ++cmd;
                                }
                            }
                            i++;
                        }

                    } // if (cmdline)...

                    EndDialog(hdlg, argc);
                    return (TRUE);

                case IDCANCEL:
                    EndDialog(hdlg, 0);
                    return (TRUE);
            }
            break;
    }
    return (FALSE);

    lParam; // unreferenced formal parameter
}


README.TXT

Demonstration of the Console Functions

The CONSOLE sample demonstrates most of the Win32 console functions. The
program takes no parameters. After starting the sample, you can click one
of the functions on the screen to get a demonstration of that function.
When viewing a demo of the function, the title of the Console window is
changed to show the name of the source file where that demo function
resides. This should make it easy to find the sample code where the
function of interest resides.

Please note that some of the demos cover multiple console functions, so
some of the choices run the same demo.


CODEPAGE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include "console.h"

/* Note: demos for Get/SetConsoleCP not implemented */

/*********************************************************************
* FUNCTION: showCP(HANDLE hConOut, UINT uiCP)                        *
*                                                                    *
* PURPOSE: dump the 256 character set in the given codepage          *
*                                                                    *
* INPUT: the console output handle to use and the desired code page  *
*        to display the characters in                                *
*********************************************************************/

void showCP(HANDLE hConOut, UINT uiCP)
{
  BOOL bSuccess;
  DWORD dwSaveMode; /* store the current console mode to restore later */
  UINT uiSaveCP; /* store the current code page to restore later */
  int i;
  DWORD dwWritten;

  /* save the current output mode and code page */
  uiSaveCP = GetConsoleOutputCP();
  bSuccess = GetConsoleMode(hConOut, &dwSaveMode);
  PERR(bSuccess, "GetconsoleMode");
  /* turn off processed output so that all chars will display */
  bSuccess = SetConsoleMode(hConOut, dwSaveMode & ~ENABLE_PROCESSED_OUTPUT);
  PERR(bSuccess, "SetConsoleMode");
  bSuccess = SetConsoleOutputCP(uiCP);
  PERR(bSuccess, "SetConsoleOutputCP");
  for (i = 0; i < 256; i++)
    WriteConsole(hConOut, &i, 1, &dwWritten, NULL);
  /* now reset to original code page and output mode */
  SetConsoleOutputCP(uiSaveCP);
  bSuccess = SetConsoleMode(hConOut, dwSaveMode);
  PERR(bSuccess, "SetConsoleMode");
  return;
}

/*********************************************************************
* FUNCTION: demoOutputCodePage(HANDLE hConOut)                       *
*                                                                    *
* PURPOSE: demonstrate SetConsoleOutputCP and GetConsoleOutputCP.    *
*          Dump the 256 character set for each code paged installed  *
*          in the system.                                            *
*                                                                    *
* INPUT: the console output handle to manipulate the codepage for    *
*********************************************************************/

void demoOutputCodePage(HANDLE hConOut)
{
  DWORD iValue = 0; /* index value for RegEnumValue */
  LONG lSuccess; /* registry API return code */
  HKEY hKey; /* handle to registry key containing codepage info */
  char szValue[16], szData[16]; /* value and data info from registry */
  DWORD dwType, cchValue, cbData;
  UINT uiCP; /* the code page number that we extract from the registry */
  char szTemp[128];
  BOOL bSuccess;
  OSVERSIONINFO osVer; /* for GetVersionEx() */

  /* check if Win95, if so, display notice and return - the console codepage */
  /* APIs are not supported under Win95. */
  osVer.dwOSVersionInfoSize = sizeof(osVer);
  bSuccess = GetVersionEx(&osVer);
  PERR(bSuccess, "GetVersionEx");
  if (osVer.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
    {
    MessageBox(NULL,
        "The console codepage APIs are not supported on Windows 95",
        "Console API Test Application", MB_OK );
    return;
    }
  setConTitle(__FILE__);
  /* set the color for future text output */
  bSuccess = SetConsoleTextAttribute(hConOut, FOREGROUND_YELLOW |
      FOREGROUND_INTENSITY | BACKGROUND_BLUE);
  PERR(bSuccess, "SetConsoleTextAttribute");
  cls(hConOut); /* clear screen to new attributes */
  myPuts(hConOut, "Warning: code page info will not display properly unless you are\n"
         "using a TrueType font in the console. From the system menu, select\n"
         "Fonts, then select a TrueType font from the list available.");
  /* open the key containing all the code page info */
  lSuccess = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
    "SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage", 0, KEY_QUERY_VALUE,
    &hKey);
  PERR(lSuccess == ERROR_SUCCESS, "RegOpenKeyEx");
  while(TRUE)
  {
    cchValue = sizeof(szValue);
    cbData = sizeof(szData);
    /* get the next code page entry */
    lSuccess = RegEnumValue(hKey, iValue++, szValue, &cchValue, NULL,
      &dwType, szData, &cbData);
    if (lSuccess == ERROR_NO_MORE_ITEMS)
      break;
    if (!szData[0]) /* if value data is NULL, it's an invalid code page; skip */
      continue;
    if (!(uiCP = atoi(szValue))) /* if value name is not a number, skip */
      continue;
    sprintf(szTemp, "\n\nCodepage: %s", szValue);
    myPuts(hConOut, szTemp);
    /* set code page and display all 256 chars on the console */
    showCP(hConOut, uiCP);
  }
  myPuts(hConOut, "\n\nHit enter to continue...");
  myGetchar();
  return;
}

/*********************************************************************
* FUNCTION: demoInputCodePage(HANDLE hConOut)                        *
*                                                                    *
* PURPOSE: demonstrate SetConsoleInputCP and GetConsoleInputCP.      *
*          Read and display keyboard input, including special chars. *
*                                                                    *
* INPUT: the console output handle to manipulate the codepage for    *
*********************************************************************/

void demoInputCodePage(HANDLE hConOut)
{
  myPuts(hConOut, "SetConsoleCP and GetConsoleCP demos");
  myPuts(hConOut, "not implemented yet.");
  myPuts(hConOut, "\n\nHit enter to continue...");
  myGetchar();
  return;
}


CONINFO.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include "console.h"

/*******************************************************************
* FUNCTION: demoConInfo(HANDLE hConOut)                            *
*                                                                  *
* PURPOSE: demonstrate GetConsoleScreenBufferInfo. Get the current *
*          console screen buffer information and display it on the *
*          console.                                                *
*                                                                  *
* INPUT: console handle to output to                               *
*******************************************************************/

void demoConInfo(HANDLE hConOut)
{
  BOOL bSuccess;
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  CHAR szTemp[128];

  setConTitle(__FILE__);
  myPuts(hConOut, "For information on the console buffer, we need to call\n"
                  "GetConsoleScreenBufferInfo. Make any adjustments to the\n"
                  "screen buffer/console window now, then hit return...\n");
  myGetchar();
  bSuccess = GetConsoleScreenBufferInfo(hConOut, &csbi);
  PERR(bSuccess, "GetConsoleScreenBufferInfo");
  myPuts(hConOut, "\nHere is the information on the buffer returned in the\n"
                  "CONSOLE_SCREEN_BUFFER_INFO structure:");
  sprintf(szTemp, "Size: X = %d, Y = %d", csbi.dwSize.X, csbi.dwSize.Y);
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "Cursor pos: X = %d, Y = %d", csbi.dwCursorPosition.X,
      csbi.dwCursorPosition.Y);
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "Character display attribute: 0x%04x", csbi.wAttributes);
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "Window position: Left = %d, Right = %d, Top = %d, "
      "Bottom = %d", csbi.srWindow.Left, csbi.srWindow.Top,
      csbi.srWindow.Right, csbi.srWindow.Bottom);
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "Maximum window size: X = %d, Y = %d",
      csbi.dwMaximumWindowSize.X, csbi.dwMaximumWindowSize.Y);
  myPuts(hConOut, szTemp);
  myPuts(hConOut, "\nHit any key to return...");
  myGetchar();
  return;
}


CONMODE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "console.h"

/*********************************************************************
* FUNCTION: rawOut(HANDLE hConsole, PCHAR s)                         *
*                                                                    *
* PURPOSE: output the contents of the input buffer to the console    *
*          handle in 'raw' mode                                      *
*                                                                    *
* INPUT: the console handle to write to, and the string to output in *
*        'raw' mode                                                  *
*********************************************************************/

void rawOut(HANDLE hConsole, PCHAR s)
{
  BOOL bSuccess;
  DWORD cCharsWritten;
  DWORD dwOutputMode;

  bSuccess = GetConsoleMode(hConsole, &dwOutputMode);
  PERR(bSuccess, "GetconsoleMode");
  /* output the string in raw mode. */
  /* turn off processed output, output the string, and reset output mode */
  bSuccess = SetConsoleMode(hConsole, dwOutputMode & ~ENABLE_PROCESSED_OUTPUT);
  PERR(bSuccess, "SetConsoleMode");
  bSuccess = WriteFile(hConsole, s, strlen(s), &cCharsWritten, NULL);
  PERR(bSuccess, "WriteFile");
  /* now reset output mode */
  bSuccess = SetConsoleMode(hConsole, dwOutputMode);
  PERR(bSuccess, "SetConsoleMode");
  return;
}

/******************************************************************
* FUNCTION: demoConMode(HANDLE hConOut)                           *
*                                                                 *
* PURPOSE: demonstrate GetConsoleMode and SetConsoleMode. Display *
*          the current console input and output modes, then       *
*          demonstrate each console input and output mode         *
*                                                                 *
*                                                                 *
* INPUT: the console output handle to write to                    *
******************************************************************/

void demoConMode(HANDLE hConOut)
{
  BOOL bSuccess;
  DWORD dwOutputMode, dwInputMode; /* save input & output console modes */
  CHAR szTemp[128];
  HANDLE hStdIn; /* standard input handle */
  /* a string to test 'raw' vs. 'processed' output with */
  PCHAR szModeTst = "tab:\t backspace:\b linefeed:\n bell:\a cr:\r";
  DWORD dwRead;
  CHAR chBuf[256]; /* buffer to read a user string from the console */
  PCHAR szLong = "This is a line of text that is greater than 80 characters wide. This is a test of ENABLE_WRAP_AT_EOL_OUTPUT.";

  setConTitle(__FILE__);
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  /* get the input mode and save it so that we can restore it later */
  bSuccess = GetConsoleMode(hStdIn, &dwInputMode);
  PERR(bSuccess, "GetconsoleMode");
  myPuts(hConOut, "Current console input modes, as reported by GetconsoleMode:");
  sprintf(szTemp, "ENABLE_LINE_INPUT: %s", dwInputMode & ENABLE_LINE_INPUT ?
      "on" : "off");
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "ENABLE_ECHO_INPUT: %s", dwInputMode & ENABLE_ECHO_INPUT ?
      "on" : "off");
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "ENABLE_WINDOW_INPUT: %s", dwInputMode &
      ENABLE_WINDOW_INPUT ? "on" : "off");
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "ENABLE_PROCESSED_INPUT: %s", dwInputMode &
      ENABLE_PROCESSED_INPUT ? "on" : "off");
  myPuts(hConOut, szTemp);
  bSuccess = GetConsoleMode(hConOut, &dwOutputMode);
  PERR(bSuccess, "GetconsoleMode");
  myPuts(hConOut, "\nCurrent console output modes:");
  sprintf(szTemp, "ENABLE_PROCESSED_OUTPUT: %s", dwOutputMode &
      ENABLE_PROCESSED_OUTPUT ? "on" : "off");
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "ENABLE_WRAP_AT_EOL_OUTPUT: %s", dwOutputMode &
      ENABLE_WRAP_AT_EOL_OUTPUT ? "on" : "off");
  myPuts(hConOut, szTemp);
  bSuccess = SetConsoleMode(hConOut, dwOutputMode);  /* back to normal */
  PERR(bSuccess, "SetConsoleMode");
  myPuts(hConOut, "\nHit enter to continue...");
  myGetchar();
  cls(hConOut);
  myPuts(hConOut, "\nLet's test the console output modes with SetConsoleMode.\n"
                  "First we'll output a long line of text with\n"
                  "ENABLE_WRAP_AT_EOL_OUTPUT enabled, then with it disabled:\n");
  myPuts(hConOut, szLong);
  myPuts(hConOut, "\nNow we'll disable ENABLE_WRAP_AT_EOL_OUTPUT and display the same\n"
                  "string. Note how the wrapped line does NOT get displayed\n"
                  "on the next line but on the same line:\n");
  /* turn off EOL wrap */
  bSuccess = SetConsoleMode(hConOut, dwOutputMode &
      ~ENABLE_WRAP_AT_EOL_OUTPUT);
  PERR(bSuccess, "SetConsoleMode");
  myPuts(hConOut, szLong);
  /* turn on EOL wrap */
  bSuccess = SetConsoleMode(hConOut, dwOutputMode |
      ENABLE_WRAP_AT_EOL_OUTPUT);
  PERR(bSuccess, "SetConsoleMode");
  myPuts(hConOut, "\n\nNow let's test processed output. We'll output a string\n"
                  "with ENABLE_PROCESSED_OUTPUT disabled, then with it enabled.\n"
                  "Note how the backspace, tab, bell, carriage return, and\n"
                  "linefeed characters are processed differently:\n");
  /* turn off processed ("cooked") output - now in "raw" mode */
  bSuccess = SetConsoleMode(hConOut, dwOutputMode & ~ENABLE_PROCESSED_OUTPUT);
  PERR(bSuccess, "SetConsoleMode");
  /* myPuts() appends a \n, so can't use it for this case */
  bSuccess = WriteFile(hConOut, szModeTst, strlen(szModeTst), &dwRead, NULL);
  PERR(bSuccess, "WriteFile");
  /* turn processed ("cooked") mode back on */
  bSuccess = SetConsoleMode(hConOut, dwOutputMode | ENABLE_PROCESSED_OUTPUT);
  PERR(bSuccess, "SetConsoleMode");
  myPuts(hConOut, "\nNow we'll set ENABLE_PROCESSED_OUTPUT and output the same\n"
                  "string:\n");
  myPuts(hConOut, szModeTst);
  myPuts(hConOut, "\nHit enter for the next test...");
  myGetchar();
  cls(hConOut);
  myPuts(hConOut, "Now let's test the input modes. First you'll input\n"
                  "an input string with ENABLE_PROCESSED_INPUT enabled.\n"
                  "We'll output exactly what was read in this mode.\n"
                  "Note how characters such as backspace, tab, ctrl+g,\n"
                  "carriage return and line feed are interpreted.\n"
                  "Enter a line of text, end by hitting enter:");
  /* turn on processed, line, and echo modes. */
  /* MUST turn on echo mode when turning on line mode */
  bSuccess = SetConsoleMode(hStdIn, dwInputMode | ENABLE_PROCESSED_INPUT |
      ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  memset(chBuf, 0, sizeof(chBuf));
  bSuccess = ReadFile(hStdIn, chBuf, sizeof(chBuf), &dwRead, NULL);
  PERR(bSuccess, "ReadFile");
  /* output the contents of chBuf in raw mode */
  rawOut(hConOut, chBuf);
  myPuts(hConOut, "\nNow let's disable ENABLE_PROCESSED_INPUT. Enter another\n"
                  "input string:");
  /* turn off processed, line, and echo input. */
  /* MUST turn off echo input when turning off line input */
  bSuccess = SetConsoleMode(hStdIn, dwInputMode & ~ENABLE_PROCESSED_INPUT |
      ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  memset(chBuf, 0, sizeof(chBuf));
  bSuccess = ReadFile(hStdIn, chBuf, sizeof(chBuf), &dwRead, NULL);
  PERR(bSuccess, "ReadFile");
  rawOut(hConOut, chBuf);
  myPuts(hConOut, "\nHit enter to continue...");
  myGetchar();
  cls(hConOut);
  myPuts(hConOut, "\n\nLet's turn off ENABLE_LINE_INPUT - all of our input\n"
                  "previously has been line-input. We must also disable\n"
                  "ENABLE_ECHO_INPUT when disabling this flag. Let's read\n"
                  "a single character from the console and we will return\n"
                  "immediately with the charater read. Note that the character\n"
                  "does not echo. Hit any key:");
  bSuccess = SetConsoleMode(hStdIn, dwInputMode & ~ENABLE_LINE_INPUT &
      ~ENABLE_ECHO_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  memset(chBuf, 0, sizeof(chBuf));
    bSuccess = ReadFile(hStdIn, chBuf, sizeof(chBuf), &dwRead, NULL);
  PERR(bSuccess, "ReadFile");
  myPuts(hConOut, "\nHere is the character read:");
  myPuts(hConOut, chBuf);
  myPuts(hConOut, "\n\nHit enter to continue...");
  bSuccess = SetConsoleMode(hStdIn, dwInputMode | ENABLE_WINDOW_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  myGetchar();
  return;
}


CONSOLE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
version 1.0 (initial release)
version 1.1 (March beta release)
  - now using MessageBox to display errors rather than the console
  - handler/demo for new CTRL_CLOSE_EVENT added to handler.c
  - demo for GenerateConsoleCtrlEvent added to SetConsoleCtrlHandler demo
  - larger screen/buffer sizes for buffer/console window sizing demos
  - fixed resizeConBufAndWindow bug in size.c
version 1.2
  - fixed bug in readout.c - dragging large areas caused trap d
version 1.3
  - make cls() more generic - now clears attributes as well
version 1.4
  - added Get/SetConsoleOutputCP demos

Notes:
This sample needs some minor work to make it Unicode-capable; some
variable types need to be tweaked to Unicode-friendly types ('TCHAR' rather
than 'char', etc.)
*/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "console.h"
#include "size.h"  /* for resizeConBufAndWindow() */

/* used to set our initial console screen buffer size */
#define CONX 80
#define CONY 43

/* prototypes of externally defined demo functions */
extern void demoAllocFree(HANDLE hConOld, HANDLE *hConsole);
extern void demoCreate(HANDLE hConOld);
extern void demoFillAtt(HANDLE hConOut);
extern void demoFillChar(HANDLE hConOut);
extern void demoFlush(HANDLE hConOut);
extern void demoCursor(HANDLE hConOut);
extern void demoConMode(HANDLE hConOut);
extern void demoConInfo(HANDLE hConOut);
extern void demoGetTitle(HANDLE hConOut);
extern void demoGetLargest(HANDLE hConOut);
extern void demoGetNumEvents(HANDLE hConOut);
extern void demoGetNumBut(HANDLE hConOut);
extern void demoReadConOut(HANDLE hConOut);
extern void demoReadConChar(HANDLE hConOut);
extern void demoScrollCon(HANDLE hConOut);
extern void demoSizeInfo(HANDLE hConOut);
extern void demoSetCtrlHandler(HANDLE hConOut);
extern void demoWriteIn(HANDLE hConOut);
extern void demoOutputCodePage(HANDLE hConOut);
extern void demoInputCodePage(HANDLE hConOut);

/* information to display on the screen for user to click on */
PCHAR conAPIs[] = {
  "AllocConsole                    Creates a console for the current process",
  "CreateConsoleScreenBuffer       Returns a handle to a new screen buffer",
  "FillConsoleOutputAttribute      Writes attributes to the screen buffer",
  "FillConsoleOutputCharacter      Writes characters to the screen buffer",
  "FlushConsoleInputBuffer         Clears the console input buffer",
  "FreeConsole                     Frees the current console",
  "GenerateConsoleCtrlEvent        Generates a console control event",
  "GetConsoleCP                    Get current console input codepage",
  "GetConsoleOutputCP              Get current console output codepage",
  "GetConsoleCursorInfo            Returns console size and visibility",
  "GetConsoleMode                  Returns console input or output mode",
  "GetConsoleScreenBufferInfo      Returns screen-buffer information",
  "GetConsoleTitle                 Returns console-window title",
  "GetLargestConsoleWindowSize     Returns largest possible window size",
  "GetNumberOfConsoleInputEvents   Retrieves number of console-queue events",
  "GetNumberOfConsoleMouseButtons  Retrieves number of mouse buttons",
  "PeekConsoleInput                Previews console input data",
  "ReadConsoleInput                Reads console input data",
  "ReadConsoleOutput               Reads screen-buffer data",
  "ReadConsoleOutputAttribute      Reads a console attribute string",
  "ReadConsoleOutputCharacter      Reads a screen-buffer string",
  "ScrollConsoleScreenBuffer       Scrolls data in the screen buffer",
  "SetConsoleActiveScreenBuffer    Changes displayed screen buffer",
  "SetConsoleCP                    Sets console input codepage",
  "SetConsoleOutputCP              Sets console output codepage",
  "SetConsoleCursorInfo            Sets cursor size and visibility",
  "SetConsoleCursorPosition        Sets cursor position",
  "SetConsoleMode                  Sets console input or output mode",
  "SetConsoleScreenBufferSize      Changes screen-buffer size",
  "SetConsoleTextAttribute         Sets attributes for screen text",
  "SetConsoleTitle                 Sets console-window title string",
  "SetConsoleWindowInfo            Sets console window size",
  "SetConsoleCtrlHandler           Sets console ctrl-c handler",
  "WriteConsoleInput               Writes to console input buffer",
  "WriteConsoleOutput              Writes to screen buffer",
  "WriteConsoleOutputAttribute     Writes an attribute string to console",
  "WriteConsoleOutputCharacter     Writes a character string to console"
};

/* this variable holds the number next to the API on the screen that */
/* the user clicks on */
enum cAPIs { ALLOC = 1, CREATE, FILLATT, FILLCHAR, FLUSH, FREE,
  GENCTRL, GETCP, GETOUTCP, GETCUR, GETMODE, GETCONINFO, GETTITLE,
  GETLARGEST, GETNUMEV, GETNUMBUT, PEEK, READCONIN, READCONOUT,
  READCONATT, READCONCHAR, SCROLL, SETACTIVE, SETCP, SETOUTCP,
  SETCURINF, SETCURPOS, SETMODE, SETSIZE, SETATT, SETTITLE, SETINFO,
  SETHAND, WRITEIN, WRITEOUT, WRITEATT, WRITECHAR };

/*****************************************************************
* FUNCTION: myGetchar(void)                                      *
*                                                                *
* PURPOSE: get a single character from the standard input handle *
*                                                                *
* INPUT: none                                                    *
*                                                                *
* RETURNS: the char received from the console                    *
*****************************************************************/

CHAR myGetchar(void)
{
  HANDLE hStdIn; /* standard input */
  DWORD dwInputMode; /* to save the input mode */
  BOOL bSuccess;
  CHAR chBuf; /* buffer to read into */
  DWORD dwRead;

  /* get the standard input handle to read from. There is only one */
  /* instance of standard input per process at any given time */
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  /* save the console mode */
  bSuccess = GetConsoleMode(hStdIn, &dwInputMode);
  PERR(bSuccess, "GetconsoleMode");
  /* disable line input. Echo input must be disabled when disabling */
  /* line input */
  bSuccess = SetConsoleMode(hStdIn, dwInputMode & ~ENABLE_LINE_INPUT &
      ~ENABLE_ECHO_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  /* read a character from the console input */
  bSuccess = ReadFile(hStdIn, &chBuf, sizeof(chBuf), &dwRead, NULL);
  PERR(bSuccess, "ReadFile");
  /* restore the original console input mode */
  bSuccess = SetConsoleMode(hStdIn, dwInputMode);
  PERR(bSuccess, "SetConsoleMode");
  return(chBuf);
}

/*********************************************************************
* FUNCTION: perr(PCHAR szFileName, int line, PCHAR szApiName,        *
*                DWORD dwError)                                      *
*                                                                    *
* PURPOSE: report API errors. Allocate a new console buffer, display *
*          error number and error text, restore previous console     *
*          buffer                                                    *
*                                                                    *
* INPUT: current source file name, current line number, name of the  *
*        API that failed, and the error number                       *
*                                                                    *
* RETURNS: none                                                      *
*********************************************************************/

/* maximum size of the buffer to be returned from FormatMessage */
#define MAX_MSG_BUF_SIZE 512

void perr(PCHAR szFileName, int line, PCHAR szApiName, DWORD dwError)
{
  CHAR szTemp[1024];
  DWORD cMsgLen;
  CHAR *msgBuf; /* buffer for message text from system */
  int iButtonPressed; /* receives button pressed in the error box */

  /* format our error message */
  sprintf(szTemp, "%s: Error %d from %s on line %d:\n", szFileName,
      dwError, szApiName, line);
  /* get the text description for that error number from the system */
  cMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
      FORMAT_MESSAGE_ALLOCATE_BUFFER | 40, NULL, dwError,
      MAKELANGID(0, SUBLANG_ENGLISH_US), (LPTSTR) &msgBuf, MAX_MSG_BUF_SIZE,
      NULL);
  if (!cMsgLen)
    sprintf(szTemp + strlen(szTemp), "Unable to obtain error message text! \n"
        "%s: Error %d from %s on line %d", __FILE__,
        GetLastError(), "FormatMessage", __LINE__);
  else
    strcat(szTemp, msgBuf);
  strcat(szTemp, "\n\nContinue execution?");
  MessageBeep(MB_ICONEXCLAMATION);
  iButtonPressed = MessageBox(NULL, szTemp, "Console API Error",
      MB_ICONEXCLAMATION | MB_YESNO | MB_SETFOREGROUND);
  /* free the message buffer returned to us by the system */
  if (cMsgLen)
    LocalFree((HLOCAL) msgBuf);
  if (iButtonPressed == IDNO)
    exit(1);
  return;
}

/*************************************************************************
* FUNCTION: myPuts(HANDLE hConsole, PCHAR s)                             *
*                                                                        *
* PURPOSE: write a string to the given console buffer, appending a cr/lf *
*                                                                        *
* INPUT: the console to write to, and the string to write                *
*                                                                        *
* RETURNS: TRUE if success, FALSE if an error occured                    *
*************************************************************************/

BOOL myPuts(HANDLE hConsole, PCHAR s)
{
  BOOL bSuccess;
  DWORD cCharsWritten;
  const PCHAR crlf = "\n";
  BOOL retflag = TRUE;

  /* write the string to the console */
  bSuccess = WriteConsole(hConsole, s, strlen(s), &cCharsWritten, NULL);
  PERR(bSuccess, "WriteConsole");
  retflag = bSuccess;
  /* put a carriage return & line feed after the string */
  bSuccess = WriteConsole(hConsole, crlf, strlen(crlf), &cCharsWritten, NULL);
  PERR(bSuccess, "WriteConsole");
  if (!bSuccess)
    retflag = FALSE;
  return(retflag);
}

/********************************************************************
* FUNCTION: getConX(HANDLE hCon)                                    *
*                                                                   *
* PURPROSE: to get the current width of the console output buffer   *
*                                                                   *
* INPUT: the handle to get the information for                      *
*                                                                   *
* RETURNS: the width of the current console output buffer, in chars *
********************************************************************/

SHORT getConX(HANDLE hCon)
{
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  BOOL bSuccess;

  bSuccess = GetConsoleScreenBufferInfo(hCon, &csbi);
  PERR(bSuccess, "GetConsoleScreenBufferInfo");
  return(csbi.dwSize.X);
}

/*********************************************************************
* FUNCTION: getConY(HANDLE hCon)                                     *
*                                                                    *
* PURPROSE: to get the current height of the console output buffer   *
*                                                                    *
* INPUT: the handle to get the information for                       *
*                                                                    *
* RETURNS: the height of the current console output buffer, in chars *
*********************************************************************/

SHORT getConY(HANDLE hCon)
{
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  BOOL bSuccess;

  bSuccess = GetConsoleScreenBufferInfo(hCon, &csbi);
  PERR(bSuccess, "GetConsoleScreenBufferInfo");
  return(csbi.dwSize.Y);
}

/*************************************************************************
* FUNCTION: showConAPIs(HANDLE hConsole)                                 *
*                                                                        *
* PURPOSE: to display a list of console APIs on the given console buffer *
*                                                                        *
* INPUT: the console to display the APIs on                              *
*                                                                        *
* RETURNS: none                                                          *
*************************************************************************/

void showConAPIs(HANDLE hConsole)
{
  COORD coordScreen;
  int i;
  BOOL bSuccess;
  DWORD cCharsWritten;
  CHAR szTemps[128];

  resizeConBufAndWindow(hConsole, CONX, CONY); /* defined in size.c */
  /* set attributes for new writes to the console */
  bSuccess = SetConsoleTextAttribute(hConsole, BACKGROUND_BLUE |
      FOREGROUND_WHITE);
  PERR(bSuccess, "SetConsoleTextAttribute");
  cls(hConsole); /* will clear screen to newly set color attribute */
  /* fill screen with API list */
  for (i = 0; i < sizeof(conAPIs) / sizeof(conAPIs[0]); i++)
    {
    coordScreen.X = 0;
    coordScreen.Y = (SHORT) (i + 2);  /* start on third line (zero based) */
    /* position the cursor to start the API list */
    bSuccess = SetConsoleCursorPosition(hConsole, coordScreen);
    PERR(bSuccess, "SetConsoleCursorPosition");
    /* format a line and write it to the screen */
    sprintf(szTemps, "%-3d%s", i + 1, conAPIs[i]);
    myPuts(hConsole, szTemps);
    /* color the number yellow on blue */
    bSuccess = FillConsoleOutputAttribute(hConsole, FOREGROUND_YELLOW |
        FOREGROUND_INTENSITY | BACKGROUND_BLUE, 3, coordScreen,
        &cCharsWritten);
    PERR(bSuccess, "FillConsoleOutputAttribute");
    coordScreen.X = 3;
    /* color text light cyan on blue */
    bSuccess = FillConsoleOutputAttribute(hConsole, FOREGROUND_CYAN |
        FOREGROUND_INTENSITY | BACKGROUND_BLUE, strlen(conAPIs[i]),
        coordScreen, &cCharsWritten);
    PERR(bSuccess, "FillConsoleOutputAttribute");
    }
  myPuts(hConsole, "\nClick on an API to see a demonstration of that API.\n"
                   "Hit ESC to exit the program.");
  return;
}

/*************************************************************************
* FUNCTION: putStatusLine(HANDLE hOut, PCHAR buf)                        *
*                                                                        *
* PURPOSE: display a string on the top line of the console output buffer *
*                                                                        *
* INPUT: the output handle, the string to display                        *
*                                                                        *
* RETURNS: none                                                          *
*************************************************************************/

void putStatusLine(HANDLE hOut, PCHAR buf)
{
  BOOL bSuccess;
  /* position the status line at (0, 0) */
  const COORD dwWriteCoord = {0, 0};
  DWORD cCharsWritten;
  int len; /* the length of the input string parameter */
  CHAR szTemp[256];
  SHORT sWidth; /* console width */

  sWidth = getConX(hOut);
  strcpy(szTemp, buf);
  len = strlen(szTemp);
  memset(szTemp + len, ' ', sWidth - len);  /* blank out rest of line */
  /* write the string to the console at the correct position */
  bSuccess = WriteConsoleOutputCharacter(hOut, szTemp, sWidth,
      dwWriteCoord, &cCharsWritten);
  PERR(bSuccess, "WriteConsoleOutput");
  /* color the status line so it stands out */
  bSuccess = FillConsoleOutputAttribute(hOut, FOREGROUND_RED |
      BACKGROUND_WHITE, sWidth, dwWriteCoord, &cCharsWritten);
  PERR(bSuccess, "FillConsoleOutputAttribute");
  return;
}

/********************************************************************
* FUNCTION: demoAPI(HANDLE *hConsole, enum cAPIs apiNumber)         *
*                                                                   *
* PURPOSE: call the correct demo function based on the input index  *
*                                                                   *
* INPUT: current console output buffer, and screen index of the API *
*        to demonstrate                                             *
*                                                                   *
* RETURNS: none                                                     *
********************************************************************/

static void demoAPI(HANDLE *phConsole, enum cAPIs apiNumber)
{
  BOOL bSuccess;
  HANDLE hConTemp;
  CHAR szConsoleTitle[128]; /* to store the console title */
  DWORD dwCharsRead;

  /* create a temp screen buffer to write to */
  hConTemp = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER,
      NULL);
  PERR(hConTemp != INVALID_HANDLE_VALUE,
      "CreateConsoleScreenBuffer");
  /* make it the active buffer */
  bSuccess = SetConsoleActiveScreenBuffer(hConTemp);
  PERR(bSuccess, "SetConsoleActiveScreenBuffer");
  /* clear screen & save the console title */
  bSuccess = SetConsoleTextAttribute(hConTemp, BACKGROUND_CYAN);
  PERR(bSuccess, "SetConsoleTextAttribute");
  cls(hConTemp); /* will clear with new color attribute */
  dwCharsRead = GetConsoleTitle(szConsoleTitle, sizeof(szConsoleTitle));
  PERR(dwCharsRead, "GetConsoleTitle");

  /* flush the input buffer and call the correct demo function */
  bSuccess = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
  PERR(bSuccess, "FlushConsoleInputBuffer");
  switch(apiNumber)
    {
    case ALLOC:
    case FREE:
      demoAllocFree(hConTemp, phConsole);
      return;
      break;
    case CREATE:
    case SETACTIVE:
    case SETATT:
      demoCreate(hConTemp);
      break;
    case FILLATT:
    case WRITECHAR:
      demoFillAtt(hConTemp);
      break;
    case FILLCHAR:
      demoFillChar(hConTemp);
      break;
    case FLUSH:
      demoFlush(hConTemp);
      break;
    case GETCUR:
    case SETCURINF:
    case SETCURPOS:
      demoCursor(hConTemp);
      break;
    case GETMODE:
    case SETMODE:
      demoConMode(hConTemp);
      break;
    case GETCONINFO:
      demoConInfo(hConTemp);
      break;
    case GETTITLE:
    case SETTITLE:
      demoGetTitle(hConTemp);
      break;
    case GETLARGEST:
      demoGetLargest(hConTemp);
      break;
    case GETNUMEV:
    case PEEK:
    case READCONIN:
      demoGetNumEvents(hConTemp);
      break;
    case GETNUMBUT:
      demoGetNumBut(hConTemp);
      break;
    case READCONOUT:
    case WRITEOUT:
    case READCONATT:
    case WRITEATT:
      demoReadConOut(hConTemp);
      break;
    case READCONCHAR:
      demoReadConChar(hConTemp);
      break;
    case SCROLL:
      demoScrollCon(hConTemp);
      break;
    case SETSIZE:
    case SETINFO:
      demoSizeInfo(hConTemp);
      break;
    case SETHAND:
    case GENCTRL:
      demoSetCtrlHandler(hConTemp);
      break;
    case WRITEIN:
      demoWriteIn(hConTemp);
      break;
    case GETCP:
    case SETCP:
      demoInputCodePage(hConTemp);
      break;
    case GETOUTCP:
    case SETOUTCP:
      demoOutputCodePage(hConTemp);
      break;
    default:
      break;
    }
  CloseHandle(hConTemp);  /* free temporary console buffer */
  /* reset active buffer to original buffer. If the temp buffer has been */
  /* resized, the console window will automatically be sized correctly */
  /* when the current console is set back to the old one */
  bSuccess = SetConsoleActiveScreenBuffer(*phConsole);
  PERR(bSuccess, "SetConsoleActiveScreenBuffer");
  /* restore the original console title */
  bSuccess = SetConsoleTitle(szConsoleTitle);
  PERR(bSuccess, "SetConsoleTitle");
  /* flush the input buffer to remove any 'leftover' clicks or key events */
  bSuccess = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
  PERR(bSuccess, "FlushConsoleInputBuffer");
  return;
}

/************************************************************************
* FUNCTION: cls(HANDLE hConsole)                                        *
*                                                                       *
* PURPOSE: clear the screen by filling it with blanks, then home cursor *
*                                                                       *
* INPUT: the console buffer to clear                                    *
*                                                                       *
* RETURNS: none                                                         *
*************************************************************************/

void cls(HANDLE hConsole)
{
  COORD coordScreen = { 0, 0 }; /* here's where we'll home the cursor */
  BOOL bSuccess;
  DWORD cCharsWritten;
  CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
  DWORD dwConSize; /* number of character cells in the current buffer */

  /* get the number of character cells in the current buffer */
  bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
  PERR(bSuccess, "GetConsoleScreenBufferInfo");
  dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
  /* fill the entire screen with blanks */
  bSuccess = FillConsoleOutputCharacter(hConsole, (TCHAR) ' ',
      dwConSize, coordScreen, &cCharsWritten);
  PERR(bSuccess, "FillConsoleOutputCharacter");
  /* get the current text attribute */
  bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
  PERR(bSuccess, "ConsoleScreenBufferInfo");
  /* now set the buffer's attributes accordingly */
  bSuccess = FillConsoleOutputAttribute(hConsole, csbi.wAttributes,
      dwConSize, coordScreen, &cCharsWritten);
  PERR(bSuccess, "FillConsoleOutputAttribute");
  /* put the cursor at (0, 0) */
  bSuccess = SetConsoleCursorPosition(hConsole, coordScreen);
  PERR(bSuccess, "SetConsoleCursorPosition");
  return;
}

/*********************************************************************
* FUNCTION: setConTitle                                              *
*                                                                    *
* PURPOSE: simply set the current console title. Called by each demo *
*          function to specify the name of the source file that      *
*          contains the demo function.                               *
*                                                                    *
* INPUT: null terminated string                                      *
*********************************************************************/

void setConTitle(PCHAR szTitle)
{
  BOOL bSuccess;

  /* set the console title to the input string parameter */
  bSuccess = SetConsoleTitle(szTitle);
  PERR(bSuccess, "SetConsoleTitle");
  return;
}

/*********************************************************************
* FUNCTION: main                                                     *
*                                                                    *
* PURPOSE: main input loop. Set up console input and output          *
*          attributes, then read the input queue and process input   *
*          events. If ESC is hit, end the process. If the mouse is   *
*          clicked, read the line from the output console where the  *
*          mouse was clicked. If an index number can be found at the *
*          beginning of the line, call the appropriate demo function *
*          to demonstrate that API. All input event information is   *
*          displayed on the status line on the top line.             *
*                                                                    *
* INPUT: none                                                        *
*                                                                    *
* RETURNS: 1 if running on non-supported Win32 platform, 0 for a     *
* normal exit                                                        *
*                                                                    *
*********************************************************************/

int main(void)
{
  BOOL bSuccess;
  HANDLE hStdIn, hStdOut; /* standard input, output handles */
  DWORD dwMode;
  /* array of console input event records */
  INPUT_RECORD inputBuffer;
  DWORD dwInputEvents; /* number of events actually read */
  CHAR bOutBuf[256]; /* buffer to format event information into */
  /* used to get the new console screen buffer size if it is changed */
  COORD coordScreen; /* used when reading the index number from the screen */
  CHAR *szLineBuf; /* buffer to read the index number into */
  DWORD cCharsRead;
  enum cAPIs apiNumber; /* the index number of the API read from the screen */
  CONSOLE_CURSOR_INFO cci; /* used when turning off the cursor */
  CONSOLE_SCREEN_BUFFER_INFO csbi; /* used to get cursor position */
  OSVERSIONINFO osVer; /* for GetVersionEx() */

  /* check if Win32s, if so, display notice and terminate */
  osVer.dwOSVersionInfoSize = sizeof(osVer);
  bSuccess = GetVersionEx(&osVer);
  PERR(bSuccess, "GetVersionEx");
  if (osVer.dwPlatformId == VER_PLATFORM_WIN32s)
    {
    MessageBox(NULL,
        "This application cannot run on Windows 3.1.\n"
        "This application will now terminate.",
        "Error: Windows NT or Windows 95 Required to Run",  MB_OK );
    return(1);
    }

  /* Free the console and immediately allocate a new one. This is done so */
  /* that when debugging under ntsd, the application output will not be */
  /* intermingled with the debugger output. This also makes cleanup */
  /* much easier; we won't need to restore the cursor, buffer/console */
  /* sizes, screen colors, etc. since we'll have our own console window */
  /* that will go away when we terminate. */

  /* Here's a trick we'll use to guess whether we're starting from a */
  /* console or GUI app: if the cursor is at 0,0, we'll assume that we've */
  /* started from a GUI app and not bother freeing/allocing a new console. */
  /* This will also avoid cases where the new console is not in the */
  /* foreground (AllocConsole does not guarantee a foreground window). */
  bSuccess = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
             &csbi);
  PERR(bSuccess, "GetConsoleScreenBufferInfo");
  if (csbi.dwCursorPosition.X | csbi.dwCursorPosition.Y) /* either non-zero? */
    {
    bSuccess = FreeConsole();
    PERR(bSuccess, "FreeConsole");
    /* C run-time standard-handle I/O will not work from now on unless we */
    /* "fix" the C standard handles to reference the console standard */
    /* handles which will be created by the AllocConsole call below. */
    /* In this sample, we won't be using C run-time I/O, so it's not an */
    /* issue. */
    bSuccess = AllocConsole();
    PERR(bSuccess, "AllocConsole");
    }
  /* let's put up a meaningful console title */
  bSuccess = SetConsoleTitle("Win32 Console API Demo");
  PERR(bSuccess, "SetConsoleTitle");
  /* get the standard handles */
  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  PERR(hStdOut != INVALID_HANDLE_VALUE, "GetStdHandle");
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  /* set up mouse and window input */
  bSuccess = GetConsoleMode(hStdIn, &dwMode);
  PERR(bSuccess, "GetConsoleMode");
  /* when turning off ENABLE_LINE_INPUT, you MUST also turn off */
  /* ENABLE_ECHO_INPUT. */
  bSuccess = SetConsoleMode(hStdIn, (dwMode & ~(ENABLE_LINE_INPUT |
      ENABLE_ECHO_INPUT)) | ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  /* save then hide the cursor */
  cci.dwSize = 100;
  cci.bVisible = FALSE;
  bSuccess = SetConsoleCursorInfo(hStdOut, &cci);
  PERR(bSuccess, "SetConsoleCursorInfo");
  /* resize console to CONX * CONY and put list of console APIs up */
  showConAPIs(hStdOut);
  /* This is the main input loop. Read from the input queue and process */
  /* the events read */
  szLineBuf = (char *) malloc(getConX(hStdOut));
  PERR(szLineBuf, "malloc");

  do
    {
    /* read an input events from the input event queue */
    bSuccess = ReadConsoleInput(hStdIn, &inputBuffer, 1, &dwInputEvents);
    PERR(bSuccess, "ReadConsoleInput");
    switch (inputBuffer.EventType)
      {
      case KEY_EVENT:
        if (inputBuffer.Event.KeyEvent.bKeyDown)
          {
          /* display the key event info on the status line */
          sprintf(bOutBuf, "key: virtual=%d ascii=%c",
              inputBuffer.Event.KeyEvent.wVirtualKeyCode,
              inputBuffer.Event.KeyEvent.uChar.AsciiChar);
          putStatusLine(hStdOut, bOutBuf);
          }
        break;
      case MOUSE_EVENT:
        sprintf(bOutBuf, "mouse: %s at %d, %d",
            (inputBuffer.Event.MouseEvent.dwEventFlags == MOUSE_MOVED ?
            "moved" : "clicked"), inputBuffer.Event.MouseEvent.dwMousePosition.X,

inputBuffer.Event.MouseEvent.dwMousePosition.Y);
        putStatusLine(hStdOut, bOutBuf);
        /* was this a mouse click? If so dwEventFlags contains 0 */
        /* are any mouse buttons down? If so dwButtonState is not 0 */
        if (inputBuffer.Event.MouseEvent.dwEventFlags == 0 &&
            inputBuffer.Event.MouseEvent.dwButtonState)
          {
          coordScreen.X = 0;
          coordScreen.Y = inputBuffer.Event.MouseEvent.dwMousePosition.Y;
          /* read the line where the mouse clicked */
          bSuccess = ReadConsoleOutputCharacter(hStdOut, szLineBuf,
              sizeof(szLineBuf), coordScreen, &cCharsRead);
          PERR(bSuccess, "ReadConsoleOutputCharacter");
          /* scan for an index number at the beginning of that line. */
          /* if there was one read, call the correct demo function */
          if (sscanf(szLineBuf, "%d", &apiNumber))
            demoAPI(&hStdOut, apiNumber);
          }
        break;
      case WINDOW_BUFFER_SIZE_EVENT:
        sprintf(bOutBuf, "window: %d, %d",
            inputBuffer.Event.WindowBufferSizeEvent.dwSize.X,
            inputBuffer.Event.WindowBufferSizeEvent.dwSize.Y);
        putStatusLine(hStdOut, bOutBuf);
        Sleep(1000);
        break;
      } /* switch */
    /* when we receive an esc down key, drop out of do loop */
    } while (!(inputBuffer.EventType == KEY_EVENT &&
            inputBuffer.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE &&
            inputBuffer.Event.KeyEvent.bKeyDown));
  free(szLineBuf); /* free allocated line buffer */
  return(0);
}


CONSOLE.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define BACKGROUND_WHITE  (WORD) 0x00f0
#define BACKGROUND_CYAN   (WORD) 0x0030
#define FOREGROUND_YELLOW (WORD) 0x0006
#define FOREGROUND_CYAN   (WORD) 0x0003
#define FOREGROUND_WHITE  (WORD) 0x0007

#define PERR(bSuccess, api) {if (!(bSuccess)) perr(__FILE__, __LINE__, \
    api, GetLastError());}

/* function prototypes */
extern int myPuts(HANDLE hConsole, char *s);
extern void showConAPIs(HANDLE hConsole);
extern void putStatusLine(HANDLE hOut, char *buf);
extern void perr(char *szFileName, int line, char *szApiName, DWORD dwError);
extern void cls(HANDLE hConsole);
extern char myGetchar(void);
extern void setConTitle(char *szTitle);
extern SHORT getConX(HANDLE hCon);
extern SHORT getConY(HANDLE hCon);


CONTITLE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include "console.h"

/********************************************************************
* FUNCTION: demoGetTitle(HANDLE hConOut)                            *
*                                                                   *
* PURPOSE: demonstrate GetConsoleTitle and SetConsoleTitle. Read and*
*          display the console title, then set the console title.   *
*                                                                   *
* INPUT: handle to write to                                         *
********************************************************************/

void demoGetTitle(HANDLE hConOut)
{
  BOOL bSuccess;
  CHAR szTitleBuf[256]; /* buffer for the current console title */
  CHAR szTemp[256];
  HANDLE hStdIn; /* standard input handle */
  DWORD dwStdInMode; /* standard input handle mode */
  DWORD dwBytesRead;

  setConTitle(__FILE__);
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  /* save the console mode */
  bSuccess = GetConsoleMode(hStdIn, &dwStdInMode);
  PERR(bSuccess, "GetConsoleMode");
  /* turn on line input mode so we can enter a string. */
  /* when turning on ENABLE_LINE_INPUT, you MUST also turn on */
  /* ENABLE_ECHO_INPUT. */
  bSuccess = SetConsoleMode(hStdIn, dwStdInMode | ENABLE_LINE_INPUT |
      ENABLE_ECHO_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  myPuts(hConOut, "Let's get the console title with GetConsoleTitle.");
  /* get the console title */
  dwBytesRead = GetConsoleTitle(szTitleBuf, sizeof(szTitleBuf));
  PERR(dwBytesRead, "GetConsoleTitle");
  sprintf(szTemp, "The console title is: %s", szTitleBuf);
  myPuts(hConOut, szTemp);
  myPuts(hConOut, "\nNow let's set a new console title with SetConsoleTitle.");
  myPuts(hConOut, "\nEnter a new console title:");
  bSuccess = ReadFile(hStdIn, szTitleBuf, 256, &dwBytesRead, NULL);
  PERR(bSuccess, "ReadFile");
  if (dwBytesRead > 2) /* did the user type any chars before hitting return? */
    {
    /* null terminate the string - less two for cr/lf */
    szTitleBuf[dwBytesRead - 2] = 0;
    bSuccess = SetConsoleTitle(szTitleBuf);
    PERR(bSuccess, "SetConsoleTitle");
    }
  /* restore the console title to the original mode */
  bSuccess = SetConsoleMode(hStdIn, dwStdInMode);
  PERR(bSuccess, "SetConsoleMode");
  myPuts(hConOut, "\nHit enter to return...");
  myGetchar();
  return;
}


CREATE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include "console.h"

/*********************************************************************
* FUNCTION: demoCreate(HANDLE hConOld)                               *
*                                                                    *
* PURPOSE: demonstrate CreateConsoleScreenBuffer,                    *
*          SetConsoleTextAttribute, and                              *
*          SetConsoleActiveScreenBuffer. Create a 'help' screen on a *
*          new buffer and quickly switch between the main buffer and *
*          the help buffer on command without redrawing the entire   *
*          screens. SetConsoleTextAttribute will be used to make the *
*          help screen a different color.                            *
*                                                                    *
* INPUT: the output handle to write to                               *
**********************************************************************/

void demoCreate(HANDLE hConOld)
{
  BOOL bSuccess;
  HANDLE hConHelp; /* console for the help screen */
  COORD dwWriteCoord = {0, 0}; /* where to write the screen attributes */
  DWORD cCharsWritten;
  HANDLE hStdIn; /* standard input handle */
  CHAR c = 0; /* virtual key code that we will read */
  HANDLE hConCurrent; /* keep track of the current visible console buffer */
  INPUT_RECORD inputBuf; /* console input event record */
  DWORD cInputEvents;

  setConTitle(__FILE__);
  myPuts(hConOld, "Let's create a help screen in another buffer. Then\n"
                  "when the user hits F1, we can easily flip to the help\n"
                  "screen and back without recreating the text on the\n"
                  "screens by simply changing the active buffer.\n"
                  "We'll change the default text attribute with\n"
                  "SetConsoleTextAttribute before creating the help screen\n"
                  "so that the sample text is in a different color.\n"
                  "Hit F1 now for help, and esc to return here.\n"
                  "Hit esc from this screen to return to API list.");
  /* create a separate console buffer for the help screen */
  hConHelp = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER,
      NULL);
  PERR(hConHelp != INVALID_HANDLE_VALUE, "CreateConsoleScreenBuffer");
  /* change the color of the help screen */
  bSuccess = FillConsoleOutputAttribute(hConHelp, BACKGROUND_BLUE,
      getConX(hConHelp) * getConY(hConHelp), dwWriteCoord, &cCharsWritten);
  PERR(bSuccess, "FillConsoleOutputAttribute");
  /* set the color for future text output */
  bSuccess = SetConsoleTextAttribute(hConHelp, FOREGROUND_YELLOW |
      FOREGROUND_INTENSITY | BACKGROUND_BLUE);
  PERR(bSuccess, "SetConsoleTextAttribute");
  myPuts(hConHelp, "                S u p e r D u p e r B a s e   H e l p\n"
                   "\n\n                  F1: search        F6: window\n"
                   "                  F2: extract       F7: initialize\n"
                   "                  F3: sort          F8: exit\n"
                   "                  F4: query         F9: save\n"
                   "                  F5: execute       F10: explode\n"
                   "                  ESC: exit help");
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  /* keep track of the currently visible console */
  hConCurrent = hConOld;
  /* switch between the help and previous buffer when user hits F1 or ESC */
  for(;;)
    {
    do
      {
      /* throw away any non-keystroke events or any key-up events */
      bSuccess = ReadConsoleInput(hStdIn, &inputBuf, 1, &cInputEvents);
      PERR(bSuccess, "ReadConsoleInput");
      } while (inputBuf.EventType != KEY_EVENT ||
          !inputBuf.Event.KeyEvent.bKeyDown);
    /* get the virtual scan code of the key-down event */
    c = (char) inputBuf.Event.KeyEvent.wVirtualKeyCode;
    switch(c)
      {
      case VK_F1:
        /* if the current buffer is the original buffer, switch to the */
        /* help buffer */
        if (hConCurrent == hConOld)
          {
          bSuccess = SetConsoleActiveScreenBuffer(hConHelp);
          PERR(bSuccess, "SetConsoleActiveScreenBuffer");
          hConCurrent = hConHelp;
          }
        break;
      case VK_ESCAPE:
        /* if the current buffer is the help buffer, switch to the */
        /* original buffer. Otherwise, clean up and return */
        if (hConCurrent == hConHelp)
          {
          bSuccess = SetConsoleActiveScreenBuffer(hConOld);
          PERR(bSuccess, "SetConsoleActiveScreenBuffer");
          hConCurrent = hConOld;
          }
        else
          {
          CloseHandle(hConHelp);
          return;
          }
        break;
      case VK_F10:
        if (hConCurrent == hConHelp)
          myPuts(hConHelp, "BOOM!");
        break;
      default:
        break;
      }  /* switch */
    }  /* while */
  return;
}


CURSOR.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include "console.h"

/*********************************************************************
* FUNCTION: demoCursor(HANDLE hConOut)                               *
*                                                                    *
* PURPOSE: demonstrate GetConsoleCursorInfo, SetConsoleCursorInfo,   *
*          and SetConsoleCursorPosition. Show the current cursor     *
*          information, then have the cursor follow the mouse around *
*          the screen. Shrink the cursor size when the user clicks   *
*          one button, and grow the cursor when he clicks the other. *
*                                                                    *
* INPUT: the console output handle to write to and manipulate the    *
*        cursor for.                                                 *
*********************************************************************/

void demoCursor(HANDLE hConOut)
{
  BOOL bSuccess;
  /* to set initial size and visibility of cursor */
  CONSOLE_CURSOR_INFO cci;
  INPUT_RECORD inputBuffer;
  DWORD dwInputEvents;
  HANDLE hStdIn;
  CHAR szTemp[128];

  setConTitle(__FILE__);
  myPuts(hConOut, "Current cursor information, as reported by\n"
                  "GetConsoleCursorInfo:\n");
  bSuccess = GetConsoleCursorInfo(hConOut, &cci);
  PERR(bSuccess, "GetConsoleCursorInfo");
  sprintf(szTemp, "Cursor size (in percent maximum): %d", cci.dwSize);
  myPuts(hConOut, szTemp);
  sprintf(szTemp, "Cursor visible: %s", cci.bVisible ? "TRUE" : "FALSE");
  myPuts(hConOut, szTemp);
  myPuts(hConOut, "\nLet's use SetConsoleCursorPosition to have the console\n"
                  "cursor follow the mouse pointer around on the screen.\n"
                  "When the left mouse button is clicked, we'll use \n"
                  "SetConsoleCursorInfo to increase the cursor size by 10%.\n"
                  "When the right mouse button is clicked, the cursor size\n"
                  "will be decreased by 10%. To return, hit ESC.");
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  for(;;)
    {
      /* get an input event */
    bSuccess = ReadConsoleInput(hStdIn, &inputBuffer, 1, &dwInputEvents);
    PERR(bSuccess, "ReadConsoleInput");
    switch (inputBuffer.EventType)
      {
      case KEY_EVENT:
        /* is it a key-down event? Is it an ESC char? If so return */
        if (inputBuffer.Event.KeyEvent.bKeyDown &&
            inputBuffer.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
          return;
        break;
      case MOUSE_EVENT:
        /* if the mouse moved draw the cursor at the mouse position */
        if (inputBuffer.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
          {
          bSuccess = SetConsoleCursorPosition(hConOut,
              inputBuffer.Event.MouseEvent.dwMousePosition);
          PERR(bSuccess, "SetConsoleCursorPosition");
          }
        /* if the mouse is clicked, increase/decrease cursor size. */
        /* Consider a double click a single click for this sample */
        if (!inputBuffer.Event.MouseEvent.dwEventFlags || /* a click */
            inputBuffer.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)
          {
          /* is the leftmost mouse button is down? If so, increase cursor */
          if (inputBuffer.Event.MouseEvent.dwButtonState ==
              FROM_LEFT_1ST_BUTTON_PRESSED)
            {
            /* if cursor size grows > 100, wrap around to small size */
            cci.dwSize = (cci.dwSize + 10) % 100 + 1;
            bSuccess = SetConsoleCursorInfo(hConOut, &cci);
            PERR(bSuccess, "SetConsoleCursorInfo");
            }
          /* is the rightmost button is down? */
          if (inputBuffer.Event.MouseEvent.dwButtonState ==
              RIGHTMOST_BUTTON_PRESSED)
            {
            /* if cursor size < 0, wrap around to large size */
            cci.dwSize -= 10;
            if ((int) cci.dwSize < 1)
              cci.dwSize = 100;
            bSuccess = SetConsoleCursorInfo(hConOut, &cci);
            PERR(bSuccess, "SetConsoleCursorInfo");
            }
          /* other buttons will be ignored */
          }
        break;
      } /* switch */
    }  /* while */
  return;
}


FILLATT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "console.h"

#pragma setlocale(".1252")

/* number of colors in the color bar */
#define MAXCOLORS 16
/* horizontal size in characters of the sample text area */
#define SAMPTEXTX 33
/* veritcal size in characters of the sample text area */
#define SAMPTEXTY 5

/********************************************************************
* FUNCTION: demoFillAtt(HANDLE hConOut)                             *
*                                                                   *
* PURPOSE: demonstrate FillConsoleOutputAttribute and               *
*          WriteConsoleOutputCharacter. Create a console version of *
*          the "ScreenSize..." menu item from the console system    *
*          menu. Allow the user to select a foreground and          *
*          background color, which will be immediately displayed in *
*          the sample text box                                      *
*                                                                   *
* INPUT: the console output handle to write to                      *
********************************************************************/

void demoFillAtt(HANDLE hConOut)
{
  COORD foregLoc = {5, 5}; /* foreground 'button' location */
  COORD backgLoc = {5, 7}; /* background 'button' location */
  COORD okLoc = {20, 5}; /* ok 'button' location */
  COORD colorbarLoc = {6, 10};  /* loc of first color in colorbar */
  COORD sampleLoc = {5, 13}; /* sample text location */
  COORD dwBufCoord; /* temp COORD structure */
  BOOL bSuccess;
  PCHAR szForeg = "  Screen Text"; /* foreground 'button' and text */
  PCHAR szBackg = "  Screen Background"; /* background 'button' */
  PCHAR szOk = "  OK"; /* OK 'button' */
  /* these strings are the box around the color bar */
  PCHAR szColors1 = "                                  ";
  PCHAR szColors2 = "                                  ";
  PCHAR szColors3 = "                                  ";
  WORD wButtonColor; /* holds the button 'color' */
  DWORD dwCharsWritten;
  WORD i;
  INPUT_RECORD inputBuf;
  DWORD cInputEvents;
  BOOL bForeground; /* state flag: foreground button active? */
  COORD wCurPos;
  HANDLE hStdIn;
  /* arraw of attributes to put in the colorbar, each attribute in 2 spaces */
  WORD szAttr[MAXCOLORS * 2];
  PCHAR szSampText = "A console consists of a keyboard and mouse input buffer"
                  " and one or more screen buffers.  \"CONIN$\" refers to the"
                  " input buffer. Stdin is a handle to \"CONIN$\".  \"CONOUT$\""
                  " refers to a screen buffer.";
  PCHAR p;
  WORD wCurSampAttr; /* current attribute of the sample text area */

  setConTitle(__FILE__);
  /* place our "buttons" and text on the screen. First the 'foreground' */
  /* button... */
  bSuccess = WriteConsoleOutputCharacter(hConOut, szForeg, strlen(szForeg),
      foregLoc, &dwCharsWritten);
  /* now the 'background' button */
  PERR(bSuccess, "WriteConsoleOutputCharacter");
  bSuccess = WriteConsoleOutputCharacter(hConOut, szBackg, strlen(szBackg),
      backgLoc, &dwCharsWritten);
  /* place the 'OK' button */
  PERR(bSuccess, "WriteConsoleOutputCharacter");
  bSuccess = WriteConsoleOutputCharacter(hConOut, szOk, strlen(szOk),
      okLoc, &dwCharsWritten);
  PERR(bSuccess, "WriteConsoleOutputCharacter");
  /* now let's draw a boxed colorbar */
  dwBufCoord = colorbarLoc;
  /* move up and left one to draw a box around the colorbar */
  dwBufCoord.X--;
  dwBufCoord.Y--;
  /* the top of the box */
  bSuccess = WriteConsoleOutputCharacter(hConOut, szColors1, strlen(szColors1),
      dwBufCoord, &dwCharsWritten);
  PERR(bSuccess, "WriteConsoleOutputCharacter");
  dwBufCoord.Y++;
  /* the middle of the box */
  bSuccess = WriteConsoleOutputCharacter(hConOut, szColors2, strlen(szColors2),
      dwBufCoord, &dwCharsWritten);
  PERR(bSuccess, "WriteConsoleOutputCharacter");
  dwBufCoord.Y++;
  /* the bottom of the box */
  bSuccess = WriteConsoleOutputCharacter(hConOut, szColors3, strlen(szColors3),
      dwBufCoord, &dwCharsWritten);
  PERR(bSuccess, "WriteConsoleOutputCharacter");

  /* color the "buttons" a different color */
  wButtonColor = FOREGROUND_WHITE | BACKGROUND_WHITE;
  /* color the 'backgroud' button */
  bSuccess = WriteConsoleOutputAttribute(hConOut, &wButtonColor,
      1, backgLoc, &dwCharsWritten);
  PERR(bSuccess, "WriteConsoleOutputAttribute");
  /* color the 'OK' button */
  bSuccess = WriteConsoleOutputAttribute(hConOut, &wButtonColor,
      1, okLoc, &dwCharsWritten);
  PERR(bSuccess, "WriteConsoleOutputAttribute");
  /* color the 'foreground' button, but with a different color */
  wButtonColor = BACKGROUND_WHITE;
  bSuccess = WriteConsoleOutputAttribute(hConOut, &wButtonColor,
      1, foregLoc, &dwCharsWritten);
  PERR(bSuccess, "WriteConsoleOutputAttribute");
  bForeground = TRUE; /* the 'foreground' button is 'highlighted' */

  /* construct the attribute string */
  for (i = 0; i < MAXCOLORS; i++)
    /* for each set of two, the color attributes are in the high byte */
    /* of the word - shift them into the high byte */
    szAttr[i * 2] = szAttr[(i * 2) + 1] = i << 4;
  /* write out the attributes at the colorbar location */
  dwBufCoord = colorbarLoc;
  bSuccess = WriteConsoleOutputAttribute(hConOut, szAttr, MAXCOLORS * 2,
      dwBufCoord, &dwCharsWritten);
  PERR(bSuccess, "WriteConsoleOutputAttribute");

  /* put up a sample text area */
  p = szSampText;
  wCurPos = sampleLoc;
  wCurSampAttr = BACKGROUND_WHITE;
  for (i = 0; i < SAMPTEXTY; i++)
    {
    bSuccess = WriteConsoleOutputCharacter(hConOut, p,
        min(strlen(p), SAMPTEXTX), wCurPos, &dwCharsWritten);
    PERR(bSuccess, "WriteConsoleOutputCharacter");
    /* color the text with the current sample attribute */
    bSuccess = FillConsoleOutputAttribute(hConOut, wCurSampAttr, SAMPTEXTX,
        wCurPos, &dwCharsWritten);
    PERR(bSuccess, "FillConsoleOutputAttribute");
    wCurPos.Y++;
    /* advance pointer to the next row of characters in the sample string */
    p += min(strlen(p), SAMPTEXTX);
    }

  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  for(;;)
    {
    /* get an input event */
    bSuccess = ReadConsoleInput(hStdIn, &inputBuf, 1, &cInputEvents);
    /* if it's a mouse event but not a mouse move, it's a click */
    PERR(bSuccess, "ReadConsoleInput");
    if (inputBuf.EventType == MOUSE_EVENT &&
        inputBuf.Event.MouseEvent.dwEventFlags != MOUSE_MOVED)
      {
      wCurPos = inputBuf.Event.MouseEvent.dwMousePosition;
      /* is the mouse on the 'foreground' button? */
      if (wCurPos.X == foregLoc.X && wCurPos.Y == foregLoc.Y)
        {
        bForeground = TRUE;
        /* recolor the background color and foreground color */
        /* of the foreground to show the foreground button active */
        wButtonColor = BACKGROUND_WHITE;
        bSuccess = WriteConsoleOutputAttribute(hConOut, &wButtonColor,
            1, foregLoc, &dwCharsWritten);
        PERR(bSuccess, "WriteConsoleOutputAttribute");
        /* recolor the background color to show as inactive */
        wButtonColor = FOREGROUND_WHITE | BACKGROUND_WHITE;
        bSuccess = WriteConsoleOutputAttribute(hConOut, &wButtonColor,
            1, backgLoc, &dwCharsWritten);
        PERR(bSuccess, "WriteConsoleOutputAttribute");
        }
      /* is the mouse on the 'background' button? */
      if (wCurPos.X == backgLoc.X && wCurPos.Y == backgLoc.Y)
        {
        bForeground = FALSE;
        /* recolor the background button to show as active */
        wButtonColor = BACKGROUND_WHITE;
        bSuccess = WriteConsoleOutputAttribute(hConOut, &wButtonColor,
            1, backgLoc, &dwCharsWritten);
        PERR(bSuccess, "WriteConsoleOutputAttribute");
        /* recolor the foreground button to show as inactive */
        wButtonColor = FOREGROUND_WHITE | BACKGROUND_WHITE;
        bSuccess = WriteConsoleOutputAttribute(hConOut, &wButtonColor,
            1, foregLoc, &dwCharsWritten);
        PERR(bSuccess, "WriteConsoleOutputAttribute");
        }
      /* is the mouse on the 'OK' button? */
      if (wCurPos.X == okLoc.X && wCurPos.Y == okLoc.Y)
        break;
      /* is the mouse in the colorbar? */
      if (wCurPos.Y == colorbarLoc.Y && wCurPos.X >= colorbarLoc.X &&
          wCurPos.X < colorbarLoc.X + (SHORT) (MAXCOLORS * 2))
        {
        /* get the screen attribute at the mouse position */
        bSuccess = ReadConsoleOutputAttribute(hConOut, &wButtonColor,
            sizeof(wButtonColor), wCurPos, &dwCharsWritten);
        PERR(bSuccess, "ReadConsoleOutputAttribute");
        /* if the foreground button is active, set the foreground */
        /* attribute for the sample text */
        if (bForeground)
          /* mask off the foreground color and 'or' it with the color */
          /* we just got from the screen, shifted up to make it a */
          /* foreground color (it's actually a background attribute when */
          /* read from the console buffer). */
          wCurSampAttr = (wCurSampAttr & (WORD) 0xF0) | (wButtonColor >> 4);
        /* otherwise set the background attribute for the sample text */
        else
          /* mask off the background color and 'or'  it with the background */
          /* color we just read from the console. */
          wCurSampAttr = (wCurSampAttr & (WORD) 0x0F) | wButtonColor;
        /* now set the attribute of the sample text to the new attribute */
        wCurPos = sampleLoc;
        for (i = 0; i < SAMPTEXTY; i++)
          {
          bSuccess = FillConsoleOutputAttribute(hConOut, wCurSampAttr,
              SAMPTEXTX, wCurPos, &dwCharsWritten);
          PERR(bSuccess, "FillConsoleOutputAttribute");
          wCurPos.Y++;
          }
        }  /* if */
      }  /* if */
    }  /* while */
  return;
}


FILLCHAR.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include "console.h"

/*******************************************************************
* FUNCTION: demoFillChar(HANDLE hConOut)                           *
*                                                                  *
* PURPOSE: demonstrate FillConsoleOutputCharacter. Fill the entire *
*          console with the character that the user hits           *
*                                                                  *
* INPUT: the output console to fill with characters                *
********************************************************************/

void demoFillChar(HANDLE hConOut)
{
  HANDLE hStdIn;
  INPUT_RECORD inputBuf;
  CHAR c; /* ascii character read from the console */
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  COORD coordScreen = {0, 1}; /* location to start the attribute fill */
  DWORD cCharsWritten;
  BOOL bSuccess;
  DWORD cInputEvents;

  setConTitle(__FILE__);
  myPuts(hConOut, "Let's fill the console buffer with a given character by\n"
                  "using the FillConsoleOutputCharacter API. Hit a key to \n"
                  "fill the buffer with (hit ESC to return):");
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  for(;;)
    {
    do
      {
      /* get input events until you get a key-down event */
      bSuccess = ReadConsoleInput(hStdIn, &inputBuf, 1, &cInputEvents);
      PERR(bSuccess, "ReadConsoleInput");
      } while (inputBuf.EventType != KEY_EVENT ||
            !inputBuf.Event.KeyEvent.bKeyDown);
    c = (char) inputBuf.Event.KeyEvent.uChar.AsciiChar;
    if (inputBuf.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
      break;
    /* we need to get the console buffer size */
    bSuccess = GetConsoleScreenBufferInfo(hConOut, &csbi);
    PERR(bSuccess, "GetConsoleScreenBufferInfo");
    bSuccess = FillConsoleOutputCharacter(hConOut, /* screen buffer handle */
        c, /* character to write */
        (csbi.dwSize.X * csbi.dwSize.Y) - csbi.dwSize.X, /* number of chars */
        coordScreen, /* x and y coordinates of first cell */
        &cCharsWritten); /* receives number of cells written to */
    PERR(bSuccess, "FillConsoleOutputCharacter");
  }
  return;
}


FLUSH.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include "console.h"

/*********************************************************************
* FUNCTION: demoFlush(HANDLE hConOut)                                *
*                                                                    *
* PURPOSE: demonstrate FlushConsoleInputBuffer. Slowly read from the *
*          iput queue, allowing a backlog of input events to start   *
*          filling the queue. Flush the input queue after outputting *
*          every fifth character.                                    *
*                                                                    *
* INPUT: the output console handle to write to                       *
*********************************************************************/

void demoFlush(HANDLE hConOut)
{
  HANDLE hStdIn;
  INPUT_RECORD InputBuffer;
  DWORD dwInputEvents;
  int i = 0;
  BOOL bSuccess;
  DWORD dwBytesWritten;

  setConTitle(__FILE__);
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  myPuts(hConOut, "Type a number of characters quickly. I will read 5\n"
                  "characters from the input buffer with a Sleep() delay\n"
                  "which will allow it to fill with characters. After 5\n"
                  "characters I will flush the input buffer with\n"
                  "FlushConsoleInputBuffer and restart the sequence. Note\n"
                  "that any characters you've typed that haven't been read\n"
                  "yet are lost due to the flush.\n"
                  "Enter characters (hit ESC to return):");
  for(;;)
    {
    bSuccess = ReadConsoleInput(hStdIn, &InputBuffer, 1, &dwInputEvents);
    PERR(bSuccess, "ReadConsoleInput");
    /* is it a key down event? */
    if (InputBuffer.EventType == KEY_EVENT &&
        InputBuffer.Event.KeyEvent.bKeyDown)
      {
      if (InputBuffer.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
        return;
      /* write the ascii character out to the console */
      bSuccess = WriteFile(hConOut,
          &InputBuffer.Event.KeyEvent.uChar.AsciiChar,
          1, &dwBytesWritten, NULL);
      PERR(bSuccess, "WriteFile");
      Sleep(1000); /* pause for 1s */
      i++;
      if (i > 5)
        {
        /* flush the input buffer */
        bSuccess = FlushConsoleInputBuffer(hStdIn);
        PERR(bSuccess, "FlushConsoleInputBuffer");
        i = 0;
        }
      } /* if */
    } /* while */
  return;
}


GETLRGST.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include "console.h"

/*********************************************************************
* FUNCTION: demoGetLargest(HANDLE hConOut)                           *
*                                                                    *
* PURPOSE: demonstrate GetLargestConsoleWindowSize. Reports the size *
*          of the largest possible console window, given the current *
*          font.                                                     *
*                                                                    *
* INPUT: console input handle to query the information from and to   *
*        output to                                                   *
*********************************************************************/

void demoGetLargest(HANDLE hConOut)
{
  COORD coordLargest; /* hold the largest window size */
  CHAR szTemp[128];

  setConTitle(__FILE__);
  coordLargest = GetLargestConsoleWindowSize(hConOut);
  PERR(coordLargest.X | coordLargest.Y, "GetLargestConsoleWindowSize");
  myPuts(hConOut, "The largest console window size for this console, as\n"
                  "reported by GetLargestConsoleWindowSize, is:");
  sprintf(szTemp, "%d wide by %d high.", coordLargest.X, coordLargest.Y);
  myPuts(hConOut, szTemp);
  myPuts(hConOut, "\nHit enter to return...");
  myGetchar();
  return;
}


GETNUMEV.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "console.h"

/* maximum number of input queue events to peek at */
#define INPUT_RECS 256

/* array of records to store peeked events from the input queue */
INPUT_RECORD aInputBuffer[INPUT_RECS];

/*********************************************************************
* FUNCTION: demoGetNumEvents(HANDLE hConOut)                         *
*                                                                    *
* PURPOSE: demonstrate GetNumberOfConsoleInputEvents,                *
*          PeekConsoleInput, and ReadConsoleInput. Delay the         *
*          processing of console input to start filling the input    *
*          queue. The number of console events in the input queue    *
*          will be updated in the status line at the top of the      *
*          console. Peek the unread characters for an ESC and return *
*          when one is found                                         *
*                                                                    *
* INPUT: console output handle to write to                           *
*********************************************************************/

void demoGetNumEvents(HANDLE hConOut)
{
  BOOL bSuccess;
  DWORD dwNumEvents; /* number of events in the input queue */
  DWORD dwStdInMode; /* save the input mode here */
  HANDLE hStdIn;
  DWORD dwInputEvents; /* number of events read from the queue */
  CHAR bOutBuf[256], szTemp[256];
  /* indexes to latest unread event checked for ESC char */
  DWORD iEvent, iPrevEvent;
  DWORD dwEventsPeeked; /* number of events peeked at */
  unsigned i;
  DWORD dwCharsWritten;

  setConTitle(__FILE__);
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  myPuts(hConOut, "\nLet's display a running count of events waiting in the\n"
                  "console input queue by using the GetNumberOfConsoleInputEvents\n"
                  "API. I will enable mouse input in this console; try\n"
                  "generating a lot of mouse and keyboard events. I will\n"
                  "read them from the input queue using ReadConsoleInput\n"
                  "with .5 second delays between reads. Note the event\n"
                  "count and event information on the top line.\n\n"
                  "Hit ESC at any time to return. We will use\n"
                  "PeekConsoleInput to monitor the unread input queue\n"
                  "contents for an ESC character. When this character\n"
                  "is detected, we will flush the input queue and return\n"
                  "immediately. Note that for simplification of this\n"
                  "demo only the first 256 unread events will be scanned\n"
                  "to find an ESC keystroke.\n\n");

  bSuccess = GetConsoleMode(hStdIn, &dwStdInMode);
  PERR(bSuccess, "GetConsoleMode");
  /* when turning off ENABLE_LINE_INPUT, you MUST also turn off */
  /* ENABLE_ECHO_INPUT. */
  bSuccess = SetConsoleMode(hStdIn, (dwStdInMode & ~(ENABLE_LINE_INPUT |
      ENABLE_ECHO_INPUT)) | ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  iEvent = 0; /* index to last event peeked in the input queue */
  for(;;)
    {
    Sleep(500);
    bSuccess = GetNumberOfConsoleInputEvents(hStdIn, &dwNumEvents);
    PERR(bSuccess, "GetNumberOfConsoleInputEvents");
    sprintf(bOutBuf, "input queue events: %d", dwNumEvents);
    if (!dwNumEvents)
      /* put a status line on the first line */
      putStatusLine(hConOut, bOutBuf);
    else
      {
      /* save the previous index we've peeked at */
      iPrevEvent = iEvent;
      /* peek at the console input queue. Don't peek more than what will */
      /* fit in the buffer */
      bSuccess = PeekConsoleInput(hStdIn, aInputBuffer, min(dwNumEvents,
          INPUT_RECS), &dwEventsPeeked);
      PERR(bSuccess, "PeekConsoleInput");
      /* set current index to the highest number event peeked at */
      iEvent = dwEventsPeeked;
      /* scan unread events for an ESC key */
      for (i = iPrevEvent; i < iEvent; i++)
        {
        if (aInputBuffer[i].EventType == KEY_EVENT &&
            aInputBuffer[i].Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
          {
          /* set input mode back to what it was originally */
          bSuccess = SetConsoleMode(hStdIn, dwStdInMode);
          PERR(bSuccess, "SetConsoleMode");
          /* flush the input buffer and return */
          bSuccess = FlushConsoleInputBuffer(hStdIn);
          PERR(bSuccess, "FlushConsoleInputBuffer");
          return;
          }
        } /* for */
      } /* else */
    bSuccess = ReadConsoleInput(hStdIn, &aInputBuffer[0], 1, &dwInputEvents);
    PERR(bSuccess, "ReadConsoleInput");
    /* decrement "last peeked at" index by number of records we just read */
    iEvent -= dwInputEvents;
    switch (aInputBuffer[0].EventType)
      {
      case KEY_EVENT:
        if (aInputBuffer[0].Event.KeyEvent.bKeyDown)
          {
          if (aInputBuffer[0].Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
            {
            /* set input mode back to what it was originally */
            bSuccess = SetConsoleMode(hStdIn, dwStdInMode);
            PERR(bSuccess, "SetConsoleMode");
            return;
            }
          /* put the keystroke information on the status line */
          sprintf(szTemp, " keystroke: %c",
              aInputBuffer[0].Event.KeyEvent.uChar.AsciiChar);
          strcat(bOutBuf, szTemp);
          /* put the status line on the screen */
          putStatusLine(hConOut, bOutBuf);
          /* output the character read from the input queue */
          bSuccess = WriteFile(hConOut,
              &aInputBuffer[0].Event.KeyEvent.uChar.AsciiChar, 1,
              &dwCharsWritten, NULL);
          PERR(bSuccess, "WriteFile");
          }
        break;
      case MOUSE_EVENT:
        sprintf(szTemp, " mouse: %s at %d, %d",
            (aInputBuffer[0].Event.MouseEvent.dwEventFlags == MOUSE_MOVED ?
            "moved" : "clicked"), aInputBuffer[0].Event.MouseEvent.dwMousePosition.X,
            aInputBuffer[0].Event.MouseEvent.dwMousePosition.Y);
        strcat(bOutBuf, szTemp);
        putStatusLine(hConOut, bOutBuf);
        break;
      case WINDOW_BUFFER_SIZE_EVENT:
        sprintf(szTemp, " window: %d, %d",
            aInputBuffer[0].Event.WindowBufferSizeEvent.dwSize.X,
            aInputBuffer[0].Event.WindowBufferSizeEvent.dwSize.Y);
        strcat(bOutBuf, szTemp);
        putStatusLine(hConOut, bOutBuf);
        break;
      } /* switch */
    } /* while */
}


HANDLER.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <string.h>
#include "console.h"

HANDLE hConsole; /* current console output handle */

/*******************************************************************
* FUNCTION: handler_routine(DWORD dwCtrlType)                      *
*                                                                  *
* PURPOSE: this is the control handler routine activated by ctrl+c *
*          or ctrl+break keys                                      *
*                                                                  *
* INPUT: the type of control event                                 *
*******************************************************************/

BOOL WINAPI handler_routine(DWORD dwCtrlType)
{
  CHAR szTemp[64];

  /* print out what control event was received to the current console */
  switch(dwCtrlType)
    {
    case CTRL_C_EVENT:
      strcpy(szTemp, "CTRL_C_EVENT");
      break;
    case CTRL_BREAK_EVENT:
      strcpy(szTemp, "CTRL_BREAK_EVENT");
      break;
    case CTRL_CLOSE_EVENT:
      strcpy(szTemp, "CTRL_CLOSE_EVENT");
      break;
    case CTRL_LOGOFF_EVENT:
      strcpy(szTemp, "CTRL_LOGOFF_EVENT");
      break;
    case CTRL_SHUTDOWN_EVENT:
      strcpy(szTemp, "CTRL_SHUTDOWN_EVENT");
      break;
    default:
      strcpy(szTemp, "unknown event");
      break;
    }
  strcat(szTemp, " detected");
  myPuts(hConsole, szTemp);
  return(TRUE);
}

/*********************************************************************
* FUNCTION: demoSetCtrlHandler(HANDLE hConOut)                       *
*                                                                    *
* PURPOSE: demonstrate SetConsoleCtrlHandler by setting a ctrl+break *
*          and ctrl+c handler. When the user hits either one of      *
*          these keys, a message is printed to the console           *
*          indicating the event.                                     *
*                                                                    *
* INPUT: the output handle to write to                               *
*********************************************************************/

void demoSetCtrlHandler(HANDLE hConOut)
{
  BOOL bSuccess;

  setConTitle(__FILE__);
  hConsole = hConOut; /* set global console output handle for handler */
  myPuts(hConOut, "Let's install a ctrl+c and ctrl+break handler for this\n"
                  "process. Hit ctrl+c and ctrl+break a few times to test\n"
                  "the handler. Hit enter to return...");
  /* set handler for this process */
  bSuccess = SetConsoleCtrlHandler(handler_routine, TRUE);
  PERR(bSuccess, "SetConsoleCtrlHandler");
  /* wait for user to hit enter */
  while (myGetchar() != 0xd)
    ;
  /* now let's generate some control events */
  myPuts(hConOut, "Now we'll use GenerateConsoleCtrlEvent to generate a\n"
                  "ctrl+c and a ctrl+break event...\n");
  bSuccess = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
  PERR(bSuccess, "GenerateConsoleCtrlEvent");
  bSuccess = GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
  PERR(bSuccess, "GenerateConsoleCtrlEvent");
  Sleep(1000); /* give ctrl handle time to output messages */
  myPuts(hConOut, "\nNow choose 'Close' then 'Cancel' from the system\n"
                  "menu of this console and note that we receive a\n"
                  "CTRL_CLOSE_EVENT...\n");
  myPuts(hConOut, "\nHit enter to continue...");
  myGetchar();
  /* remove our handler from the list of handlers */
  bSuccess = SetConsoleCtrlHandler(handler_routine, FALSE);
  PERR(bSuccess, "SetConsoleCtrlHandler");
  return;
}


NUMBUT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include "console.h"

/*************************************************************
* FUNCTION: demoGetNumBut(HANDLE hConOut)                    *
*                                                            *
* PURPOSE: simply report the number of buttons on your mouse *
*                                                            *
* INPUT: the console output handle to write to               *
*************************************************************/

void demoGetNumBut(HANDLE hConOut)
{
  DWORD dwNumMouseButtons;
  BOOL bSuccess;
  CHAR szTemp[256];

  setConTitle(__FILE__);
  bSuccess = GetNumberOfConsoleMouseButtons(&dwNumMouseButtons);
  PERR(bSuccess, "GetNumberOfConsoleMouseButtons");
  myPuts(hConOut, "Using GetNumberOfConsoleMouseButtons to obtain the\n"
                  "number of buttons on your mouse...");
  sprintf(szTemp, "Your mouse has %d buttons.", dwNumMouseButtons);
  myPuts(hConOut, szTemp);
  myPuts(hConOut, "\nHit enter to return...");
  myGetchar();
  return;
}


READCHAR.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <string.h>
#include <malloc.h>
#include "console.h"

/*********************************************************************
* FUNCTION: demoReadConChar(HANDLE hConOut)                          *
*                                                                    *
* PURPOSE: demonstrate ReadConsoleOutputCharacter. Read the text on  *
*          line that the user clicks on and output it to the console *
*                                                                    *
* INPUT: the console output handle to write to                       *
*********************************************************************/

void demoReadConChar(HANDLE hConOut)
{
  BOOL bSuccess;
  INPUT_RECORD inputBuffer;
  DWORD dwStdInMode;
  HANDLE hStdIn;
  DWORD dwInputEvents;
  COORD coordLine; /* coordinates of where to read characters from */
  CHAR *szLine;  /* buffer to hold the line read from the console */
  DWORD dwCharsRead;
  int i;

  setConTitle(__FILE__);
  myPuts(hConOut, "Click on any line containing characters. I will use\n"
                  "ReadConsoleOutputCharacter to read that line of text into\n"
                  "a buffer, then print that buffer to the console at the\n"
                  "current cursor position. Hit ESC to return.\n\n");
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  /* save the console mode */
  bSuccess = GetConsoleMode(hStdIn, &dwStdInMode);
  PERR(bSuccess, "GetConsoleMode");
  /* enable mouse input */
  bSuccess = SetConsoleMode(hStdIn, dwStdInMode | ENABLE_MOUSE_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  /* allocate space for one line */
  szLine = (char *) malloc(getConX(hConOut));
  PERR(szLine, "malloc");
  for(;;)
    {
    /* get a single input event */
    bSuccess = ReadConsoleInput(hStdIn, &inputBuffer, 1, &dwInputEvents);
    PERR(bSuccess, "ReadConsoleInput");
    switch (inputBuffer.EventType)
      {
      case KEY_EVENT:
        /* is it an ESC key? */
        if (inputBuffer.Event.KeyEvent.bKeyDown &&
            inputBuffer.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
          {
          /* set input mode back to what it was originally and return */
          bSuccess = SetConsoleMode(hStdIn, dwStdInMode);
          PERR(bSuccess, "SetConsoleMode");
          free(szLine); /* free allocated space for a text line */
          return;
          }
        break;
      case MOUSE_EVENT:
        /* was this was a click event? Is any button down or not? */
        if (inputBuffer.Event.MouseEvent.dwEventFlags != MOUSE_MOVED &&
            inputBuffer.Event.MouseEvent.dwButtonState)
          {
          /* read the line where the mouse is, starting at column 0 */
          coordLine.X = 0;
          coordLine.Y = inputBuffer.Event.MouseEvent.dwMousePosition.Y;
          bSuccess = ReadConsoleOutputCharacter(hConOut, szLine,
              getConX(hConOut), coordLine, &dwCharsRead);
          PERR(bSuccess, "ReadConsoleOutputCharacter");
          /* strip trailing spaces */
          i = getConX(hConOut) - 1;
          szLine[i--] = 0; /* null terminate */
          while (szLine[i] == ' ')
            szLine[i--] = 0;
          myPuts(hConOut, szLine);
          }
      } /* switch */
    } /* while */
}


ALOCFREE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include "console.h"

/*********************************************************************
* FUNCTION: demoAllocFree(HANDLE hConOld, HANDLE *hConOut)           *
*                                                                    *
* PURPOSE: demonstrate FreeConsole & AllocConsole. Free the console  *
*          and allocate a new one                                    *
*                                                                    *
* INPUT: the current console output handle and a temporary 'scratch' *
*        console handle                                              *
*********************************************************************/

void demoAllocFree(HANDLE hConOld, HANDLE *hConOut)
{
  BOOL bSuccess;

  setConTitle(__FILE__);
  myPuts(hConOld, "Let's free the current console with FreeConsole. It will\n"
                  "disappear for 3 seconds, then we'll allocate a new one.\n"
                  "with AllocConsole. Hit return to continue...");
  myGetchar();
  bSuccess = FreeConsole();
  PERR(bSuccess, "FreeConsole");
  Sleep(3000);
  bSuccess = AllocConsole();
  PERR(bSuccess, "AllocConsole");
  *hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
  /* set our console text attribute */
  bSuccess = SetConsoleTextAttribute(*hConOut, FOREGROUND_CYAN);
  PERR(bSuccess, "SetConsoleTextAttribute");
  myPuts(*hConOut, "This is a new console that we've allocated with AllocConsole.");
  Sleep(2000);
  /* must refresh the screen since we've replaced the console */
  showConAPIs(*hConOut);
  return;
}


READOUT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <string.h>
#include <malloc.h>
#include "console.h"

/********************************************************************
* FUNCTION: invertRectangle(HANDLE hConOut, COORD coordStart,       *
*                           COORD coordCur)                         *
*                                                                   *
* PURPOSE: invert the attributes of the rectangle defined as the    *
*          area between two input COORD parameters                  *
*                                                                   *
* INPUT: the output handle of the console to make the changes to,   *
*        and two sets of COORD structures defining the rectangle to *
*        invert                                                     *
********************************************************************/

void invertRectangle(HANDLE hConOut, COORD coordStart, COORD coordCur)
{
  WORD *awLineAttr;
  unsigned i;
  DWORD dwBoxWidth;
  WORD top, left, right, bottom;
  BOOL bSuccess;
  DWORD dwNumAttrsRead;

  /* convert coordSart to upper left & coordCur to lower right */
  top = min(coordStart.Y, coordCur.Y);
  bottom = max(coordStart.Y, coordCur.Y);
  left = min(coordStart.X, coordCur.X);
  right = max(coordStart.X, coordCur.X);
  coordStart.X = left;
  coordStart.Y = top;
  coordCur.X = right;
  coordCur.Y = bottom;
  /* get the width of the selected area */
  dwBoxWidth = right - left + 1;
  /* allocate a buffer for the attributes read from the console */
  awLineAttr = (PWORD) malloc(getConX(hConOut) * sizeof(DWORD));
  PERR(awLineAttr, "malloc");
  /* for each line in the selected area */
  while (coordStart.Y <= coordCur.Y)
    {
    /* read the attributes on that line */
    bSuccess = ReadConsoleOutputAttribute(hConOut, awLineAttr,
        dwBoxWidth, coordStart, &dwNumAttrsRead);
    PERR(bSuccess, "ReadConsoleOutputAttribute");
    /* for each attribute in the line, invert the color */
    for (i = 0; i < dwBoxWidth; i++)
      awLineAttr[i] = ~awLineAttr[i];
    /* put the inverted color back on the line */
    bSuccess = WriteConsoleOutputAttribute(hConOut, awLineAttr, dwBoxWidth,
        coordStart, &dwNumAttrsRead);
    PERR(bSuccess, "WriteConsoleOutputAttribute");
    coordStart.Y++; /* go to the next line */
    }
  free(awLineAttr); /* free allocated attribute buffer */
  return;
}

/********************************************************************
* FUNCTION: demoReadConOut(HANDLE hConOut)                          *
*                                                                   *
* PURPOSE: demonstrate ReadConsoleOutput, WriteConsoleOutput,       *
*          ReadConsoleOutputAttribute, and                          *
*          WriteConsoleOutputAttribute. Implement a cut and past    *
*          function; as the user clicks and drags the mouse, invert *
*          the selection rectangle to show the area selected. When  *
*          the mouse button is released, copy the selected area and *
*          paste it to the console                                  *
*                                                                   *
* INPUT: the output handle to read from and write to                *
********************************************************************/

void demoReadConOut(HANDLE hConOut)
{
  BOOL bSuccess;
  INPUT_RECORD inputBuffer;
  DWORD dwStdInMode;
  HANDLE hStdIn;
  DWORD dwInputEvents;
  COORD coordStart, coordEnd, coordDest = {0, 0};
  BOOL bDragMode = FALSE; /* mode flag - selecting (dragging) or not? */
  COORD coordPrev; /* coordinates of the start of the selected area */
  CHAR_INFO *pCharInfo; /* buffer to store info from ReadConsoleOutput */
  COORD coordBufSize; /* the size of the buffer to read into */
  SMALL_RECT srReadRegion; /* source rectangle to read from */
  WORD top, left, right, bottom; /* sides of the source rectangle */
  CONSOLE_SCREEN_BUFFER_INFO csbi; /* used to get cursor position */

  setConTitle(__FILE__);
  myPuts(hConOut, "Let's implement a simple cut & paste with the left mouse\n"
                  "button. Click and drag to select an area of the screen\n"
                  "to copy. We will read, invert, then write the screen attributes\n"
                  "of the area you are dragging over to show the current\n"
                  "selection. Release the mouse button and I will read the\n"
                  "screen contained in the output area and print the contents\n"
                  "of the buffer to the screen at the current cursor location.\n"
                  "Hit ESC to return.\n\n");
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  /* save the console mode */
  bSuccess = GetConsoleMode(hStdIn, &dwStdInMode);
  PERR(bSuccess, "GetConsoleMode");
  /* enable mouse input */
  bSuccess = SetConsoleMode(hStdIn, dwStdInMode | ENABLE_MOUSE_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  for(;;)
    {
    /* get an input event */
    bSuccess = ReadConsoleInput(hStdIn, &inputBuffer, 1, &dwInputEvents);
    PERR(bSuccess, "ReadConsoleInput");
    switch (inputBuffer.EventType)
      {
      case KEY_EVENT:
        /* is it an ESC character? */
        if (inputBuffer.Event.KeyEvent.bKeyDown &&
            inputBuffer.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
          {
          /* set input mode back to what it was originally and return */
          bSuccess = SetConsoleMode(hStdIn, dwStdInMode);
          PERR(bSuccess, "SetConsoleMode");
          return;
          }
        break;
      case MOUSE_EVENT:
        /* was this was a click event? */
        if (inputBuffer.Event.MouseEvent.dwEventFlags != MOUSE_MOVED)
          /* is only leftmost button down? */
          {
          if(inputBuffer.Event.MouseEvent.dwButtonState ==
              FROM_LEFT_1ST_BUTTON_PRESSED)
            {
            /* save the starting coordinates of the selected area */
            coordStart = coordPrev =
                inputBuffer.Event.MouseEvent.dwMousePosition;
            /* invert the single char attribute the cursor is on */
            invertRectangle(hConOut, coordStart, coordStart);
            bDragMode = TRUE; /* we are now in 'drag' mode */
            }
          else /* button #1 has just been released */
            {
            /* if we're not in drag mode, break; we might get a mouse */
            /* button release when gaining the focus */
            if (!bDragMode)
              break;
            /* save the coordinates of the mouse release */
            coordEnd = inputBuffer.Event.MouseEvent.dwMousePosition;
            bDragMode = FALSE; /* we're not in 'drag' mode anymore */
            /* invert the rectangle back to normal */
            invertRectangle(hConOut, coordStart, coordEnd);
            /* copy the buffer in the rectangle and output it. First convert */
            /* coordSart to upper left & coordCur to lower right */
            top = min(coordStart.Y, coordEnd.Y);
            left = min(coordStart.X, coordEnd.X);
            bottom = max(coordStart.Y, coordEnd.Y);
            right = max(coordStart.X, coordEnd.X);
            coordStart.X = srReadRegion.Left = left;
            coordStart.Y = srReadRegion.Top = top;
            coordEnd.X = srReadRegion.Right = right;
            coordEnd.Y = srReadRegion.Bottom = bottom;
            /* get the size of the selected rectangle */
            coordBufSize.X = coordEnd.X - coordStart.X + (SHORT) 1;
            coordBufSize.Y = coordEnd.Y - coordStart.Y + (SHORT) 1;
            /* allocate a buffer for the screen rectangle */
            pCharInfo = malloc(coordBufSize.X * coordBufSize.Y *
                sizeof(CHAR_INFO));
            PERR(pCharInfo, "malloc");
            /* read the screen rectangle into the buffer */
            bSuccess = ReadConsoleOutput(hConOut, /* output buffer */
                pCharInfo, /* buffer that receives data */
                coordBufSize, /* col/row size of destination buffer */
                coordDest, /* upper-left cell to write data to in dest */
                &srReadRegion); /* screen buffer rectangle to read from */
            PERR(bSuccess, "ReadConsoleOutput");
            /* get current cursor position */
            bSuccess = GetConsoleScreenBufferInfo(hConOut, &csbi);
            PERR(bSuccess, "GetConsoleScreenBufferInfo");
            /* set up an output destination rectangle for the buffer. */
            /* Put it just below the current cursor position. */
            srReadRegion.Top = csbi.dwCursorPosition.Y;
            srReadRegion.Bottom = csbi.dwCursorPosition.Y + (bottom - top);
            srReadRegion.Left = 0;
            srReadRegion.Right = right - left;
            bSuccess = WriteConsoleOutput(hConOut, /* output buffer */
                pCharInfo, /* buffer with data to write */
                coordBufSize, /* col/row size of source buffer */
                coordDest, /* upper-left cell to write data from in src */
                &srReadRegion); /* screen buffer rect to write data to */
            PERR(bSuccess, "WriteConsoleOutput");
            free(pCharInfo);
            }
          }
        /* is it a mouse movement while we are in drag mode? */
        if (inputBuffer.Event.MouseEvent.dwEventFlags == MOUSE_MOVED &&
            bDragMode)
          {
          /* if the cursor moves from the rectangle it was previously in, */
          /* invert the old rectangle then invert the new rectangle. */
          /* Compare the current coordinates with the previous coordinates */
          if (memcmp(&inputBuffer.Event.MouseEvent.dwMousePosition,
              &coordPrev, sizeof(COORD)))
            {
            /* You could be clever and only invert what needs to be */
            /* inverted rather than the entire two rectangles... */
            /* Invert the old rectangle */
            invertRectangle(hConOut, coordStart, coordPrev);
            /* and invert the new rectangle */
            invertRectangle(hConOut, coordStart,
                inputBuffer.Event.MouseEvent.dwMousePosition);
            /* save the current mouse position to the previous position */
            memcpy(&coordPrev,
                &inputBuffer.Event.MouseEvent.dwMousePosition,
                sizeof(COORD));
            }
          }
        break;
      } /* switch */
    } /* while */
}


SCROLL.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include "console.h"

/* The following is the sample text that we will place on the lower
half of the screen to scroll: */

static PCHAR szSampTxt =
  "BOOL ScrollConsoleScreenBuffer( hConsoleOutput, lpScrollRectangle,\n"
  "                               lpClipRectangle, dwDestinationOrigin,\n"
  "                               lpFill)\n"
  "HANDLE hConsoleOutput;\n"
  "PSMALL_RECT lpScrollRectangle;\n"
  "PSMALL_RECT lpClipRectangle;\n"
  "COORD dwDestinationOrigin;\n"
  "PCHAR_INFO lpFill;\n"
  "\n"
  "This function may be used to scroll the data in the screen buffer.\n"
  "\n"
  "Parameter            Description\n"
  "                                                                            \n"
  "hConsoleOutput       Supplies an open handle to console output.\n"
  "lpScrollRectangle    Pointer to region within screen buffer to move.\n"
  "lpClipRectangle      Pointer to region within screen buffer that may be\n"
  "                     affected by this scroll. This pointer may be NULL.\n"
  "dwDestinationOrigin  Upper left corner of new location of ScrollRectangle\n"
  "                     contents.\n"
  "lpFill               Pointer to structure containing new contents of\n"
  "                     ScrollRectangle region.\n"
  "\n"
  "Return Value\n"
  "The return value is TRUE if the function was successful, otherwise it is\n"
  "FALSE in which case extended error information can be retrieved by calling\n"
  "the GetLastError function.\n"
  "\n"
  "Comments\n"
  "This function copies the contents of a rectangular region of the screen\n"
  "buffer, the scroll region, to another area of the screen buffer, the target\n"
  "region. The target region is defined as a rectangle the same dimensions as\n"
  "the scroll region with the upper left corner at dwDestinationOrigin. Each\n"
  "cell in the scroll region is then filled with the contents of Fill. Any\n"
  "overlap between the scroll region and the target region is not filled. The\n"
  "clip rectangle applies to changes made in both the ScrollRectangle and the\n"
  "destination rectangle, i.e. if the clip rectangle does not include the\n"
  "scroll rectangle, the scroll rectangle will not be updated with the contents";

/*******************************************************************
* FUNCTION: demoScrollCon(HANDLE hConOut)                          *
*                                                                  *
* PURPOSE: demonstrate ScrollConsoleScreenBuffer. Scroll the lower *
*          half of the console with each mouse click               *
*                                                                  *
* INPUT: the console output handle to scroll                       *
********************************************************************/

void demoScrollCon(HANDLE hConOut)
{
  BOOL bSuccess;
  INPUT_RECORD inputBuffer;
  DWORD dwStdInMode;
  HANDLE hStdIn;
  DWORD dwInputEvents;
  COORD coordDest; /* destination of scroll movement */
  BOOL bDragMode = FALSE;
  CHAR_INFO chiFill; /* char and attributes to fill empty space with */
  CONSOLE_SCREEN_BUFFER_INFO csbi; /* used to get current attribute */
  SMALL_RECT srctScrollRect; /* area of the screen to scroll */

  setConTitle(__FILE__);
  myPuts(hConOut, "Let's use ScrollConsoleScreenBuffer to scroll the lower");
  myPuts(hConOut, "half of the screen up with each mouse click. I'll fill the");
  myPuts(hConOut, "screen with some sample text so you can see the effect.");
  myPuts(hConOut, "Hit return to continue, and hit ESC at any time to return.\n");
  myGetchar();
  myPuts(hConOut, szSampTxt);
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  bSuccess = GetConsoleMode(hStdIn, &dwStdInMode);
  PERR(bSuccess, "GetConsoleMode");
  bSuccess = SetConsoleMode(hStdIn, dwStdInMode | ENABLE_MOUSE_INPUT);
  PERR(bSuccess, "SetConsoleMode");
  /* define region we want to move */
  srctScrollRect.Top = getConY(hConOut) / 2;
  srctScrollRect.Bottom = getConY(hConOut) - 1;
  srctScrollRect.Left = 0;
  srctScrollRect.Right = getConX(hConOut) - 1;
  /* define origin where we want to move the scrolled region */
  coordDest.X = 0;
  coordDest.Y = (getConY(hConOut) / 2) - 1;
  /* get current attributes and fill out CHAR_INFO structure for fill char */
  bSuccess = GetConsoleScreenBufferInfo(hConOut, &csbi);
  PERR(bSuccess, "GetConsoleScreenBufferInfo");
  chiFill.Char.AsciiChar = ' ';
  chiFill.Attributes = csbi.wAttributes;
  for(;;)
    {
    bSuccess = ReadConsoleInput(hStdIn, &inputBuffer, 1, &dwInputEvents);
    PERR(bSuccess, "ReadConsoleInput");
    switch (inputBuffer.EventType)
      {
      case KEY_EVENT:
        if (inputBuffer.Event.KeyEvent.bKeyDown &&
            inputBuffer.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
          {
          /* set input mode back to what it was originally */
          bSuccess = SetConsoleMode(hStdIn, dwStdInMode);
          PERR(bSuccess, "SetConsoleMode");
          return;
          }
        break;
      case MOUSE_EVENT:
        /* was this was a click or double click event? Is any button down? */
        if (inputBuffer.Event.MouseEvent.dwEventFlags != MOUSE_MOVED &&
            inputBuffer.Event.MouseEvent.dwButtonState)
          {
          bSuccess = ScrollConsoleScreenBuffer(hConOut,
              &srctScrollRect,
              NULL, /* no clipping rectangle */
              coordDest, /* coordinates of destination */
              &chiFill); /* attribute to fill empty space with */
          PERR(bSuccess, "ScrollConsoleScreenBuffer");
          }
        break;
      } /* switch */
    } /* while */
}


SIZE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include "console.h"

/*********************************************************************
* FUNCTION: resizeConBufAndWindow(HANDLE hConsole, SHORT xSize,      *
*                                 SHORT ySize)                       *
*                                                                    *
* PURPOSE: resize both the console output buffer and the console     *
*          window to the given x and y size parameters               *
*                                                                    *
* INPUT: the console output handle to resize, and the required x and *
*        y size to resize the buffer and window to.                  *
*                                                                    *
* COMMENTS: Note that care must be taken to resize the correct item  *
*           first; you cannot have a console buffer that is smaller  *
*           than the console window.                                 *
*********************************************************************/

void resizeConBufAndWindow(HANDLE hConsole, SHORT xSize, SHORT ySize)
{
  CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
  BOOL bSuccess;
  SMALL_RECT srWindowRect; /* hold the new console size */
  COORD coordScreen;

  bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
  PERR(bSuccess, "GetConsoleScreenBufferInfo");
  /* get the largest size we can size the console window to */
  coordScreen = GetLargestConsoleWindowSize(hConsole);
  PERR(coordScreen.X | coordScreen.Y, "GetLargestConsoleWindowSize");
  /* define the new console window size and scroll position */
  srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
  srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
  srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
  /* define the new console buffer size */
  coordScreen.X = xSize;
  coordScreen.Y = ySize;
  /* if the current buffer is larger than what we want, resize the */
  /* console window first, then the buffer */
  if ((DWORD) csbi.dwSize.X * csbi.dwSize.Y > (DWORD) xSize * ySize)
    {
    bSuccess = SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect);
    PERR(bSuccess, "SetConsoleWindowInfo");
    bSuccess = SetConsoleScreenBufferSize(hConsole, coordScreen);
    PERR(bSuccess, "SetConsoleScreenBufferSize");
    }
  /* if the current buffer is smaller than what we want, resize the */
  /* buffer first, then the console window */
  if ((DWORD) csbi.dwSize.X * csbi.dwSize.Y < (DWORD) xSize * ySize)
    {
    bSuccess = SetConsoleScreenBufferSize(hConsole, coordScreen);
    PERR(bSuccess, "SetConsoleScreenBufferSize");
    bSuccess = SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect);
    PERR(bSuccess, "SetConsoleWindowInfo");
    }
  /* if the current buffer *is* the size we want, don't do anything! */
  return;
}

/*********************************************************************
* FUNCTION: demoSizeInfo(HANDLE hConOut)                             *
*                                                                    *
* PURPOSE: demonstrate SetConsoleWindowInfo and                      *
*          SetConsoleScreenBufferSize. Resize the console buffer and *
*          window                                                    *
*                                                                    *
* INPUT: console output handle to set the information for            *
*********************************************************************/

void demoSizeInfo(HANDLE hConOut)
{
  SHORT sConX, sConY; /* save the current console dimensions */

  setConTitle(__FILE__);
  myPuts(hConOut, "Let's resize the console buffer and window to a 40 x 25\n"
                  "size screen by using the SetConsoleScreenBufferSize and\n"
                  "SetConsoleWindowInfo APIs. Hit enter to continue...\n");
  myGetchar();
  sConX = getConX(hConOut);
  sConY = getConY(hConOut);
  resizeConBufAndWindow(hConOut, (SHORT) 40, (SHORT) 25);
  myPuts(hConOut, "Now let's resize to a large size of\n"
                  "200 x 200 - notice that the console\n"
                  "window size will not grow larger than\n"
                  "the physical screen size. Hit enter\n"
                  "to continue...\n");
  myGetchar();
  resizeConBufAndWindow(hConOut, (SHORT) 200, (SHORT) 200);
  myPuts(hConOut, "Now let's resize back to our original size screen.\n"
                  "Hit enter to continue...\n");
  myGetchar();
  resizeConBufAndWindow(hConOut, sConX, sConY);
  myPuts(hConOut, "Now we're back to our original size. Hit enter to return...");
  myGetchar();
  return;
}


SIZE.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

extern void resizeConBufAndWindow(HANDLE hConsole, SHORT xSize,
    SHORT ySize);


WRITEIN.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <memory.h>
#include <stdio.h>
#include <ctype.h>
#include "console.h"

#define MAX_MACRO_EVENTS 256
#define ALT_PRESSED (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
#define CONTROL_KEY (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | \
                     RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED | ENHANCED_KEY)

/************************************************************************
* FUNCTION: demoWriteIn(HANDLE hConOut)                                 *
*                                                                       *
* PURPOSE: demonstrate WriteConsoleInput by implementing a macro        *
*          recording facility that writes the recorded keystrokes to    *
*          the input queue for playback.                                *
*                                                                       *
* INPUT: the console buffer to record from and playback to              *
*                                                                       *
* RETURNS: none                                                         *
*************************************************************************/

void demoWriteIn(HANDLE hConOut)
{
  BOOL bSuccess;
  HANDLE hStdIn; /* standard input handle */
  INPUT_RECORD inputBuffer; /* buffer to hold a single console input record */
  INPUT_RECORD irMacroBuf[MAX_MACRO_EVENTS]; /* array of input events */
  DWORD dwInputEvents;
  DWORD dwBytesWritten, dwRecordsWritten;
  CHAR bOutBuf[256]; /* buffer to format event information into */
  BOOL bRecording = FALSE; /* TRUE if recording a macro */
  int iir = 0; /* index into macro input buffer */
  CHAR c;

  setConTitle(__FILE__);
  myPuts(hConOut, "\n\nLet's implement a simple macro record/playback facility.\n"
                  "Hit Alt+R to record, and Alt+P to playback. After turning\n"
                  "on Record mode, enter some keystrokes or mouse clicks.\n"
                  "Hit Alt+R to turn Record mode off, and Alt+P to playback\n"
                  "your keystrokes. I'll use the WriteConsoleInput API to\n"
                  "insert the recorded events into the input buffer.\n"
                  "Hit ESC at any time to return.");
  hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  for(;;)
    {
    /* if our macro event buffer is full, leave Record mode */
    if (iir == MAX_MACRO_EVENTS && bRecording)
      {
      bRecording = FALSE;
      putStatusLine(hConOut, "");
      }
    /* read an input event from the input event queue */
    bSuccess = ReadConsoleInput(hStdIn, &inputBuffer, 1, &dwInputEvents);
    PERR(bSuccess, "ReadConsoleInput");
    switch (inputBuffer.EventType)
      {
      case KEY_EVENT:
        if (inputBuffer.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
          return;
        /* get the character that we read */
        c = inputBuffer.Event.KeyEvent.uChar.AsciiChar;
        if (!bRecording)
          {
          /* ignore key releases */
          if (!inputBuffer.Event.KeyEvent.bKeyDown)
            break;
          /* display the key event info on the status line */
          sprintf(bOutBuf, "key: virtual=%d ascii=%c",
              inputBuffer.Event.KeyEvent.wVirtualKeyCode, c);
          putStatusLine(hConOut, bOutBuf);
          /* if it's printable, and no control keys are down, show it */
          if (isprint(c) && !(inputBuffer.Event.KeyEvent.dwControlKeyState &
                CONTROL_KEY))
            {
            bSuccess = WriteFile(hConOut, &c, sizeof(c), &dwBytesWritten,
                NULL);
            PERR(bSuccess, "WriteFile");
            }
          /* is it an 'r' key? Is either the left or right ALT key down? */
          if (c == 'r' && (inputBuffer.Event.KeyEvent.dwControlKeyState &
              (ALT_PRESSED)))
            {
            bRecording = TRUE;
            iir = 0; /* reset macro buffer pointer to beginning */
            putStatusLine(hConOut, "RECORDING...");
            }
          /* is it a 'p' key? Is either the left of right ALT key down? */
          if (c == 'p' && (inputBuffer.Event.KeyEvent.dwControlKeyState &
              (ALT_PRESSED)))
            {
            /* stuff all the input events into the input buffer */
            bSuccess = WriteConsoleInput(hStdIn, /* console input buffer */
                irMacroBuf, /* address of the buffer for write data */
                iir, /* number of records to write */
                &dwRecordsWritten); /* returns number of records written */
            PERR(bSuccess, "WriteConsoleInput");
            }
          }
        else /* we're recording */
          {
          if (inputBuffer.Event.KeyEvent.bKeyDown)
            {
            /* is it an 'r' key? Is either the left or right ALT key down? */
            if (c == 'r' && (inputBuffer.Event.KeyEvent.dwControlKeyState &
                (ALT_PRESSED)))
              {
              bRecording = FALSE;
              putStatusLine(hConOut, "");
              break;
              }
            /* we need to ignore the 'playback' keystroke or we'll */
            /* automatically start playing back during playback! */
            if (c == 'p' && (inputBuffer.Event.KeyEvent.dwControlKeyState &
                (ALT_PRESSED)))
              break;
            /* if it's printable, and no control keys are down, show it */
            if (isprint(c) && !(inputBuffer.Event.KeyEvent.dwControlKeyState &
                CONTROL_KEY))
              {
              bSuccess = WriteFile(hConOut, &c, sizeof(c), &dwBytesWritten,
                  NULL);
              PERR(bSuccess, "WriteFile");
              }
            }
          /* store the key event in the macro buffer */
          memcpy(&irMacroBuf[iir++], &inputBuffer, sizeof(inputBuffer));
          } /* else */
        break;
      case MOUSE_EVENT:
        if (!bRecording)
          {
          sprintf(bOutBuf, "mouse: %s at %d, %d",
              (inputBuffer.Event.MouseEvent.dwEventFlags == MOUSE_MOVED ?
              "moved" : "clicked"), inputBuffer.Event.MouseEvent.dwMousePosition.X,
              inputBuffer.Event.MouseEvent.dwMousePosition.Y);
          putStatusLine(hConOut, bOutBuf);
          }
        else /* copy the mouse event into the macro buffer */
          memcpy(&irMacroBuf[iir++], &inputBuffer, sizeof(inputBuffer));
        break;
      case WINDOW_BUFFER_SIZE_EVENT:
        if (!bRecording)
          {
          sprintf(bOutBuf, "window: %d, %d",
              inputBuffer.Event.WindowBufferSizeEvent.dwSize.X,
              inputBuffer.Event.WindowBufferSizeEvent.dwSize.Y);
          putStatusLine(hConOut, bOutBuf);
          Sleep(1000);
          }
        break;
      } /* switch */
    } /* while */
  return;
}


README.TXT

Standard DLL & Example of Creating a Custom Control Class

SUMMARY
=======

The SPINCUBE sample provides a generic Win32 DLL template. It demonstrates
the use of DLL entry points, exported variables, and using the C run-time in
a DLL. This sample also provides a functional example of how to create a
custom control class that can be used by applications, such as SPINTEST.EXE,
as well as the Dialog Editor.

MORE INFORMATION
================

SPINCUBE.DLL contains the control window procedure and the interface
functions required by the Dialog Editor (see SPINCUBE.C), as well as the
control paint routines (see PAINT.C). SPINTEST.EXE is a small test program
that loads SPINCUBE.DLL and creates a few of the custom controls.

To test SPINCUBE with the Dialog Editor:

 1. Start the editor. From the File menu, choose Open Custom.
 2. Enter the path and filename of SPINCUBE.DLL.
 3. Create a new dialog box and choose a custom control button from the
    control palette (lower-right corner).
 4. Click the dialog box to create a SPINCUBE control.
 5. Save the dialog box template.
 6. Inspect the .DLG file that was created.


PAINT.C

/******************************************************************************\
*
*  MODULE:      PAINT.C
*
*  PURPOSE:     This is the module responsible for painting the SPINCUBE
*               custom control. When Paint() is called we retrieve a
*               pointer to a SPINCUBEINFO structure, and then use it's
*               current rotation & translation values to transform the
*               polyhedron described by gNormalizedVertices & gaiFacets.
*               Once we've transformed the vertices, we draw the
*               background, which consists of a grey rectangle and a few
*               black lines (a crass attempt to render a perspective
*               view into a "room"), on the offscreen bitmap associated
*               with the control (i.e. pSCI->hbmCompat). Then we walk the
*               facet list of the transformed polyhedron (gXformedVertices
*               & gaiFacets), drawing only those facets whose outward
*               normal faces us (again, drawing on pSCI->hbmCompat).
*               Finally, we BitBlt the appropriate rectangle from our
*               offscreen bitmap to the screen itself.
*
*               Drawing to the offscreen bitmap has two advantages over
*               drawing straight to the screen:
*
*                 1. The actual drawing the user sees consists of only
*                    a single BitBlt. Otherwise, the user would see us
*                    both erase the polyhedron in it's old position and
*                    draw it in it's new position (alot of flashing- not
*                    very smooth animation).
*
*                 2. When a spincube control with the SS_ERASE style
*                    is brought to the foreground, all it's contents
*                    i.e. the cube trails) are saved & can be re-Blted
*                    to the screen. Otherwise, all this info would be
*                    lost & there'd be a big blank spot in the middle
*                    of the control!
*
*               Interested persons should consult a text on 3 dimensional
*               graphics for more information (i.e. "Computer Graphics:
*               Principles and Practice", by Foley & van Dam).
*
*               Notes:
*
*               - A 3x2 tranformation matrix  is used instead of a  3x3
*                 matrix, since the transformed z-values aren't needed.
*                 (Normally these would be required for use in depth
*                 sorting  [for hidden surface removal], but  since we
*                 draw  only  a single convex polyhedron this  is not
*                 necessary.)
*
*               - A simplified perspective viewing transformation
*                 (which also  precludes the need for the transformed z
*                 coordinates). In a nutshell, the perspective  scale
*                 is as follows:
*
*                                    p' = S    x  p
*                                          per
*
*                 where:
*                        S    = WindowDepth /
*                         per      (WindowDepth + fCurrentZTranslation)
*
*                 (WindowDepth is  the greater of the  control's window
*                 height or window width.)
*
*
*  FUNCTIONS:   Paint()                         - the paint routine
*               TransformVertices()             - transforms vertices
*               ComputeRotationTransformation() - computes xformation
*                                                 based on current x, y
*                                                 and z rotation angles
*
*
*                           Microsoft Developer Support
*                  Copyright 1992 - 1998 Microsoft Corporation
*
\******************************************************************************/

#include <windows.h>
#include <math.h>
#include <stdlib.h>
#include "spincube.h"
#include "paint.h"

/******************************************************************************\
*
*  FUNCTION:     Paint
*
*  INPUTS:       hwnd - Handle of the window to draw into.
*
*  COMMENTS:     Draws window background & a polyhedron in the window.
*
\******************************************************************************/

void Paint (HWND hwnd)
{
  PSPINCUBEINFO  pSCI;
  RECT           rect;
  int            i;
  LONG           lScaleFactor;
  PAINTSTRUCT    ps;
  HRGN           hrgnClip;
  HBRUSH         hBrush, hBrushSave;
  int            iX, iY, iCX, iCY;
  int            facetIndex, numPoints;
  POINT          polygn[MAX_POINTS_PER_FACET];
  POINT          vector1, vector2;
  COLORREF       acrColor[6] = { 0x0000ff, 0x00ff00, 0xff0000,
                                 0x00ffff, 0xff00ff, 0xffff00 };

  pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd, GWL_SPINCUBEDATA);

  BeginPaint (hwnd, &ps);

  if (memcmp((void *)&ps.rcPaint, (void *)&pSCI->rcCubeBoundary, sizeof(RECT))
      & !REPAINT_BKGND(pSCI))

  {
    //
    // We're not here because it's time to animate (i.e. this paint isn't
    //   the result of a WM_TIMER), so just do the Blt & blow out of here...
    //

    BitBlt (ps.hdc,
            ps.rcPaint.left,
            ps.rcPaint.top,
            ps.rcPaint.right - ps.rcPaint.left,
            ps.rcPaint.bottom - ps.rcPaint.top,
            pSCI->hdcCompat, ps.rcPaint.left,
            ps.rcPaint.top, SRCCOPY);

    EndPaint (hwnd, &ps);
    return;
  }

  //
  // The rectangle we get back is in Desktop coordinates, so we need to
  //   modify it to reflect coordinates relative to this window.
  //

  GetWindowRect (hwnd, &rect);

  rect.right  -= rect.left;
  rect.bottom -= rect.top;
  rect.left = rect.top = 0;

  //
  // Determine a "best fit" scale factor for our polyhedron
  //

  if (!(lScaleFactor = rect.right > rect.bottom ?
                       rect.bottom/12 : rect.right/12))

    lScaleFactor = 1;

  TransformVertices (hwnd, &rect, pSCI, lScaleFactor);

  //
  // Draw the window frame & background
  //
  // Note: The chances are that we are coming through here because we
  //   got a WM_TIMER message & it's time to redraw the cube to simulate
  //   animation. In that case all we want to erase/redraw is that small
  //   rectangle which bounded the polyhedron the last time. The less
  //   drawing that actually gets done the better, since we wnat to
  //   minimize the flicker on the screen. __BeginPaint__ is perfect for
  //   this because it causes all drawing outside of the invalid region
  //   to be "clipped" (no drawing is performed outside of the invalid
  //   region), and it also validates the invalid region.
  //

  if (DO_ERASE(hwnd) || REPAINT_BKGND(pSCI))
  {
    hrgnClip = CreateRectRgnIndirect (&ps.rcPaint);
    SelectClipRgn (pSCI->hdcCompat, hrgnClip);
    DeleteObject (hrgnClip);

    SelectObject (pSCI->hdcCompat, GetStockObject (GRAY_BRUSH));
    Rectangle    (pSCI->hdcCompat, (int)rect.left, (int)rect.top,
                  (int)rect.right, (int)rect.bottom);

    iX = (rect.right  - rect.left) / 4;
    iY = (rect.bottom - rect.top ) / 4;

    MoveToEx (pSCI->hdcCompat, (int)rect.left, (int)rect.top, NULL);
    LineTo   (pSCI->hdcCompat, (int)rect.left + iX, (int)rect.top + iY);
    LineTo   (pSCI->hdcCompat, (int)rect.left + iX, (int)rect.bottom - iY);
    LineTo   (pSCI->hdcCompat, (int)rect.left,      (int)rect.bottom);

    MoveToEx (pSCI->hdcCompat, (int)rect.right, (int)rect.top, NULL);
    LineTo   (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.top + iY);
    LineTo   (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.bottom- iY);
    LineTo   (pSCI->hdcCompat, (int)rect.right,      (int)rect.bottom);

    MoveToEx (pSCI->hdcCompat, (int)rect.left + iX,  (int)rect.top + iY, NULL);
    LineTo   (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.top + iY);

    MoveToEx (pSCI->hdcCompat, (int)rect.left + iX,  (int)rect.bottom - iY, NULL);
    LineTo   (pSCI->hdcCompat, (int)rect.right - iX, (int)rect.bottom - iY);

    SelectClipRgn (pSCI->hdcCompat, NULL);

    pSCI->iOptions &= ~SPINCUBE_REPAINT_BKGND;
  }

  //
  // Draw the polyhedron. We'll walk through the facets list and compute
  //   the normal for each facet- if the normal has z > 0, then the facet
  //   faces us and we'll draw it. Note that this algorithim is ONLY valid
  //   for scenes with a single, convex polyhedron.
  //
  // Note: Use GetDC here because the above call to BeginPaint will
  //   probably not give us a DC with access to as much real estate as
  //   we'd like (we wouldn't be able to draw outside of the invalid
  //   region). We can party on the entire control window with the DC
  //   returned by GetDC.
  //

  for (i = 0, facetIndex = 0; i < NUMFACETS; i++)
  {
    vector1.x = gXformedVertices[gaiFacets[facetIndex + 1]].x -
                gXformedVertices[gaiFacets[facetIndex]].x;
    vector1.y = gXformedVertices[gaiFacets[facetIndex + 1]].y -
                gXformedVertices[gaiFacets[facetIndex]].y;
    vector2.x = gXformedVertices[gaiFacets[facetIndex + 2]].x -
                gXformedVertices[gaiFacets[facetIndex + 1]].x;
    vector2.y = gXformedVertices[gaiFacets[facetIndex + 2]].y -
                gXformedVertices[gaiFacets[facetIndex + 1]].y;

    for (numPoints = 0; gaiFacets[facetIndex] != -1; numPoints++, facetIndex++)
    {
      polygn[numPoints].x = gXformedVertices[gaiFacets[facetIndex]].x;
      polygn[numPoints].y = gXformedVertices[gaiFacets[facetIndex]].y;
    }

    facetIndex++; /* skip over the -1's in the facets list */
    if ((vector1.x*vector2.y - vector1.y*vector2.x) > 0)
    {
      hBrush     = CreateSolidBrush (acrColor[i]);
      hBrushSave = (HBRUSH) SelectObject (pSCI->hdcCompat, hBrush);

      Polygon (pSCI->hdcCompat, &polygn[0], numPoints);

      SelectObject (pSCI->hdcCompat, hBrushSave);
      DeleteObject (hBrush);
    }
  }

  iX  = pSCI->rcCubeBoundary.left < ps.rcPaint.left ?
        pSCI->rcCubeBoundary.left : ps.rcPaint.left;
  iY  = pSCI->rcCubeBoundary.top  < ps.rcPaint.top  ?
        pSCI->rcCubeBoundary.top  : ps.rcPaint.top;

  iCX = (pSCI->rcCubeBoundary.right > ps.rcPaint.right ?
         pSCI->rcCubeBoundary.right : ps.rcPaint.right) - iX;

  iCY = (pSCI->rcCubeBoundary.bottom > ps.rcPaint.bottom ?
         pSCI->rcCubeBoundary.bottom : ps.rcPaint.bottom) - iY;

  EndPaint (hwnd, &ps);

  ps.hdc = GetDC (hwnd);

  BitBlt (ps.hdc, iX, iY, iCX, iCY, pSCI->hdcCompat, iX, iY, SRCCOPY);

  ReleaseDC (hwnd, ps.hdc);

}

/******************************************************************************\
*
*  FUNCTION:     TransformVertices
*
*  INPUTS:       hwnd         - control window handle
*                pWindowRect  - pointer to RECT describing control's dimensions
*                pSCI         - pointer to control's SPINCUBEINFO structure
*                fScaleFactor - scale factor for use in this window
*
\******************************************************************************/

void TransformVertices (HWND hwnd,          RECT  *pWindowRect,
                        PSPINCUBEINFO pSCI, LONG  lScaleFactor)
{
  int    i;
  int    iWindowDepth = pWindowRect->right > pWindowRect->bottom ?
                        pWindowRect->right : pWindowRect->bottom;
  RECT   WindowRect;
  float  fDepthScale;
  int    iNewTranslationInc = (rand() % 10) + 2;
  float  fNewRotationInc    = (float) 0.01 * ((rand() % 30) + 2);

  WindowRect.left = - (WindowRect.right  = pWindowRect->right  >> 1);
  WindowRect.top  = - (WindowRect.bottom = pWindowRect->bottom >> 1);

  //
  // Initiailize the bounding rectangle with max/min vals
  //

  pSCI->rcCubeBoundary.left   =
  pSCI->rcCubeBoundary.top    = 100000; // big positive value
  pSCI->rcCubeBoundary.right  =
  pSCI->rcCubeBoundary.bottom = -100000; // small negative value

  //
  // First scale, then rotate, then translate each vertex.
  //   Keep track of the maximum & minimum values bounding the
  //   vertices in the x,y plane for use later in bounds checking.
  //
  // Note: we don't bother computing z values after the scale,
  //   as they are only really necessary for the rotation. If we
  //   were doing real bounds checking we'd need it, but this code
  //   simply uses the pSCI->iCurrentZTranslation to determine
  //   the z-boundaries.
  //

  for (i = 0; i < NUMVERTICES; i++)
  {
    LONG tempX;

    //
    // Copy the static vertices into a temp array
    //

    gXformedVertices[i] = gNormalizedVertices[i];

    //
    // The scale...
    //

    gXformedVertices[i].x *= lScaleFactor;
    gXformedVertices[i].y *= lScaleFactor;
    gXformedVertices[i].z *= lScaleFactor;

    //
    // The rotation...
    //

    ComputeRotationTransformation (pSCI->fCurrentXRotation,
                                   pSCI->fCurrentYRotation,
                                   pSCI->fCurrentZRotation);

    tempX   =               (LONG) (gM[0][0] * gXformedVertices[i].x +
                                    gM[0][1] * gXformedVertices[i].y +
                                    gM[0][2] * gXformedVertices[i].z);

    gXformedVertices[i].y = (LONG) (gM[1][0] * gXformedVertices[i].x +
                                    gM[1][1] * gXformedVertices[i].y +
                                    gM[1][2] * gXformedVertices[i].z);
    gXformedVertices[i].x = tempX;

    //
    // The translation...
    //

    gXformedVertices[i].x += pSCI->iCurrentXTranslation;
    gXformedVertices[i].y += pSCI->iCurrentYTranslation;

    //
    // Check if we have new max or min vals
    //

    if (pSCI->rcCubeBoundary.left > gXformedVertices[i].x)

      pSCI->rcCubeBoundary.left = gXformedVertices[i].x;

    if (pSCI->rcCubeBoundary.right < gXformedVertices[i].x)

      pSCI->rcCubeBoundary.right = gXformedVertices[i].x;

    if (pSCI->rcCubeBoundary.top > gXformedVertices[i].y)

      pSCI->rcCubeBoundary.top = gXformedVertices[i].y;

    if (pSCI->rcCubeBoundary.bottom < gXformedVertices[i].y)

      pSCI->rcCubeBoundary.bottom = gXformedVertices[i].y;
  }

  //
  // Now for some bounds checking, change translation & rotation increments
  //   if we hit a "wall". Also so the gbHitBoundary flag so we remember
  //   to flash the cube when we draw it.
  //

  if (pSCI->rcCubeBoundary.left < WindowRect.left)
  {
    pSCI->iCurrentXTranslationInc = iNewTranslationInc;
    pSCI->fCurrentZRotationInc    = fNewRotationInc;
  }

  else if (pSCI->rcCubeBoundary.right > WindowRect.right)
  {
    pSCI->iCurrentXTranslationInc = -iNewTranslationInc;
    pSCI->fCurrentZRotationInc    = -fNewRotationInc;
  }

  if (pSCI->rcCubeBoundary.top < WindowRect.top)
   {
    pSCI->iCurrentYTranslationInc = iNewTranslationInc;
    pSCI->fCurrentXRotationInc    = fNewRotationInc;
  }

  else if (pSCI->rcCubeBoundary.bottom > WindowRect.bottom)
  {
    pSCI->iCurrentYTranslationInc = -iNewTranslationInc;
    pSCI->fCurrentXRotationInc    = -fNewRotationInc;
  }

  if (pSCI->iCurrentZTranslation < (int) lScaleFactor<<1)
  {
    pSCI->iCurrentZTranslationInc = iNewTranslationInc;
    pSCI->fCurrentYRotationInc    = fNewRotationInc;
  }

  else if (pSCI->iCurrentZTranslation > (iWindowDepth - (int) lScaleFactor))
  {
    pSCI->iCurrentZTranslationInc = -iNewTranslationInc;
    pSCI->fCurrentYRotationInc    = -fNewRotationInc;
  }

  //
  // Now a kludgy scale based on depth (iCurrentZTranslation) of the center
  //   point of the polyhedron
  //

  fDepthScale =  ((float) iWindowDepth) /
                 ((float) (iWindowDepth + pSCI->iCurrentZTranslation));

  pSCI->rcCubeBoundary.left  = (LONG)(fDepthScale* pSCI->rcCubeBoundary.left  );
  pSCI->rcCubeBoundary.right = (LONG)(fDepthScale* pSCI->rcCubeBoundary.right );
  pSCI->rcCubeBoundary.top   = (LONG)(fDepthScale* pSCI->rcCubeBoundary.top   );
  pSCI->rcCubeBoundary.bottom= (LONG)(fDepthScale* pSCI->rcCubeBoundary.bottom);

  for (i = 0; i < NUMVERTICES; i++)
  {
    gXformedVertices[i].x = (LONG) (fDepthScale * gXformedVertices[i].x);
    gXformedVertices[i].y = (LONG) (fDepthScale * gXformedVertices[i].y);
  }

  //
  // If currently in motion then increment the current rotation & tranlation
  //

  if (IN_MOTION(hwnd))
  {
    pSCI->fCurrentXRotation += pSCI->fCurrentXRotationInc;
    pSCI->fCurrentYRotation += pSCI->fCurrentYRotationInc;
    pSCI->fCurrentZRotation += pSCI->fCurrentZRotationInc;

    pSCI->iCurrentXTranslation += pSCI->iCurrentXTranslationInc;
    pSCI->iCurrentYTranslation += pSCI->iCurrentYTranslationInc;
    pSCI->iCurrentZTranslation += pSCI->iCurrentZTranslationInc;
  }

  //
  // Up to this point all coordinates are relative to a window whose
  //   center is at (0,0). Now we'll translate appropriately...
  //

  pSCI->rcCubeBoundary.left   += pWindowRect->right  >> 1;
  pSCI->rcCubeBoundary.right  += pWindowRect->right  >> 1;
  pSCI->rcCubeBoundary.top    += pWindowRect->bottom >> 1;
  pSCI->rcCubeBoundary.bottom += pWindowRect->bottom >> 1;

  for (i = 0; i < NUMVERTICES; i++)
  {
    gXformedVertices[i].x += pWindowRect->right  >> 1;
    gXformedVertices[i].y += pWindowRect->bottom >> 1;
  }

  //
  // Since FillRect's are inclusive-exclusive (there'll be leftovers
  //   from the last cube we drew otherwise)...
  //

  pSCI->rcCubeBoundary.right++;
  pSCI->rcCubeBoundary.bottom++;

  //
  // Finally, adjust the rcCubeBoundary such that it fits entirely within
  //   the acutal control window. The reason for this is that when calling
  //   InvalidateRect from SpincubeWndProc\case_WM_TIMER we may get
  //   a different PAINSTRUCT.rcPaint (since InvalidateRect clips the passed
  //   in rect to the window bounds) and our abouve test to memcmp() will
  //   fail.
  //

  if (pSCI->rcCubeBoundary.left < 0)

    pSCI->rcCubeBoundary.left = 0;

  if (pSCI->rcCubeBoundary.top < 0)

    pSCI->rcCubeBoundary.top = 0;

  if (pSCI->rcCubeBoundary.right > pWindowRect->right)

    pSCI->rcCubeBoundary.right = pWindowRect->right;

  if (pSCI->rcCubeBoundary.bottom > pWindowRect->bottom)

    pSCI->rcCubeBoundary.bottom = pWindowRect->bottom;
}

/******************************************************************************\
*
*  FUNCTION:    ComputeRotationTransformation
*
*  INPUTS:      fRotationX - Angle to rotate about X axis.
*               fRotationY - Angle to rotate about Y axis.
*               fRotationZ - Angle to rotate about Z axis.
*
*  COMMENTS:    Computes a 3x2 tranformation matrix which rotates about
*               the Z axis, the Y axis, and the X axis, respectively.
*
\******************************************************************************/

void ComputeRotationTransformation (float fRotationX,
                                    float fRotationY,
                                    float fRotationZ)
{
  float sinX, cosX, sinY, cosY, sinZ, cosZ;

  sinX = (float) sin ((double) fRotationX);
  cosX = (float) cos ((double) fRotationX);
  sinY = (float) sin ((double) fRotationY);
  cosY = (float) cos ((double) fRotationY);
  sinZ = (float) sin ((double) fRotationZ);
  cosZ = (float) cos ((double) fRotationZ);

  gM[0][0] =  cosY*cosZ;
  gM[0][1] = -cosY*sinZ;
  gM[0][2] =  sinY;
  gM[1][0] =  sinX*sinY*cosZ + cosX*sinZ;
  gM[1][1] = -sinX*sinY*sinZ + cosX*cosZ;
  gM[1][2] = -sinX*cosY;
}


PAINT.H

/******************************************************************************\
*
*                               MODULE: PAINT.H
*
\******************************************************************************/

/******************************************************************************\
*                             SYMBOLIC CONSTANTS
\******************************************************************************/

#define  NUMVERTICES  8 /* number of polyhedron vertices */
#define  NUMFACETS    6 /* number of polyhedron facets   */

#define  MAX_POINTS_PER_FACET 4

/******************************************************************************\
*                                  TYPEDEFs
\******************************************************************************/

typedef struct
{
  LONG x, y, z;

} POINT3D;

/******************************************************************************\
*                          GLOBAL VARIABLES
\******************************************************************************/

//
//  This particular set of vertices "gNormalizedVertices" and corresponding
//    facets "gaiFacets" describe a normalized cube centered about the
//    origin ([0,0,0] in 3-space). The gaiFacet array is made up of a series
//    of indices into the array of vertices, each describing an individual
//    facet (eg. a polygon), and are separated by -1. Note that the facets
//    are decribed in COUNTERCLOCKWISE (relative to the viewer) order so we
//    can consistently find the normal to any given facet. (The normal
//    is used to determine facet visibilty.)
//

static POINT3D gNormalizedVertices[NUMVERTICES] = {{ 1, 1, 1}, { 1,-1, 1},
                                                   {-1,-1, 1}, {-1, 1, 1},
                                                   { 1, 1,-1}, { 1,-1,-1},
                                                   {-1,-1,-1}, {-1, 1,-1}  };

static int gaiFacets[30] = { 3, 2, 1, 0, -1,
                             4, 5, 6, 7, -1,
                             0, 1, 5, 4, -1,
                             6, 2, 3, 7, -1,
                             7, 3, 0, 4, -1,
                             5, 1, 2, 6, -1  };

POINT3D gXformedVertices[NUMVERTICES];

float   gM[2][3]; /* the transformation matrix */

/******************************************************************************\
*                         FUNCTION PROTOTYPES
\******************************************************************************/

void TransformVertices (HWND, RECT *, PSPINCUBEINFO, LONG);
void ComputeRotationTransformation (float, float, float);


SPINCUBE.C

/******************************************************************************\
*
*  MODULE:      SPINCUBE.C
*
*
*  PURPOSE:     To provide a generic Windows NT dynamic link library
*               sample demonstrating the use of DLL entry points, exported
*               variables, using C runtime in a DLL, etc...
*
*               This module also provides a functional example of how
*               to create a custom control library which may be used by
*               applications (i.e. SPINTEST.EXE) and the Dialog Editor.
*
*
*  FUNCTIONS:   DllMain()      - Registers spincube class when a
*                                      process loads this DLL.
*               CustomControlInfoA() - Called by DLGEDIT to initialize
*                                      a CCINFO structure(s).
*               SpincubeStyle()      - Brings up dialog box which allows
*                                      user to modify control style.
*               SpincubeSizeToText() - Called by DLGEDIT if user requests
*                                      that control be sized to fit text.
*               SpincubeWndProc()    - Window procedure for spincube
*                                      control.
*               SpincubeDlgProc()    - Procedure for control style dialog.
*
*
*  COMMMENTS:   The dialog editor interface has changed since Win 3.0.
*               Recommend browsing the NT CUSTCNTL.H file to get an
*               idea of the new interface.
*
*
*                           Microsoft Developer Support
*                  Copyright 1992 - 1998 Microsoft Corporation
*
\******************************************************************************/

#include <windows.h>
#include <stdlib.h>
#include "spincube.h"

//
// function prototype for C runtime initialization routine
//

BOOL WINAPI _CRT_INIT (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved);

//
// function prototype for Paint() in PAINT.C
//

void Paint (HWND);

//
// function prototype for looking up string resources
//

LPTSTR GetStringRes (int);

//
// Declared below are the module's 2 exported variables.
//
//   giNumSpincubesThisProcess is an instance variable that contains
//   the number of (existing) Spincube controls created by the
//   current process.
//
//   giNumSpincubesAllProcesses is a shared (between processes) variable
//   which contains the total number of (existing) Spincube controls
//   created by all processes in the system.
//
//

int giNumSpincubesThisProcess = 0;

#pragma data_seg(".MYSEG")

  int giNumSpincubesAllProcesses = 0;

#pragma data_seg()

//
// Some global vars for this module
//

HANDLE    ghMod;   // DLL's module handle
LPCCSTYLE gpccs;   // global pointer to a CCSTYLE structure

CCSTYLEFLAGA aSpincubeStyleFlags[] = { { SS_ERASE,    0, "SS_ERASE"    },
                                       { SS_INMOTION, 0, "SS_INMOTION" } };

/******************************************************************************\
*
*  FUNCTION:    DllMain
*
*  INPUTS:      hDLL       - DLL module handle
*               dwReason   - reason being called (e.g. process attaching)
*               lpReserved - reserved
*
*  RETURNS:     TRUE if initialization passed, or
*               FALSE if initialization failed.
*
*  COMMENTS:    On DLL_PROCESS_ATTACH registers the SPINCUBECLASS
*
*               DLL initialization serialization is guaranteed within a
*               process (if multiple threads then DLL entry points are
*               serialized), but is not guaranteed across processes.
*
*               When synchronization objects are created, it is necesaary
*               to check the return code of GetLastError even if the create
*               call succeeded. If the object existed, ERROR_ALREADY_EXISTED
*               will be returned.
*
*               If your DLL uses any C runtime functions then you should
*               always call _CRT_INIT so that the C runtime can initialize
*               itself appropriately. Failure to do this may result in
*               indeterminate behavior. When the DLL entry point is called
*               for DLL_PROCESS_ATTACH & DLL_THREAD_ATTACH circumstances,
*               _CRT_INIT should be called before any other initilization
*               is performed. When the DLL entry point is called for
*               DLL_PROCESS_DETACH & DLL_THREAD_DETACH circumstances,
*               _CRT_INIT should be called after all cleanup has been
*               performed, i.e. right before the function returns.
*
\******************************************************************************/

BOOL WINAPI DllMain (HANDLE hDLL, DWORD dwReason, LPVOID lpReserved)
{
  ghMod = hDLL;
  switch (dwReason)
  {
    case DLL_PROCESS_ATTACH:
    {
      WNDCLASS wc;

      if (!_CRT_INIT (hDLL, dwReason, lpReserved))

        return FALSE;

      wc.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC |
                         CS_GLOBALCLASS ;
      wc.lpfnWndProc   = (WNDPROC) SpincubeWndProc;
      wc.cbClsExtra    = 0;
      wc.cbWndExtra    = SPINCUBE_EXTRA;
      wc.hInstance     = hDLL;
      wc.hIcon         = NULL;
      wc.hCursor       = LoadCursor (NULL, IDC_ARROW);
      wc.hbrBackground = NULL;
      wc.lpszMenuName  = (LPSTR) NULL;
      wc.lpszClassName = (LPSTR) SPINCUBECLASS;

      if (!RegisterClass (&wc))
      {
        MessageBox (NULL,
                    GetStringRes (IDS_REGCLASSFAIL),
                    (LPCTSTR) "SPINCUBE.DLL",
                    MB_OK | MB_ICONEXCLAMATION);

        return FALSE;
      }

      break;
    }

    case DLL_PROCESS_DETACH:
    {

      if (!_CRT_INIT (hDLL, dwReason, lpReserved))

        return FALSE;

      if (!UnregisterClass ((LPSTR) SPINCUBECLASS, hDLL ))
      {
        MessageBox (NULL,
                    GetStringRes (IDS_UNREGFAIL),
                    (LPCTSTR) "SPINCUBE.DLL",
                    MB_OK | MB_ICONEXCLAMATION);

        return FALSE;
      }

      break;
    }

    default:

      if (!_CRT_INIT (hDLL, dwReason, lpReserved))

        return FALSE;

      break;
  }
  return TRUE;
}

/******************************************************************************\
*
*  FUNCTION:    CustomControlInfoA
*
*  INPUTS:      acci - pointer to an array od CCINFOA structures
*
*  RETURNS:     Number of controls supported by this DLL
*
*  COMMENTS:    See CUSTCNTL.H for more info
*
\******************************************************************************/

UINT CALLBACK CustomControlInfoA (LPCCINFOA acci)
{
  //
  // Dlgedit is querying the number of controls this DLL supports, so return 1.
  //   Then we'll get called again with a valid "acci"
  //

  if (!acci)

    return 1;

  //
  // Fill in the constant calues.
  //

  acci[0].flOptions         = 0;
  acci[0].cxDefault         = 40;      // default width  (dialog units)
  acci[0].cyDefault         = 40;      // default height (dialog units)
  acci[0].flStyleDefault    = WS_CHILD |
                              WS_VISIBLE |
                              SS_INMOTION;
  acci[0].flExtStyleDefault = 0;
  acci[0].flCtrlTypeMask    = 0;
  acci[0].cStyleFlags       = NUM_SPINCUBE_STYLES;
  acci[0].aStyleFlags       = aSpincubeStyleFlags;
  acci[0].lpfnStyle         = SpincubeStyle;
  acci[0].lpfnSizeToText    = SpincubeSizeToText;
  acci[0].dwReserved1       = 0;
  acci[0].dwReserved2       = 0;

  //
  // Copy the strings
  //
  // NOTE: MAKE SURE THE STRINGS COPIED DO NOT EXCEED THE LENGTH OF
  //       THE BUFFERS IN THE CCINFO STRUCTURE!
  //

  lstrcpy (acci[0].szClass, SPINCUBECLASS);
  lstrcpy (acci[0].szDesc,  SPINCUBEDESCRIPTION);
  lstrcpy (acci[0].szTextDefault, SPINCUBEDEFAULTTEXT);

  //
  // Return the number of controls that the DLL supports
  //

  return 1;
}

/******************************************************************************\
*
*  FUNCTION:    SpincubeStyle
*
*  INPUTS:      hWndParent - handle of parent window (dialog editor)
*               pccs       - pointer to a CCSTYLE structure
*
*  RETURNS:     TRUE  if success,
*               FALSE if error occured
*
*  LOCAL VARS:  rc - return code from DialogBox
*
\******************************************************************************/

BOOL CALLBACK SpincubeStyle (HWND hWndParent, LPCCSTYLE pccs)
{
  int rc;

  gpccs = pccs;

  if ((rc = DialogBox (ghMod, "SpincubeStyle", hWndParent,
                       (DLGPROC)SpincubeDlgProc)) == -1)
  {
    MessageBox (hWndParent,
                GetStringRes (IDS_DLGBOXFAIL),
                (LPCTSTR) "Spincube.dll",
                MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
    rc = 0;
  }

  return (BOOL) rc;
}

/******************************************************************************\
*
*  FUNCTION:    SpincubeSizeToText
*
*  INPUTS:      flStyle    - control style
*               flExtStyle - control extended style
*               hFont      - handle of font used to draw text
*               pszText    - control text
*
*  RETURNS:     Width (in pixels) control must be to accomodate text, or
*               -1 if an error occurs.
*
*  COMMENTS:    Just no-op here (since we never actually display text in
*               the control it doesn't need to be resized).
*
\******************************************************************************/

INT CALLBACK SpincubeSizeToText (DWORD flStyle, DWORD flExtStyle,
                                 HFONT hFont,   LPSTR pszText)
{
  return -1;
}

/******************************************************************************\
*
*  FUNCTION:    SpincubeWndProc (standard window procedure INPUTS/RETURNS)
*
*  COMMENTS:    This is the window procedure for our custom control. At
*               creation we alloc a SPINCUBEINFO struct, initialize it,
*               and associate it with this particular control. We also
*               start a timer which will invalidate the window every so
*               often; this causes a repaint, and the cube gets drawn in
*               a new position. Left button clicks will turn toggle the
*               erase option, causing a "trail" of cubes to be left when
*               off. Right button clicks will toggle the motion state of
*               the control (and turn the timer on/off).
*
\******************************************************************************/

LRESULT CALLBACK SpincubeWndProc (HWND hwnd, UINT msg, WPARAM wParam,
                                  LPARAM lParam)
{
  switch (msg)
  {
    case WM_CREATE:
    {
      //
      // Alloc & init a SPINCUBEINFO struct for this particular control
      //

      HDC            hdc;
      LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam;
      PSPINCUBEINFO  pSCI = (PSPINCUBEINFO) LocalAlloc (LPTR,
                                                        sizeof(SPINCUBEINFO));
      if (!pSCI)
      {
        MessageBox (NULL,
                    GetStringRes (IDS_ALLOCFAIL),
                    (LPCTSTR) "SPINCUBE.DLL",
                    MB_OK | MB_ICONEXCLAMATION);
        return -1;
      }

      //
      // Alloc the compatible DC for this control.
      //

      hdc = GetDC (hwnd);

      if ((pSCI->hdcCompat = CreateCompatibleDC (hdc)) == NULL)
      {
        MessageBox (NULL,
                    GetStringRes (IDS_CREATEDCFAIL),
                    (LPCTSTR) "SPINCUBE.DLL",
                    MB_OK | MB_ICONEXCLAMATION);
        return -1;
      }

      ReleaseDC (hwnd, hdc);

      //
      // Initialize this instance structure
      //

      pSCI->fCurrentXRotation =
      pSCI->fCurrentYRotation =
      pSCI->fCurrentZRotation = (float) 0.0;

      pSCI->fCurrentXRotationInc =
      pSCI->fCurrentYRotationInc =
      pSCI->fCurrentZRotationInc = (float) 0.2617; // a random # (15 degrees)

      pSCI->iCurrentXTranslation =
      pSCI->iCurrentYTranslation =
      pSCI->iCurrentZTranslation = 0;

      //
      // All these calculations so the cubes start out with random movements.
      //

      if ((pSCI->iCurrentXTranslationInc = (rand() % 10) + 2) > 7)

        pSCI->iCurrentXTranslationInc = -pSCI->iCurrentXTranslationInc;

      if ((pSCI->iCurrentYTranslationInc = (rand() % 10) + 2) <= 7)

        pSCI->iCurrentYTranslationInc = -pSCI->iCurrentYTranslationInc;

      if ((pSCI->iCurrentZTranslationInc = (rand() % 10) + 2) > 7)

        pSCI->iCurrentZTranslationInc = -pSCI->iCurrentZTranslationInc;

      pSCI->rcCubeBoundary.left   =
      pSCI->rcCubeBoundary.top    = 0;
      pSCI->rcCubeBoundary.right  = lpcs->cx;
      pSCI->rcCubeBoundary.bottom = lpcs->cy;

      pSCI->iOptions  = SPINCUBE_REPAINT_BKGND;
      pSCI->hbmCompat = NULL;

      SetWindowLong (hwnd, GWL_SPINCUBEDATA, (LONG) pSCI);

      SetTimer (hwnd, SPIN_EVENT, SPIN_INTERVAL, NULL);

      //
      // Increment the count vars
      //

      giNumSpincubesThisProcess++;
      giNumSpincubesAllProcesses++;

      break;
    }

    case WM_PAINT:

      Paint (hwnd);
      break;

    case WM_TIMER:

      switch (wParam)
      {
        case SPIN_EVENT:
        {
          PSPINCUBEINFO pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd,
                                                              GWL_SPINCUBEDATA);

          InvalidateRect (hwnd, &pSCI->rcCubeBoundary, FALSE);

          break;
        }
      }

      break;

    case WM_LBUTTONDBLCLK:
    {
      //
      // Toggle the erase state of the control
      //

      if (DO_ERASE(hwnd))

        SetWindowLong (hwnd, GWL_STYLE,
                       GetWindowLong (hwnd, GWL_STYLE) & ~SS_ERASE);

      else
      {
        //
        // Repaint the entire control to get rid of the (cube trails) mess
        //

        PSPINCUBEINFO pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd,
                                                            GWL_SPINCUBEDATA);

        SetWindowLong (hwnd, GWL_STYLE,
                       GetWindowLong (hwnd, GWL_STYLE) | SS_ERASE);
        pSCI->iOptions |= SPINCUBE_REPAINT_BKGND;
        InvalidateRect (hwnd, NULL, FALSE);
        SendMessage (hwnd, WM_PAINT, 0, 0);
      }
      break;
    }

    case WM_RBUTTONDBLCLK:
    {
      //
      // Toggle the motion state of the control
      //

      if (IN_MOTION(hwnd))
      {
        KillTimer (hwnd, SPIN_EVENT);
        SetWindowLong (hwnd, GWL_STYLE,
                       GetWindowLong (hwnd, GWL_STYLE) & ~SS_INMOTION);
      }
      else
      {
        SetTimer (hwnd, SPIN_EVENT, SPIN_INTERVAL, NULL);
        SetWindowLong (hwnd, GWL_STYLE,
                       GetWindowLong (hwnd, GWL_STYLE) | SS_INMOTION);
      }

      break;
    }

    case WM_SIZE:

      if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED)
      {
        PSPINCUBEINFO pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd,
                                                            GWL_SPINCUBEDATA);
        //
        // Get a new bitmap which is the new size of our window
        //

        HDC hdc = GetDC (hwnd);
        HBITMAP hbmTemp = CreateCompatibleBitmap (hdc,
                                                  (int) LOWORD (lParam),
                                                  (int) HIWORD (lParam));
        if (!hbmTemp)
        {
          //
          // Scream, yell, & committ an untimely demise...
          //

          MessageBox (NULL,
                      GetStringRes (IDS_CREATEBITMAPFAIL),
                      (LPCTSTR) "SPINCUBE.DLL",
                      MB_OK | MB_ICONEXCLAMATION);
          DestroyWindow (hwnd);
        }

        pSCI->hbmSave = SelectObject (pSCI->hdcCompat, hbmTemp);
        if (pSCI->hbmCompat)
        DeleteObject (pSCI->hbmCompat);
        ReleaseDC    (hwnd, hdc);
        pSCI->hbmCompat = hbmTemp;

        //
        // Reset the translation so the cube doesn't go spinning off into
        //   space somewhere- we'd never see it again!
        //

        pSCI->iCurrentXTranslation =
        pSCI->iCurrentYTranslation =
        pSCI->iCurrentZTranslation = 0;

        //
        // All these calculations so the cube starts out with random movements,
        //

        if ((pSCI->iCurrentXTranslationInc = (rand() % 10) + 2) > 7)

          pSCI->iCurrentXTranslationInc = -pSCI->iCurrentXTranslationInc;

        if ((pSCI->iCurrentYTranslationInc = (rand() % 10) + 2) <= 7)

          pSCI->iCurrentYTranslationInc = -pSCI->iCurrentYTranslationInc;

        if ((pSCI->iCurrentZTranslationInc = (rand() % 10) + 2) > 7)

          pSCI->iCurrentZTranslationInc = -pSCI->iCurrentZTranslationInc;

        pSCI->rcCubeBoundary.left   =
        pSCI->rcCubeBoundary.top    = 0;
        pSCI->rcCubeBoundary.right  = (int) LOWORD (lParam);
        pSCI->rcCubeBoundary.bottom = (int) HIWORD (lParam);

        pSCI->iOptions |= SPINCUBE_REPAINT_BKGND;

        InvalidateRect (hwnd, NULL, FALSE);
      }

      break;

    case WM_DESTROY:
    {
      PSPINCUBEINFO pSCI = (PSPINCUBEINFO) GetWindowLong (hwnd,
                                                          GWL_SPINCUBEDATA);
      //
      // Clean up all the resources used for this control
      //

      if (IN_MOTION(hwnd))

        KillTimer (hwnd, SPIN_EVENT);

      SelectObject (pSCI->hdcCompat, pSCI->hbmSave);
      DeleteObject (pSCI->hbmCompat);
      DeleteDC     (pSCI->hdcCompat);

      LocalFree (LocalHandle ((LPVOID) pSCI));

      //
      // Decrement the global count vars
      //

      giNumSpincubesThisProcess--;
      giNumSpincubesAllProcesses--;

      break;
    }

    default:

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

  return ((LONG) TRUE);
}

/******************************************************************************\
*
*  FUNCTION:    SpincubeDlgProc (standard dialog procedure INPUTS/RETURNS)
*
*  COMMENTS:    This dialog comes up in response to a user requesting to
*               modify the control style. This sample allows for changing
*               the control's text, and this is done by modifying the
*               CCSTYLE structure pointed at by "gpccs" (a pointer
*               that was passed to us by dlgedit).
*
\******************************************************************************/

LRESULT CALLBACK SpincubeDlgProc (HWND hDlg, UINT msg, WPARAM wParam,
                                  LPARAM lParam)
{
  switch (msg)
  {
    case WM_INITDIALOG :
    {
      if (gpccs->flStyle & SS_ERASE)

        CheckDlgButton (hDlg, DID_ERASE, 1);

      if (gpccs->flStyle & SS_INMOTION)

        CheckDlgButton (hDlg, DID_INMOTION, 1);

      break;
    }

    case WM_COMMAND:

      switch (LOWORD(wParam))
      {
        case DID_ERASE:

          if (IsDlgButtonChecked (hDlg, DID_ERASE))

            gpccs->flStyle |= SS_ERASE;

          else

            gpccs->flStyle &= ~SS_ERASE;

          break;

        case DID_INMOTION:

          if (IsDlgButtonChecked (hDlg, DID_INMOTION))

            gpccs->flStyle |= SS_INMOTION;

          else

            gpccs->flStyle &= ~SS_INMOTION;

          break;

        case DID_OK:

          EndDialog  (hDlg, 1);
          break;
      }
      break;
  }
  return FALSE;
}

/******************************************************************************\
*
*  FUNCTION:    GetStringRes (int id INPUT ONLY)
*
*  COMMENTS:    Load the resource string with the ID given, and return a
*               pointer to it.  Notice that the buffer is common memory so
*               the string must be used before this call is made a second time.
*
\******************************************************************************/

LPTSTR   GetStringRes (int id)
{
  static TCHAR buffer[MAX_PATH];

  buffer[0]=0;
  LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  return buffer;
}


SPINCUBE.H

/******************************************************************************\
*
*                                  SPINCUBE.H
*
\******************************************************************************/

#include <custcntl.h>

/******************************************************************************\
*                               SYMBOLIC CONSTANTS
\******************************************************************************/

#define SPINCUBECLASS           "Spincube"
#define SPINCUBEDESCRIPTION     "An animated control"
#define SPINCUBEDEFAULTTEXT     ":-)"

#define CCHSTYLE                20  // size of style string, i.e. "SS_ERASE"

#define NUM_SPINCUBE_STYLES     2

#define SS_ERASE                0x0001  // spincube window styles
#define SS_INMOTION             0x0002

#define SPINCUBE_EXTRA          4   // number of extra bytes for spincube class

#define SPIN_EVENT              1   // timer event id to repaint control
#ifdef _ALPHA_
#define SPIN_INTERVAL           0   // milliseconds between repaints. what
                                    //   would be really cool is a way to
                                    //   dynamically adjust the interval-
                                    //   on real fast machines we might
                                    //   almost be able to get decent-looking
                                    //   animation! :)

#else
#define SPIN_INTERVAL           75  // milliseconds between repaints. what
                                    //   would be really cool is a way to
                                    //   dynamically adjust the interval-
                                    //   on real fast machines we might
                                    //   almost be able to get decent-looking
                                    //   animation! :)

#endif
#define DID_ERASE               101 // dialog control id's
#define DID_INMOTION            102
#define DID_OK                  103

#define GWL_SPINCUBEDATA        0   // offset of control's instance data

#define SPINCUBE_REPAINT_BKGND  0x00000001

#define DO_ERASE(hwnd)          GetWindowLong(hwnd,GWL_STYLE) & SS_ERASE \
                                           ? TRUE : FALSE

#define IN_MOTION(hwnd)         GetWindowLong(hwnd,GWL_STYLE) & SS_INMOTION \
                                           ? TRUE : FALSE

#define REPAINT_BKGND(pSCI)     pSCI->iOptions&SPINCUBE_REPAINT_BKGND \
                                           ? TRUE : FALSE

#define IDS_REGCLASSFAIL      16
#define IDS_UNREGFAIL         17
#define IDS_DLGBOXFAIL        18
#define IDS_ALLOCFAIL         19
#define IDS_CREATEDCFAIL      20
#define IDS_CREATEBITMAPFAIL  21

/******************************************************************************\
*                                    TYPEDEFs
\******************************************************************************/

typedef struct
{
  HDC      hdcCompat;               // the DC that will contain our off-screen
                                    //   image
  HBITMAP  hbmSave;                 // Save previous selected bitmap
  HBITMAP  hbmCompat;               // The bitmap that will contain the actual
                                    //   image, i.e. we will always do our
                                    //   drawing on this bmp & then blt the
                                    //   result to the screen.

  float    fCurrentXRotation;       // Angle (in radians) to rotate cube about
  float    fCurrentYRotation;       //   x, y, z axis
  float    fCurrentZRotation;

  float    fCurrentXRotationInc;    // Amount to inc rotation angle each
  float    fCurrentYRotationInc;    //   time we repaint (and are in motion)
  float    fCurrentZRotationInc;

  int      iCurrentXTranslation;    // Distance (in pels) to translate cube
  int      iCurrentYTranslation;
  int      iCurrentZTranslation;

  int      iCurrentXTranslationInc; // Amount to inc translation distance each
  int      iCurrentYTranslationInc; //   time we repaint (and are in motion)
  int      iCurrentZTranslationInc;

  RECT     rcCubeBoundary;          // Bounding rectangle (in 2D) of the last
                                    //   cube drawn.  We invalidate only this
                                    //   region when we're doing animation
                                    //   and get the WM_TIMER- it's alot more
                                    //   efficient that invalidating the whole
                                    //   control (there's less screen flashing.

  int      iOptions;                // Contains the current options for this
                                    //   ctrl, i.e. erase background.

} SPINCUBEINFO, *PSPINCUBEINFO;

/******************************************************************************\
*                                FUNCTION PROTOTYPES
\******************************************************************************/

INT     CALLBACK SpincubeSizeToText (DWORD, DWORD, HFONT,  LPSTR);
BOOL    CALLBACK SpincubeStyle      (HWND,  LPCCSTYLE);
LRESULT CALLBACK SpincubeWndProc    (HWND,  UINT,  WPARAM, LPARAM);
LRESULT CALLBACK SpincubeDlgProc    (HWND,  UINT,  WPARAM, LPARAM);


SPINTEST.C

/******************************************************************************\
*
*  PROGRAM:     SPINTEST.C
*
*  PURPOSE:     Demonstrates the use of the SPINCUBE custom control.
*
*  FUNCTIONS:   WinMain        - standard stuff; also loads the
*                                  SPINCUBE.DLL and creates a couple
*                                  of spincube controls.
*               MainWndProc    - generic window procedure.
*               SpintestDlgProc- generic dialog procedure.
*               AboutDlgProc   - processes about dialog messages
*
*                           Microsoft Developer Support
*                  Copyright 1992 - 1998 Microsoft Corporation
*
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include "spintest.h"

//
// The exported variables from SPINCUBE.C.
//
//   Although pointers to these vars are actually exported,
//    the compiler will take care of that for us.
//

extern int __declspec(dllimport) giNumSpincubesThisProcess;
extern int __declspec(dllimport) giNumSpincubesAllProcesses;

//
// function prototype for looking up string resources
//

LPTSTR GetStringRes (int);

/******************************************************************************\
*
*  FUNCTION:    WinMain (standard WinMain INPUTS/RETURNS)
*
\******************************************************************************/

int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance, LPSTR lpCmdLine,
                   int     nCmdShow)
{
  WNDCLASS wc;
  HWND   hwnd;
  MSG    msg;
  RECT   rect;
  WORD   i;

  wc.style         = 0;
  wc.lpfnWndProc   = (WNDPROC) MainWndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = LoadIcon (hInstance, "spintesticon");
  wc.hCursor       = LoadCursor (NULL, IDC_ARROW);
  wc.hbrBackground = GetStockObject (WHITE_BRUSH);
  wc.lpszMenuName  = (LPSTR) "Menu";
  wc.lpszClassName = (LPSTR) "Main";

  if (!RegisterClass (&wc))
  {
    MessageBox (NULL,
                GetStringRes (IDS_REGCLASSFAIL),
                "SPINTEST", MB_OK | MB_ICONEXCLAMATION);
    return(FALSE);
  }

  ghInst = hInstance;
  if (!(hwnd = CreateWindow ("Main",
                             GetStringRes (IDS_WINDOWTITLE),
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             NULL, NULL, ghInst, NULL)))
    return 0;

  //
  // Create a couple of SpinCube custom controls, we'll size them later in
  //   the WM_SIZE message handler
  //

  for (i = 0; i < 4; i++)

    gahwndSpin[i] = CreateWindow ("Spincube", "",
                                  WS_VISIBLE | WS_CHILD |
                                  SS_INMOTION | SS_ERASE,
                                  0, 0, 0, 0, hwnd, NULL, NULL, NULL);

  //
  // Delete the SS_ERASE to the 1st & 4th controls so we get the
  //   trailing cubes effect.
  //

  SetWindowLong (gahwndSpin[0], GWL_STYLE,
                 GetWindowLong (gahwndSpin[0], GWL_STYLE) & ~ SS_ERASE);
  SetWindowLong (gahwndSpin[3], GWL_STYLE,
                 GetWindowLong (gahwndSpin[3], GWL_STYLE) & ~ SS_ERASE);

  //
  // Send ourself a WM_SIZE so the controls will get sized appropriately
  //

  GetClientRect (hwnd, &rect);
  SendMessage (hwnd, WM_SIZE, 0,
               MAKELONG((WORD)rect.right,(WORD)rect.bottom));

  ShowWindow (hwnd, nCmdShow);

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

  return (msg.wParam);
}

/******************************************************************************\
*
*  FUNCTION:    MainWndProc (standard window procedure INPUTS/RETURNS)
*
\******************************************************************************/

LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
    case WM_COMMAND:

      switch (LOWORD(wParam))
      {
        case IDM_DLGEDITDIALOG:

          DialogBox (ghInst, (LPCTSTR) "SpintestDlg", hwnd, (DLGPROC) DlgProc);
          break;

        case IDM_SPINTESTSTATS:

          DialogBox (ghInst, (LPCTSTR) "Stats", hwnd, (DLGPROC) DlgProc);
          break;

        case IDM_ABOUT:

          DialogBox (ghInst, (LPCTSTR)"About", hwnd, (DLGPROC) DlgProc);
          break;

      }
      break;

    case WM_SIZE:
    {
      //
      // Resize the controls such that each cover half the client area
      //   (plus a little border).
      //

      int width  = (int) LOWORD(lParam);
      int height = (int) HIWORD(lParam);

      SetWindowPos (gahwndSpin[0], NULL,
                    BORDER, BORDER,
                    width/2 - BORDER, height/2 - BORDER,
                    SWP_SHOWWINDOW);
      SetWindowPos (gahwndSpin[1], NULL,
                    width/2 + BORDER, BORDER,
                    width/2 - 2*BORDER, height/2 - BORDER,
                    SWP_SHOWWINDOW);
      SetWindowPos (gahwndSpin[2], NULL,
                    BORDER, height/2 + BORDER,
                    width/2 - BORDER, height/2 - 2*BORDER,
                    SWP_SHOWWINDOW);
      SetWindowPos (gahwndSpin[3], NULL,
                    width/2 + BORDER, height/2 + BORDER,
                    width/2 - 2*BORDER, height/2 - 2*BORDER,
                    SWP_SHOWWINDOW);
      break;
    }

    case WM_DESTROY:

      PostQuitMessage (0);
      break;

    default:

      return (DefWindowProc (hwnd, msg, wParam, lParam));
  }
  return 0;
}

/******************************************************************************\
*
*  FUNCTION:    DlgProc (standard dialog procedure INPUTS/RETURNS)
*
*  COMMENTS:    Our common dlg proc (why have 3 that do the same thing???)
*
\******************************************************************************/

LRESULT CALLBACK DlgProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    case WM_INITDIALOG:

      //
      // If this dlg the "Stats" dlg fill in the appropriate fields.
      //   If not these calls will just fail.
      //
      // If the references to the giNum* vars are commented out &
      //   the program gets rebuilt don't be surprised if no spincubes
      //   appear- since no references to spincube.lib the linker will
      //   infer that it is not needed, & will not cause it to get
      //   loaded. You'll need to make a call to LoadLibrary ("SPINCUBE.DLL")
      //   prior to calling CreateWindow ("SPINCUBE"...).
      //

      SetDlgItemInt (hwnd, 500, giNumSpincubesThisProcess, TRUE);
      SetDlgItemInt (hwnd, 501, giNumSpincubesAllProcesses, TRUE);
      return (TRUE);

    case WM_COMMAND:

      if (LOWORD(wParam) == IDOK)

        EndDialog (hwnd, TRUE);

      return (TRUE);
  }
  return (FALSE);
}

/******************************************************************************\
*
*  FUNCTION:    GetStringRes (int id INPUT ONLY)
*
*  COMMENTS:    Load the resource string with the ID given, and return a
*               pointer to it.  Notice that the buffer is common memory so
*               the string must be used before this call is made a second time.
*
\******************************************************************************/

LPTSTR   GetStringRes (int id)
{
  static TCHAR buffer[MAX_PATH];

  buffer[0]=0;
  LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  return buffer;
}


SPINTEST.H

/******************************************************************************\
*
*                                 SPINTEST.H
*
\******************************************************************************/

/******************************************************************************\
*                             SYMBOLIC CONSTANTS
\******************************************************************************/

#define IDM_DLGEDITDIALOG  101  /* menuitem id's */
#define IDM_SPINTESTSTATS  102
#define IDM_ABOUT          103

#define BORDER             4

#define IDS_REGCLASSFAIL    16
#define IDS_WINDOWTITLE     17

//
// Spincube window styles as defined in spincube.h.
//

#define SS_ERASE                0x0001
#define SS_INMOTION             0x0002

/******************************************************************************\
*                               GLOBAL VARIABLES
\******************************************************************************/

HANDLE ghInst;
HWND   gahwndSpin[4];

/******************************************************************************\
*                              FUNCTION PROTOTYPES
\******************************************************************************/

LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DlgProc     (HWND, UINT, WPARAM, LPARAM);


CURSOR.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

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

    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 <string.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           */

MPOINT 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 APIENTRY WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
    )
{
    MSG msg;                                 /* message                      */

    UNREFERENCED_PARAMETER( lpCmdLine );

    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, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (msg.wParam);
}

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

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

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

BOOL InitApplication(HANDLE hInstance)
{
    WNDCLASS  wc;

    wc.style = 0;
    wc.lpfnWndProc = (WNDPROC) 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(
    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 APIENTRY MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam)
{
    POINT pt;
    HDC hDC;

    switch (message) {
        case WM_COMMAND:
            // LOWORD added for portability
            if (LOWORD(wParam) == IDM_ABOUT) {

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

                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 = (SHORT)LOWORD(lParam);
                    NextY = (SHORT)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 box */
                        MoveToEx(hDC, OrgX, OrgY, NULL);
                        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;
                        MoveToEx(hDC, OrgX, OrgY, NULL); /* 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;

            MPOINT2POINT(ptCursor, pt);
            GetCursorPos(&pt);

            /* Convert screen coordinates to client coordinates */

            ScreenToClient(hWnd, &pt);
            POINT2MPOINT(pt, 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 */

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

            /* Convert the coordinates to screen coordinates */

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

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

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

        case WM_PAINT:
            {
                PAINTSTRUCT     ps;

                hDC = BeginPaint (hWnd, &ps);
                if (OrgX != PrevX || OrgY != PrevY) {
                    MoveToEx(hDC, OrgX, OrgY, NULL);
                    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 (0);
}

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

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

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

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

BOOL APIENTRY About(HWND hDlg, UINT message, UINT wParam, LONG lParam)
{
    switch (message) {
        case WM_INITDIALOG:
            return (TRUE);

        case WM_COMMAND:
            // LOWORD added for portability
            if (LOWORD(wParam) == IDOK
                || LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
                return (TRUE);
            }
            break;
    }
    return (FALSE);
        UNREFERENCED_PARAMETER(lParam);
}

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

    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().

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

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

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

INT 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);
}


CURSOR.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define IDM_ABOUT 100

typedef POINTS MPOINT;
#define MPOINT2POINT(mpt,pt)  ((pt).x = (mpt).x, (pt).y = (mpt).y)
#define POINT2MPOINT(pt, mpt) ((mpt).x = (SHORT)(pt).x, (mpt).y = (SHORT)(pt).y)

BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, INT);
LONG APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
BOOL APIENTRY About(HWND, UINT, UINT, LONG);
INT sieve(VOID);


CANDUI.C

#include <assert.h>
#include <windows.h>
#include <imm.h>
#include "fullime.h"

//********************************************************************
//
// long WINAPI CandWndProc()
//
// Window procedure of candidate window.
//
//********************************************************************

long WINAPI CandWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{

    switch( msg )
    {

        case WM_PAINT:

            CandUIPaint( hwnd );
            break;

        default:

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

    return 0;
}

//********************************************************************
//
// void DislpayCandStrings()
//
// Displays candidate strings.
//
//********************************************************************

void DisplayCandStrings( HWND hwnd, LPCANDIDATELIST lpCandList )
{

    HDC         hdc;            // Storage for device context handle.
    LPSTR       lpStr;          // Storage for LP to a string.
    DWORD       dwNumPerPage;   // Storage for num per page
    DWORD       dwStartIndex;   // Storage for candidate index
    DWORD       dwEndIndex;     // Storage for candidate index
    RECT        rect;           // Storage for client rect.
    int         y = 0;          //
    DWORD       dwBackColor;    // Storage for background color value
    DWORD       dwTextColor;

    //
    // Determine how many candidate strings per page.
    //

    dwNumPerPage = ( !lpCandList->dwPageSize ) ?
                     DEFAULT_CAND_NUM_PER_PAGE :
                     lpCandList->dwPageSize;

    //
    // Determine the dwStartIndex from which the candidate string will
    // start displaying.
    //

    dwStartIndex = lpCandList->dwPageStart;

    //
    // Determine the dwEndIndex which indicate the last candidate string
    // will be displayed.
    //

    dwEndIndex = dwStartIndex + dwNumPerPage;

    dwEndIndex = ( dwEndIndex >= lpCandList->dwCount ) ?
                 lpCandList->dwCount : dwEndIndex;

    //
    // Dissplay candidate strings.
    // First paint background color.
    //

    hdc = GetDC( hwnd );

    dwBackColor = SetBkColor( hdc, RGB( 0xbf, 0xbf, 0xbf ) );

    GetClientRect( hwnd, &rect );

    ExtTextOut( hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, 0 );

    //
    // Set background color of selected candidate string to be blue.
    //

    rect.top = rect.top + ( lpCandList->dwSelection - dwStartIndex ) *
               cyMetrics;

    rect.bottom = rect.top + cyMetrics;

    SetBkColor( hdc,RGB( 0x00, 0x00, 0xff ) );

    ExtTextOut( hdc, 0, rect.top, ETO_OPAQUE, &rect, NULL, 0, 0 );

    //
    // Display candidate strings.
    //

    SetBkMode( hdc, TRANSPARENT );

    dwTextColor = GetTextColor( hdc );

    for ( ; dwStartIndex < dwEndIndex; dwStartIndex++, y++ )
    {

        lpStr = (LPSTR) lpCandList +
                lpCandList->dwOffset[ dwStartIndex ];

        if ( dwStartIndex == lpCandList->dwSelection )
            SetTextColor( hdc, RGB( 255, 255, 255 ) );
        else
            SetTextColor( hdc, dwTextColor );

        TextOut( hdc, 3, y * cyMetrics, lpStr, lstrlen( lpStr ) );
    }

    SetTextColor( hdc, dwTextColor );

    SetBkColor( hdc, dwBackColor );

    ReleaseDC( hwnd, hdc );

}

//**********************************************************************
//
// void CandUIPaint()
//
// Handler routine of WM_PAINT message.
//
//**********************************************************************

void CandUIPaint( HWND hwnd )
{
    int         index;
    LPCANDIDATELIST lpCandList;
    HDC         hdc;
    PAINTSTRUCT ps;

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

    //
    // Determine which candidate window needs to repaint.
    //

    for ( index = 0; index < MAX_LISTCAND; index ++ )
        if ( gImeUIData.hListCand[ index ] == hwnd )
            break;

    //
    // If index == MAX_LISTCAND, then something wrong, do nothing.
    //

    if ( index == MAX_LISTCAND )
        goto exit;

    if ( !gImeUIData.hListCandMem[ index ] ||
         (lpCandList = (LPCANDIDATELIST)
              GlobalLock( gImeUIData.hListCandMem[ index ] ) ) == NULL )
        goto exit;

    DisplayCandStrings( hwnd, lpCandList );

    GlobalUnlock( gImeUIData.hListCandMem[ index ] );

exit:

    EndPaint( hwnd, (LPPAINTSTRUCT)&ps );

}


FULLIME.H

#define MAXROW          12
#define MAXCOL          60
#define FIRSTROW         0
#define FIRSTCOL         0
#define LASTROW         (MAXROW-1)
#define LASTCOL         (MAXCOL-1)
#define TABSTOP          4
#define MAX_LISTCAND     32
#define MAX_CHARS_PER_LINE   10
#define MAX_COMP_STRING_LEN  10
#define DEFAULT_CAND_NUM_PER_PAGE 5
#define X_INDENT         10
#define Y_INDENT         10

#define ID_ICON         1
#define ID_MENU         1
#define IDS_CLASS       1
#define IDS_TITLE       2
#define IDS_CANDUI      3
#define IDS_COMPTITLE   4
#define IDS_CANDTITLE   5
#define IDS_CANDCLASS   6

#define IDC_LIST1                0
#define IDC_LIST32              31

#define IDC_CLEAR               41
#define IDC_ANSIFILL            42
#define IDC_DBCSFILL            43
#define IDC_OPENIME             44
#define IDC_CLOSEIME            45

#define IME_IN_COMPOSITION      1
#define IME_IN_CHOSECAND        2

#define CheckProperty{\
    if ( ( gImeUIData.fdwProperty & IME_PROP_SPECIAL_UI ) ||\
 !( gImeUIData.fdwProperty & IME_PROP_AT_CARET ) )\
goto call_defwinproc;\
}

typedef struct _IMEUIDATA {
    int        ImeState;   // Current Ime state.
    UINT       uCompLen;   // To save previous composition string length.
    DWORD      fdwProperty;
    HWND       hListCand[ MAX_LISTCAND ];
    HGLOBAL    hListCandMem[ MAX_LISTCAND ];
} IMEUIDATA;

//
// Prototype declaration
//

long WINAPI SteWndProc( HWND, UINT, UINT, LONG );
long WINAPI CandWndProc( HWND, UINT, UINT, LONG );

void ResetCaret( HWND );

void ImeUIStartComposition( HWND );
void ImeUIComposition( HWND, WPARAM, LPARAM );
void GetCompositionStr( HWND, LPARAM );
void GetResultStr( HWND );
void ImeUIEndComposition( HWND );
void ImeUIOpenCandidate( HWND, LPARAM );
void ImeUICloseCandidate( HWND, LPARAM );
void ImeUISetOpenStatus( HWND );
void DisplayResultString( HWND, LPSTR );
void DisplayCompString( HWND, LPSTR, LPSTR );
void RestoreImeUI( HWND );
BOOL ImeUINotify( HWND, WPARAM, LPARAM );
void ImeUIChangeCandidate( HWND, LPARAM );
void DisplayCandStrings( HWND, LPCANDIDATELIST );
void CandUIPaint( HWND );
void ImeUIMoveCandWin( HWND );
BOOL MoveCaret( HWND );
void ImeUIClearData( HWND );
void SetIMECompFormPos( HWND );

//
// Global data
//

extern char szSteClass[];
extern char szSteCandUIClass[];
extern char szSteTitle[];
extern char szSteCompTitle[];
extern char szSteCandTitle[];
extern char szCandClass[];

extern UINT   cxMetrics,
              cxOverTypeCaret,
              cyMetrics;

extern int    xPos, yPos;
extern HFONT  hfntFixed;
extern HFONT  hfntOld;
extern BOOL   fInsertMode;
extern int    CaretWidth;
extern int    DBCSFillChar;
extern BYTE   textbuf[MAXROW][MAXCOL];

extern IMEUIDATA gImeUIData;

extern HKL    hCurKL;


GLOBAL.C

#include <windows.h>
#include "imm.h"
#include "fullime.h"

/************************************************************************
*
* Global data
*
************************************************************************/

char szSteClass[10];                // window class name
char szSteCandUIClass[12];          // Candidate window class name
char szSteTitle[55];                // window frame title
char szSteCompTitle[ 55 ];          // window composition framw title
char szSteCandTitle[ 55 ];          // window candidate frame title
char szCandClass[ 20 ];             // Candidate window class name
UINT  cxMetrics,                    // aver. character width
      cxOverTypeCaret,              // caret width for overtype mode
      cyMetrics;                    // character height
int xPos, yPos;                     // caret position
HFONT hfntFixed;                    // fixed-pitch font
HFONT hfntOld;                      // default font holder
BOOL  fInsertMode;                  // insert/overtype mode flag
int CaretWidth;                     // insert/overtype mode caret width
BYTE textbuf[MAXROW][MAXCOL];       // text buffer
int DBCSFillChar;                   // 'Pattern/DBCS' fill character

IMEUIDATA gImeUIData;               // IMEUI's global data.

HKL hCurKL;                         // the current keyboad layout.


IMEUI.C

#include <assert.h>
#include <windows.h>
#include <imm.h>
#include "fullime.h"

static DWORD CompColor[ 4 ] = { RGB( 255, 0, 0 ),
                                RGB( 255, 0 , 255 ),
                                RGB( 0, 0, 255 ),
                                RGB( 0, 255, 0 ) };

//**********************************************************************
//
// void ImeUIStartComposition()
//
// This handles WM_IME_STARTCOMPOSITION message.
//
//**********************************************************************

void ImeUIStartComposition( HWND hwnd )
{

    //
    // Change caption title to DBCS composition mode.
    //

    SetWindowText( hwnd, (LPSTR)szSteCompTitle );

    //
    // Reset global variables.
    //

    gImeUIData.uCompLen = 0;             // length of composition string.

    gImeUIData.ImeState |= IME_IN_COMPOSITION;
}

//**********************************************************************
//
// void ImeUIComposition()
//
// This handles WM_IME_COMPOSITION message. It here just handles
// composition string and result string. For normal case, it should
// examine all posibile flags indicated by CompFlag, then do some
// actitions to reflect what kinds of composition info. IME conversion
// engine informs.
//
//**********************************************************************

void ImeUIComposition( HWND hwnd, WPARAM wParam, LPARAM CompFlag )
{

    if ( CompFlag & GCS_RESULTSTR )
         GetResultStr( hwnd );
    else
    if ( CompFlag & GCS_COMPSTR )
         GetCompositionStr( hwnd, CompFlag );

}

//**********************************************************************
//
// void GetCompositionStr()
//
// This handles WM_IME_COMPOSITION message with GCS_COMPSTR flag on.
//
//**********************************************************************

void GetCompositionStr( HWND hwnd, LPARAM CompFlag )
{
     DWORD      dwBufLen;               // Stogare for len. of composition str
     LPSTR      lpCompStr;              // Pointer to composition str.
     HIMC       hIMC;                   // Input context handle.
     HLOCAL     hMem;                   // Memory handle.
     LPSTR      lpCompStrAttr;          // Pointer to composition str array.
     HLOCAL     hMemAttr;               // Memory handle for comp. str. array.
     DWORD      dwBufLenAttr;

     //
     // If fail to get input context handle then do nothing.
     // Applications should call ImmGetContext API to get
     // input context handle.
     //

     if ( !( hIMC = ImmGetContext( hwnd ) ) )
         return;

     //
     // Determines how much memory space to store the composition string.
     // Applications should call ImmGetCompositionString with
     // GCS_COMPSTR flag on, buffer length zero, to get the bullfer
     // length.
     //

     if ( ( dwBufLen = ImmGetCompositionString( hIMC, GCS_COMPSTR,
                                         (void FAR*)NULL, 0l ) ) < 0 )
         goto exit2;

     //
     // Allocates memory with dwBufLen+1 bytes to store the composition
     // string. Here we allocale on more byte to put null character.
     //

     if ( !( hMem = LocalAlloc( LPTR, (int)dwBufLen + 1 ) ) )
         goto exit2;

     if ( !( lpCompStr = (LPSTR) LocalLock( hMem ) ) )
         goto exit1;

     //
     // Reads in the composition string.
     //

     ImmGetCompositionString( hIMC, GCS_COMPSTR, lpCompStr, dwBufLen );

     //
     // Null terminated.
     //

     lpCompStr[ dwBufLen ] = 0;

     //
     // If GCS_COMPATTR flag is on, then we need to take care of it.
     //

     if ( CompFlag & GCS_COMPATTR )
     {

         if ( ( dwBufLenAttr = ImmGetCompositionString( hIMC, GCS_COMPATTR,
                               ( void FAR *)NULL, 0l ) ) < 0 )
             goto nothing;

         //
         // Allocate memory to store attributes of composition strings.
         //

         if ( !( hMemAttr = LocalAlloc( LPTR, (int)dwBufLenAttr + 1 ) ) )
             goto nothing;

         if ( !( lpCompStrAttr = (LPSTR) LocalLock( hMemAttr ) ) )
         {
             LocalFree( hMemAttr );
             goto nothing;
         }

         //
         // Reads in the attribute array.
         //

         ImmGetCompositionString( hIMC, GCS_COMPATTR, lpCompStrAttr,
                                  dwBufLenAttr );

         lpCompStrAttr[ dwBufLenAttr ] = 0;

     } else
     {

nothing:
         lpCompStrAttr = NULL;
     }

     //
     // Display new composition chars.
     //

     DisplayCompString( hwnd, lpCompStr, lpCompStrAttr );

     //
     // Keep the length of the composition string for using later.
     //

     gImeUIData.uCompLen = (UINT)dwBufLen;

     LocalUnlock( hMem );

     if ( lpCompStrAttr )
     {
         LocalUnlock( hMemAttr );
         LocalFree( hMemAttr );
     }

exit1:

     LocalFree( hMem );

exit2:

     ImmReleaseContext( hwnd, hIMC );

}

//***********************************************************************
//
// void GetResultStr()
//
// This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
//
//***********************************************************************

void GetResultStr( HWND hwnd )
{
    DWORD       dwBufLen;               // Storage for length of result str.
    LPSTR       lpResultStr;            // Pointer to result string.
    HIMC        hIMC;                   // Input context handle.
    HLOCAL      hMem;                   // Memory handle.

    //
    // If fail to get input context handle then do nothing.
    //

    if ( !( hIMC = ImmGetContext( hwnd ) ) )
        return;

    //
    // Determines how much memory space to store the result string.
    // Applications should call ImmGetCompositionString with
    // GCS_RESULTSTR flag on, buffer length zero, to get the bullfer
    // length.
    //

    if ( ( dwBufLen = ImmGetCompositionString( hIMC, GCS_RESULTSTR,
                                  (void FAR *)NULL, (DWORD) 0 ) ) <= 0 )
        goto exit2;

    //
    // Allocates memory with dwBufLen+1 bytes to store the result
    // string. Here we allocale on more byte to put null character.
    //

    if ( !( hMem = LocalAlloc( LPTR, (int)dwBufLen + 1 ) ) )
        goto exit2;

    if ( !( lpResultStr = (LPSTR) LocalLock( hMem ) ) )
        goto exit1;

    //
    // Reads in the result string.
    //

    ImmGetCompositionString( hIMC, GCS_RESULTSTR, lpResultStr, dwBufLen );

    //
    // Displays the result string.
    //

    DisplayResultString( hwnd, lpResultStr );

    LocalUnlock( hMem );

exit1:

    LocalFree( hMem );

exit2:

    ImmReleaseContext( hwnd, hIMC );

}

//**********************************************************************
//
// void ImeUIEndComposition
//
// This handles WM_IME_ENDCOMPOSITION message.
//
//**********************************************************************

void ImeUIEndComposition( HWND hwnd )
{

    RECT       rect;

    //
    // Change caption title to normal
    //

    SetWindowText( hwnd, (LPSTR)szSteTitle );

    //
    // Update client area.
    //

    GetClientRect( hwnd, (LPRECT)&rect );

    InvalidateRect( hwnd, (LPRECT)&rect, FALSE );

    //
    // Reset the length of composition string to zero.
    //

    gImeUIData.uCompLen = 0;

    gImeUIData.ImeState &= ~IME_IN_COMPOSITION;

}

//**********************************************************************
//
// BOOL ImeUINotify()
//
// This handles WM_IME_NOTIFY message.
//
//**********************************************************************

BOOL ImeUINotify( HWND hwnd, WPARAM wParam, LPARAM lParam )
{
    switch (wParam )
    {
        case IMN_OPENCANDIDATE:

            ImeUIOpenCandidate( hwnd, lParam );
            break;

        case IMN_CLOSECANDIDATE:

            ImeUICloseCandidate( hwnd, lParam );
            break;

        case IMN_CHANGECANDIDATE:

            ImeUIChangeCandidate( hwnd, lParam );
            break;

        case IMN_SETOPENSTATUS:

            ImeUISetOpenStatus( hwnd );
            break;

default:
    return FALSE;

    }

    return TRUE;
}

//**********************************************************************
//
// void ImeUIOpenCandidate()
//
// This handles WM_IME_NOTIFY message with wParam = IMN_OPENCANDIDATE.
//
//**********************************************************************

void ImeUIOpenCandidate( HWND hwnd, LPARAM CandList )
{
    HIMC        hIMC;                   // Input context handle.
    LPCANDIDATELIST lpCandList;         // Storage for LP to candidate list.
    DWORD       dwBufLen;               // Storage for candidate strings.
    LPSTR       lpStr;                  // Storage for LP to a string.
    DWORD       dwIndex;                // Storage for index of ListCand array
    DWORD       i;                      // Loop count.
    int         width = 0;              // Storage for width of listCand
    int         CurNumCandList = 0;     // Storage for number of cand. lists.
    DWORD       dwPreferNumPerPage;     // Storage for PreferNumPerPage
    POINT       point;                  // Storage for caret position.

    //
    // If fail to get input context handle, do nothing.
    //

    if ( ! (hIMC = ImmGetContext( hwnd ) ) )
        return;

    //
    // Change caption title to DBCS candidate mode.
    //

    SetWindowText( hwnd, (LPSTR)szSteCandTitle );

    //
    // Find out how many candidate windows have already been opened.
    //

    for( i = 0; i < MAX_LISTCAND; i++ )
    {
        if ( gImeUIData.hListCand[ i ] )
            CurNumCandList++;
    }

    //
    // Check which candidate lists should be displayed by loopping
    // through all possible candidate lists.
    //

    for( dwIndex = 0; dwIndex < MAX_LISTCAND ; dwIndex ++ )
    {

        if ( CandList & ( 1 << dwIndex ) )
        {
            //
            // The dwIndex-th candidate list contains candidate strings.
            // So here we want to display them.
            //

            //
            // Determines how musch memory space should be allocated to
            // read in the corresponding candidate list .
            //

            if ( ! ( dwBufLen = ImmGetCandidateList( hIMC, dwIndex,
                       lpCandList, 0 ) ) )
                goto exit2;

            //
            // Allocate memory space.
            //

            if( !( gImeUIData.hListCandMem[ dwIndex ]  =
                   GlobalAlloc( LPTR, (int)dwBufLen ) ) )
                goto exit2;

            if( !( lpStr =
                 (LPSTR)GlobalLock( gImeUIData.hListCandMem[ dwIndex ] ) ) )
            {
                GlobalFree( gImeUIData.hListCandMem[ dwIndex ] );
                gImeUIData.hListCandMem[ dwIndex ] = NULL;
                goto exit2;
            }

            lpCandList = (LPCANDIDATELIST) lpStr;

            //
            // Reads in the corresponding candidate list.
            //

            ImmGetCandidateList( hIMC, dwIndex, lpCandList, dwBufLen );

            //
            // Get current caret position.
            //

            GetCaretPos( (POINT FAR*)&point );
            ClientToScreen( hwnd, (LPPOINT)&point );

            //
            // Determines how many candidate strings per page.
            //

            dwPreferNumPerPage = ( !lpCandList->dwPageSize ) ?
                                 DEFAULT_CAND_NUM_PER_PAGE :
                                 lpCandList->dwPageSize;
            //
            // Determining maximum character length the list box
            // will display by loopping through all candidate strings.
            //

            for( i = 0; i < lpCandList->dwCount; i++ )
            {
                //
                // Get the pointer to i-th candidate string.
                //

                lpStr = (LPSTR)lpCandList +
                        lpCandList->dwOffset[ i ];

                width = ( width < lstrlen( lpStr ) ) ? lstrlen( lpStr ) :
                          width;

            }

            //
            // Create a candidate window for the candidate list.
            //

            gImeUIData.hListCand[ dwIndex ] = CreateWindow(
                                 (LPCTSTR)szCandClass,
                                 NULL,
                                 WS_BORDER | WS_POPUP | WS_DISABLED,
                                 CurNumCandList * X_INDENT + point.x,
                                 CurNumCandList * Y_INDENT +
                                 point.y + cyMetrics,
                                 ( width ) * cxMetrics + 10,
                                 (int)(dwPreferNumPerPage) * cyMetrics + 5,,
                                 hwnd,
                                 (HMENU)NULL,
                                 (HANDLE)GetWindowLong( hwnd, GWL_HINSTANCE ),
                                 (LPVOID)NULL
                                 );
            //
            // If fail to create the candidate window then do nothing.
            //

            if ( gImeUIData.hListCand[ dwIndex ] < 0 )
            {
                GlobalUnlock( gImeUIData.hListCandMem[ dwIndex ] );
                GlobalFree( gImeUIData.hListCandMem[ dwIndex ] );
                goto exit2;
            }

            //
            // Show candidate window.
            //

            ShowWindow( gImeUIData.hListCand[ dwIndex ], SW_SHOWNOACTIVATE );

            //
            // Display candidate strings.
            //

            DisplayCandStrings( gImeUIData.hListCand[ dwIndex ], lpCandList );

            GlobalUnlock( gImeUIData.hListCandMem[ dwIndex ] );

            CurNumCandList++;
        }
    }

    //
    // Reset IME state.
    //

    gImeUIData.ImeState |= IME_IN_CHOSECAND;

exit2:

    ImmReleaseContext( hwnd, hIMC );

}

//**********************************************************************
//
// void ImeUICloseCandidate()
//
// This handles WM_IME_NOTIFY message with
// wParam = IMN_CLOSECANDIDATE.
//
//**********************************************************************

void ImeUICloseCandidate( HWND hwnd, LPARAM CandList )
{
    int         index;

    //
    // Change window's caption title to normal.
    //

    SetWindowText( hwnd, (LPSTR)szSteCompTitle );

    //
    // If the i-th bit on of CandList that means the i-th
    // candidate list should be closed.
    //

    for( index = 0; index < MAX_LISTCAND; index ++ )
    {
        if (( CandList & ( 1 << index ) ) && gImeUIData.hListCand[ index ])
        {

            //
            // Destroy the candidate window.
            //

            DestroyWindow( gImeUIData.hListCand[ index ] );

            gImeUIData.hListCand[ index ] = NULL;

            //
            // Free memory.
            //

            GlobalFree( gImeUIData.hListCandMem[ index ] );

            gImeUIData.hListCandMem[ index ] = NULL;

        }
    }

    gImeUIData.ImeState &= ~IME_IN_CHOSECAND;

}

//**********************************************************************
//
// void ImeUIChangeCandidate()
//
// This handles WM_IME_NOTIFY message with wParam = IMN_CHANGECANDIDATE.
//
//**********************************************************************

void ImeUIChangeCandidate( HWND hwnd, LPARAM CandList )
{
    HIMC            hIMC;
    LPCANDIDATELIST lpCandList = NULL;
    DWORD           dwIndex;
    DWORD     dwBufLen;
    LPSTR    lpStr;
    DWORD    i;
    RECT    rect;
    int    width = 0;
    DWORD    dwPreferNumPerPage;

    //
    // If fail to get input context, do nothing.
    //

    if ( !( hIMC = ImmGetContext( hwnd ) ) )
        return;

    //
    // Determine which candidate list should be updated.
    //

    for ( dwIndex = 0; dwIndex < MAX_LISTCAND; dwIndex++ )
        if ( CandList & ( 1 << dwIndex ) )
            break;

    //
    // If dwIndex == MAX_LISTCAND, then something wrong, do nothing.
    //

    if ( dwIndex == MAX_LISTCAND )
        return;

    //
    // Determines how much memory space should be allocated to read in the
    // corresponding candidate list.
    //

    if ( !( dwBufLen = ImmGetCandidateList( hIMC, dwIndex, lpCandList, 0 ) ) )
goto exit2;

    //
    // Relocate memory space.
    //

    if ( !( gImeUIData.hListCandMem[ dwIndex ] = GlobalReAlloc(
    gImeUIData.hListCandMem[ dwIndex ], (int)dwBufLen, LPTR ) ) )
goto exit2;

    if ( !( lpStr =
(LPSTR)GlobalLock( gImeUIData.hListCandMem[ dwIndex ] ) ) )
    {
GlobalFree( gImeUIData.hListCandMem[ dwIndex ] );
        gImeUIData.hListCandMem[ dwIndex ] = NULL;
goto exit2;
    }

    lpCandList = (LPCANDIDATELIST) lpStr;

    //
    // Reads in the corresponding candidate list.
    //

    ImmGetCandidateList( hIMC, dwIndex, lpCandList, dwBufLen );

    //
    // Determines how many candidate strings per page.
    //

    dwPreferNumPerPage = ( !lpCandList->dwPageSize ) ?
                         DEFAULT_CAND_NUM_PER_PAGE :
                         lpCandList->dwPageSize;
    //
    // Determining maximum character length  the list box
    // will display by loopping through all candidate strings.
    //

    for( i = 0; i < lpCandList->dwCount; i++ )
    {
        //
        // Get the pointer to i-th candidate string.
        //

        lpStr = (LPSTR)lpCandList +
                lpCandList->dwOffset[ i ];

        width = ( width < lstrlen( lpStr ) ) ? lstrlen( lpStr ) :
                  width;

    }

    GetWindowRect( gImeUIData.hListCand[ dwIndex ] , (LPRECT) &rect);

    SetWindowPos( gImeUIData.hListCand[ dwIndex ],
  hwnd,
  rect.left,
  rect.top,
  ( width ) * cxMetrics + 10,
  (int)(dwPreferNumPerPage) * cyMetrics + 5,
  SWP_NOZORDER | SWP_NOACTIVATE );

    DisplayCandStrings( gImeUIData.hListCand[ dwIndex ], lpCandList );

    GlobalUnlock( gImeUIData.hListCandMem[ dwIndex ] );

exit2:

    return;

}

//***********************************************************************
//
// void ImeUISetOpenStatus()
//
// This handles WM_IME_REPORT message with wParam = IR_NOTIFY &
// lParam = IMC_SETOPENSTATUS.
//
//**********************************************************************

void ImeUISetOpenStatus( HWND hwnd )
{
    int         i;       // Lopp counter
    HIMC        hIMC;    // Storage for input context handle.

    //
    // If fail to get input context handle then do nothing
    //

    if ( !( hIMC = ImmGetContext( hwnd ) ) )
        return;

    if ( ImmGetOpenStatus( hIMC ) )
    {

        //
        // If the IME conversion engine is open, here we change
        // window's caption title to DBCS composition mode.
        //

        SetWindowText( hwnd, (LPSTR)szSteCompTitle );

    } else
    {
        RECT    rect;

        //
        // If the IME conversion engine is closed, here we
        // erase all already displayed composition chars if any,
        // change the window's caption title to normal.
        //

        GetClientRect( hwnd, (LPRECT)&rect );
        InvalidateRect( hwnd, (LPRECT)&rect, FALSE );

        SetWindowText( hwnd, (LPSTR)szSteTitle );

        //
        // Here we close and destroy all of candidate windows
        // if IME conversion engine is closed.
        //

        for( i = 0; i <= MAX_LISTCAND; i++ )
        {
            if ( gImeUIData.hListCand[ i ] )
            {
                DestroyWindow( gImeUIData.hListCand[ i ] );
                gImeUIData.hListCand[ i ] = NULL;
                GlobalFree( gImeUIData.hListCandMem[ i ] );
                gImeUIData.hListCandMem[ i ] = NULL;
            }
        }

        //
        // Reset IMEUI's global data.
        //

        gImeUIData.uCompLen = 0;
        gImeUIData.ImeState = 0;

        ResetCaret( hwnd );

    }

    ImmReleaseContext( hwnd, hIMC );
}

//*********************************************************************
//
// void DisplayCompString()
//
// This displays composition string.
//
// This function supports only fixed pitch font.
//
//*********************************************************************

void DisplayCompString( HWND hwnd, LPSTR lpStr, LPSTR lpStrAttr )
{
    HDC         hdc;
    int         StrLen = lstrlen( lpStr );
    RECT        rect;
    DWORD       dwColor;
    int         i;
    BOOL        fDBCSTrailByte = FALSE;

    hdc = GetDC( hwnd );

    HideCaret( hwnd );

    //
    // Determine OPAQUE rect.
    //

    rect.left = xPos * cxMetrics;
    rect.top  = yPos * cyMetrics;
    rect.bottom = rect.top + cyMetrics;
    rect.right = ( (int)gImeUIData.uCompLen > StrLen ) ?
                 ( xPos + gImeUIData.uCompLen ) * cxMetrics:
                 ( xPos + StrLen ) * cxMetrics;

    //
    // This example we use red to display composition chars
    // with attribute 000, pink for attribute 001,
    // blue for attribute 010, green for attribute 011.
    //

    //
    // Each composition character has different attribute.
    // We here use different kinds of color to represent attributes.
    // Red, pink, blue and green are for attribute 000, 001, 010 and 011,
    // respectively.
    //

    dwColor = GetTextColor( hdc );

    if ( ( StrLen % 2 ) )
    {
        if ( IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 1 ] ) )
        {
            textbuf[ yPos ][ xPos + StrLen ] = ' ';
            fDBCSTrailByte = TRUE;
        }
    } else
    {
        if ( !IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 2 ] ) )
        {
            if ( IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 1 ] ) )
    {
                textbuf[ yPos ][ xPos + StrLen ] = ' ';
                fDBCSTrailByte = TRUE;
            }
        }
    }

    if ( !lpStrAttr )
    {
        //
        // If there are not attribute array, here we use default color, RED,
        // to display all of composition characters.
        //

        SetTextColor( hdc, CompColor[ 0 ] ); // default color

        ExtTextOut( hdc, xPos * cxMetrics, yPos * cyMetrics,
                ETO_OPAQUE, &rect, lpStr, StrLen, 0 );

    } else
    {

        int  ColorIndex;

        ExtTextOut( hdc, xPos * cxMetrics, yPos * cyMetrics, ETO_OPAQUE,
                    &rect, NULL, 0, 0 );

        for( i = 0; *lpStr;)
        {
            int cnt = IsDBCSLeadByte(*lpStr) ? 2 : 1;

            ColorIndex = ( ((int)*lpStrAttr) < 0 ) ? 0 : (int)*lpStrAttr;
            ColorIndex = ( ColorIndex > 3 ) ? 3 : ColorIndex;

            SetTextColor( hdc, CompColor[ ColorIndex ] );

            TextOut( hdc, ( i + xPos ) * cxMetrics, yPos * cyMetrics,
                     lpStr, cnt );
            lpStr += cnt;
            lpStrAttr += cnt;
            i += cnt;
        }
    }

    SetTextColor( hdc, dwColor );

    if ( fDBCSTrailByte )
    {
        TextOut( hdc, ( xPos + StrLen ) * cxMetrics, yPos * cyMetrics,
         " ", 1 );
    }

    SetCaretPos( ( xPos + StrLen ) * cxMetrics, yPos * cyMetrics );

    ShowCaret( hwnd );

    ReleaseDC( hwnd, hdc );

}

//*********************************************************************
//
// void DisplayResultString()
//
// This displays result string.
//
// This function supports only fixed pitch font.
//
//*********************************************************************

void DisplayResultString( HWND hwnd, LPSTR lpStr )
{

    int         StrLen;         // Storage for string length.
    int         i;              // Loop counter.
    HDC         hdc;            // Display context handle.

    StrLen = lstrlen( lpStr );

    //
    // If there is no room for compsoition string, discard it
    //

    if ( xPos == ( LASTCOL - StrLen - 1 ) )
        return;

    //
    // if insert mode or during composition session,
    // move rest of line to the right by StrLen bytes.
    //

    if ( fInsertMode )
    {

        for( i = LASTCOL; i > xPos; i-- )
            textbuf[ yPos ][ i ] = textbuf[ yPos ][ i - StrLen ];

        //
        // If the row ends on a lead byte, blank it out,
        // To do this we must first traverse the string starting
        // from a know character boundry until we reach the last column.
        // If the last column is a character boundry then the last
        // character is either a string byte or a lead byte.
        //

        for( i = xPos + StrLen; i < LASTCOL; )
        {
            if ( IsDBCSLeadByte( textbuf[ yPos ][ i ] ) )
                i++;
            i++;
        }

        if ( i == LASTCOL && IsDBCSLeadByte( textbuf[ yPos ][ i ] ) )
            textbuf[ yPos ][ LASTCOL ] = ' ';

    } else
    {
        //
        // overtype mode
        //

        if ( ( StrLen % 2 ) )
        {
            if ( IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 1 ] ) )
                textbuf[ yPos ][ xPos + StrLen ] = ' ';
        } else
        {
            if ( !IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 2 ] ) )
            {
                //
                // Overtyping the current byte, plus the following byte
                // which could be a lead byte.
                //

                if ( IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 1 ] ) )
                    textbuf[ yPos ][ xPos + StrLen ] = ' ';
            }
        }
    }

    //
    // Store input character at current caret position.
    //

    for( i = 0; i <= LASTCOL && *lpStr; i++ )
        textbuf[ yPos ][ xPos + i ] = *lpStr++;

    //
    // Display input character
    //

    hdc = GetDC( hwnd );

    HideCaret( hwnd );

    //
    // Displays result string with normal color.
    //

    TextOut( hdc, xPos *cxMetrics, yPos * cyMetrics,
             &( textbuf[ yPos][xPos] ), MAXCOL - xPos );

    ShowCaret( hwnd );

    ReleaseDC( hwnd, hdc );

    //
    // Reset Caret position
    //

    xPos += StrLen;

    if ( xPos > LASTCOL )
        xPos = LASTCOL;

    ResetCaret( hwnd );

    gImeUIData.uCompLen = 0;

}

//**********************************************************************
//
// void RestoreImeUI()
//
// This repaints all displayed composition string if need.
// Main window procedure will call this upon receiving
// WM_PAINT message.
//
//**********************************************************************

void RestoreImeUI( HWND hwnd )
{
    HIMC        hIMC;           // Storage for input context handle.
    DWORD       dwBufLen;       //

    //
    // If fail to get input context handle then do nothing.
    //

    if ( !( hIMC = ImmGetContext( hwnd ) ) )
        return;

    //
    // If IME conversion engine is open and there are any composition
    // string in the context then we redisplay them.
    //

    if ( ImmGetOpenStatus( hIMC ) && gImeUIData.ImeState &&
         ( dwBufLen = ImmGetCompositionString( hIMC, GCS_COMPSTR,
                      (void FAR*)NULL, 0l ) ) > 0 )
    {
        LPSTR       lpCompStr;     // Pointer to composition string
        HLOCAL      hMem;          // Storage for memory handle.
        LPSTR       lpCompStrAttr; // Pointer to composition string's attribute
        DWORD       dwBufLenAttr;  //
        HLOCAL      hMemAttr;      // Memory handle for composition string's
                                   // attributes.

        //
        // If fail to allocate and lock memory space for reading in
        // the composition string then do nothing.
        //

        if ( !( hMem = LocalAlloc( LPTR, (int)dwBufLen + 1 ) ) )
            goto exit2;

        if( !( lpCompStr = (LPSTR) LocalLock( hMem ) ) )
        {
            LocalFree( hMem );
            goto exit2;
        }

        //
        // Get composition string and redisplay them.
        //

        if ( ImmGetCompositionString( hIMC, GCS_COMPSTR, lpCompStr,
                                      dwBufLen ) > 0 )
        {

            //
            // MAke sure whether we need to handle composition string's
            // attributes.
            //

            if ( ( dwBufLenAttr = ( ImmGetCompositionString( hIMC,
                       GCS_COMPATTR, (void FAR*)NULL, 0l ) ) ) > 0 )
            {
                //
                // If fail to allocate and lock memory space for reading in
                // the composition string's attribute then we assume
                // no attribute array.
                //

                if ( !( hMemAttr = LocalAlloc(LPTR, (int)dwBufLenAttr + 1 )))
                    goto nothing;

                if ( !( lpCompStrAttr = (LPSTR) LocalLock( hMemAttr ) ) )
                {
                    LocalFree( hMemAttr );
                    goto nothing;
                }

                ImmGetCompositionString( hIMC, GCS_COMPATTR, lpCompStrAttr,
                                         dwBufLenAttr );

                lpCompStrAttr[ dwBufLenAttr ] = 0;
            } else
            {
nothing:
                lpCompStrAttr = NULL;
            }

            lpCompStr[ dwBufLen ] = 0;

            DisplayCompString( hwnd, lpCompStr, lpCompStrAttr );

        }

        LocalUnlock( hMem );
        LocalFree( hMem );

        if ( lpCompStrAttr )
        {
            LocalUnlock( hMemAttr );
            LocalFree( hMemAttr );
        }

    }

exit2:

    ImmReleaseContext( hwnd, hIMC );

}

//**********************************************************************
//
// void ImeUIMove()
//
// Handler routine of WM_MOVE message.
//
//*********************************************************************

void ImeUIMoveCandWin( HWND hwnd )
{

    if ( gImeUIData.ImeState & IME_IN_CHOSECAND )
    {
        POINT           point;          // Storage for caret position.
        int             i;              // loop counter.
        int             NumCandWin;     // Storage for num of cand win.
        RECT            rect;           // Storage for client rect.

        //
        // If current IME state is in chosing candidate, here we
        // move all candidate windows, if any, to the appropriate
        // position based on the parent window's position.
        //

        NumCandWin = 0;

        GetCaretPos( (LPPOINT)&point );
        ClientToScreen( hwnd, (LPPOINT)&point );

        for ( i = 0; i < MAX_LISTCAND ; i++ )
        {
            if ( gImeUIData.hListCand[ i ] )
            {
                GetClientRect( gImeUIData.hListCand[ i ], &rect );

                MoveWindow( gImeUIData.hListCand[ i ],
                            point.x + X_INDENT * NumCandWin,
                            point.y + Y_INDENT * NumCandWin + cyMetrics,
                            ( rect.right - rect.left + 1 ),
                            ( rect.bottom - rect.top + 1 ), TRUE );

                NumCandWin++;
            }
        }
    }
}

//**********************************************************************
//
// void ImeUIClearData()
//
// Handler routine of WM_IME_SELECT message.
//
//**********************************************************************

void ImeUIClearData( HWND hwnd )
{

    RECT            rect;
    int             i;

    SetWindowText( hwnd, (LPSTR)szSteTitle );

    //
    // If user switches to other IME, here we destroy all candidate
    // windows which has been opened and erase all composition
    // chars if any.
    //

    for( i = 0; i < MAX_LISTCAND; i++ )
    {
        if ( gImeUIData.hListCand[ i ] )
        {
            //
            // The i-th candidate list has already been displayed,
            // destroy it and free memory which stores candidate
            // strings.
            //

            DestroyWindow( gImeUIData.hListCand[ i] );
            GlobalFree( gImeUIData.hListCandMem[ i ] );

            gImeUIData.hListCand[ i ] =
            gImeUIData.hListCandMem[ i ] = NULL;

        }
    }

    //
    // Update client area.
    //

    GetClientRect( hwnd, (LPRECT)&rect );

    InvalidateRect( hwnd, (LPRECT)&rect, FALSE );

    //
    // Reset IMEUI's global data.
    //

    gImeUIData.uCompLen = gImeUIData.ImeState = 0;

    //
    // Reset caret to the original position.
    //

    HideCaret( hwnd );
    ResetCaret( hwnd );
    ShowCaret( hwnd );

}


MAIN.C

/************************************************************************
*
* Title:
*
*   STED.C - IME Full-aware Simple Text Editor (DBCS version)
*
* Purpose:
*
*   Sample program for DBCS programming and IME full-aware aappliction.
*
* Synopsis:
*
*   This program is designed as a bare-bone example to demonstrate the
*   basic elements of DBCS-enabling, and how to design an IME full-aware
*   application.
*
*   The data structure is a fixed-size 2-dimensional byte array.  The
*   font is the fixed-pitch system font.  Rudimentary text editing
*   functions, such as the basic cursor movements, insertion, deletion,
*   have been implemented.
*
*   When you run this program in a Far East Windows environment, it
*   should be apparent that it doesn't handle DBCS character very well.
*   It is your job to locate and modify the pieces inside this program
*   that need to be DBCS-enabled.
*
* DBCS-enabling notes:
*
*   This version of STE is DBCS-enabled with respect to character input,
*   caret shape and movement, and mouse clicking.  It should work on
*   any version of Far East Windows since 3.0.
*
*   As far as source code maintenance goes, there are generally two
*   approaches.
*
*   The first is to add DBCS enabling code under '#ifdef DBCS'.  The
*   advantage of this approach is that it keeps the DBCS enabling code
*   distinct from the SBCS, so it's easier to add them in, and also to
*   remove them later.  (For example, when you want to replace DBCS
*   enabling with Unicode enabling.)  The drawback is that because the
*   DBCS and the SBCS logic are not integrated, they can easily get out
*   of sync (as the SBCS code evolves.)
*
*   The second approach, which is adopted by this sample app, is to
*   integrate DBCS enabling with SBCS.  It takes longer to do, but
*   the resulting source is easier to maintain.  Since IsDBCSLeadByte
*   is at the heart of any DBCS-enabling logic, an additional speed up
*   for generating an SBCS-only version is to define that function as
*   FALSE, and let the compiler optimize the DBCS logic away.
*
* IME Full-Aware notes:
*
*   This version of STE is an IME full-aware application with most of
*   IME UI capiabilities to display IME UI by itself. It should work
*   on any version of Far Windows since 4.0.
*
*   This kind of application typically wants to be fully responsible to
*   display any information given by IME. Ii will handle the input context
*   by itself and duisplay any necessary information given by the
*   input context not using IME UI.
*
* History:
*
*   17-Aug-1992  created
*   28-Sep-1992  added DBCS-enabling
*   30-Sep-1992  bug fixes
*   25-Mar-1994  added IME full-aware logics
*
************************************************************************/

#include <assert.h>
#include <windows.h>
#include <imm.h>
#include "fullime.h"

//
// Function prototype
//

/************************************************************************
*
*   SteRegister - standard class registration routine
*
************************************************************************/

int SteRegister( HANDLE hInstance )
{
    long WINAPI SteWndProc( HWND, UINT, UINT, LONG );

    PWNDCLASS pWndClass;

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

    pWndClass->hCursor       = LoadCursor( NULL, IDC_IBEAM );
    pWndClass->hIcon         = LoadIcon( hInstance, MAKEINTRESOURCE(ID_ICON));
    pWndClass->lpszMenuName  = MAKEINTRESOURCE(ID_MENU);
    pWndClass->hInstance     = hInstance;
    pWndClass->lpszClassName = (LPSTR)szSteClass;
    pWndClass->lpfnWndProc   = SteWndProc;
    pWndClass->hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    pWndClass->style         = CS_BYTEALIGNCLIENT | CS_CLASSDC;

    if ( !RegisterClass( (LPWNDCLASS)pWndClass ) )
return FALSE;

    pWndClass->hCursor       = LoadCursor( NULL,IDC_ARROW );
    pWndClass->hIcon         = NULL;
    pWndClass->lpszMenuName  = NULL;
    pWndClass->hInstance     = hInstance;
    pWndClass->lpszClassName = (LPSTR)szCandClass;
    pWndClass->lpfnWndProc   = CandWndProc;
    pWndClass->hbrBackground = GetStockObject( LTGRAY_BRUSH );
    pWndClass->style         = CS_HREDRAW | CS_VREDRAW;

    if ( !RegisterClass( (LPWNDCLASS)pWndClass ) )
return FALSE;

    LocalFree( (HANDLE)pWndClass );

    return TRUE;
}

/************************************************************************
*
*   WinMain
*
************************************************************************/

int PASCAL WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpszCmdLine,
    int nCmdShow )
{
    MSG msg;
    HWND hWnd;

    LoadString( hInstance, IDS_CLASS, szSteClass, 14 );
    LoadString( hInstance, IDS_TITLE, szSteTitle, 55 );
    LoadString( hInstance, IDS_CANDUI, szSteCandUIClass, 12 );
    LoadString( hInstance, IDS_COMPTITLE, szSteCompTitle, 55 );
    LoadString( hInstance, IDS_CANDTITLE, szSteCandTitle, 55 );
    LoadString( hInstance, IDS_CANDCLASS, szCandClass, 20 );

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

    if ( !(hWnd = CreateWindow( szSteClass, szSteTitle,
  WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, (HWND)NULL,
  (HMENU)NULL, hInstance, NULL)) )
return FALSE;

    //
    // Create window with just enough client space for the text buffer
    //

    SetWindowPos( hWnd, 0, 0, 0,
MAXCOL*cxMetrics + GetSystemMetrics(SM_CXBORDER)*2,
MAXROW*cyMetrics + GetSystemMetrics(SM_CYBORDER)*2
 + GetSystemMetrics(SM_CYCAPTION)
 + GetSystemMetrics(SM_CYMENU),
SWP_NOZORDER );
    ShowWindow( hWnd, nCmdShow );

    //
    // IME initial state.
    //

    gImeUIData.ImeState = 0;

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

    return msg.wParam;
}

//**********************************************************************
//
// SteIMEOpenClose()
//
// This routines calls IMM API to open or close IME.
//
//**********************************************************************

void SteImeOpenClose( HWND hWnd, BOOL fFlag )
{
HIMC            hIMC;

//
// If fFlag is true then open IME; otherwise close it.
//

        if ( !( hIMC = ImmGetContext( hWnd ) ) )
return;

        ImmSetOpenStatus( hIMC, fFlag );

        ImmReleaseContext( hWnd, hIMC );

}

//*********************************************************************
//
// WM_CREATE message handler
//
//*********************************************************************

void SteCreate( HWND hWnd )
{
    HDC hdc;
    TEXTMETRIC tm;
    int        i;
    WORD       patern = 0xA4A4;
    SIZE       size;
    HFONT      hFont;
    LOGFONT    lFont;

    //
    // Note that this window has a class DC
    //

    hdc = GetDC( hWnd );

    //
    // Select fixed pitch system font and get its text metrics
    //

    hfntFixed = GetStockObject( SYSTEM_FIXED_FONT );
    hfntOld = SelectObject( hdc, hfntFixed );
    GetTextMetrics( hdc, &tm );
    ReleaseDC( hWnd, hdc );

    GetTextExtentPoint( hdc, (LPSTR)&patern, sizeof( WORD),
(LPSIZE) &size );

//    cxMetrics = tm.tmAveCharWidth;
//    cyMetrics = tm.tmHeight;

    cxMetrics = (UINT) size.cx / 2;
    cyMetrics = (UINT) size.cy;

    //
    // Determine the version of DBCS Windows from system font charset ID.
    // Then hardcode a DBCS character value for the 'Pattern/DBCS' command.
    // The value is the Han character for 'door' or 'gate', which is
    // left-right symmetrical.
    //

    switch( tm.tmCharSet )
    {
    case SHIFTJIS_CHARSET:
DBCSFillChar = 0x96e5;
break;

    case HANGEUL_CHARSET:
DBCSFillChar = 0xdaa6;
break;

    case CHINESEBIG5_CHARSET:
DBCSFillChar = 0xaaf9;
break;

    default:
DBCSFillChar = 0x7071;  // 'pq'
break;
    }

    //
    // Initialize caret width.  Fat in INSERT mode, skinny in OVERTYPE mode.
    //

    fInsertMode = FALSE;
    CaretWidth = cxOverTypeCaret = GetSystemMetrics( SM_CXBORDER );

    //
    // Sets the logical font to be used to display characters in the
    // composition window. Especially for at caret or near caret operation,
    // application should set composition font.
    //
    // If Application provides user to dynamicly change font, each time after
    // user change font, application should set composition font again.
    //

    if ( ( hFont = (HFONT)SendMessage( hWnd, WM_GETFONT, 0, 0L ) ) != NULL )
    {
        if ( GetObject( hFont, sizeof(LOGFONT), (LPVOID)&lFont ) )
        {
            HIMChImc;

            if ( (  hImc = ImmGetContext( hWnd ) ) )
    {
        ImmSetCompositionFont( hImc, &lFont );
ImmReleaseContext( hWnd, hImc );
    }
}
    }

    //
    // Get the property and apiabilities of current keyboard layout(IME).
    // If the keyboard layout is US, the return value will be zero.
    //

    gImeUIData.fdwProperty = ImmGetProperty( GetKeyboardLayout(0),
     IGP_PROPERTY );

    //
    // Initialize candidate list window array.
    //

    for( i = 0; i < MAX_LISTCAND; i++ )
    {
         gImeUIData.hListCand[ i ] = NULL;
         gImeUIData.hListCandMem[ i ] = NULL;
    }

    PostMessage( hWnd, WM_COMMAND, IDC_CLEAR, 0L );

    //
    // Initialise the current keyboard layout.
    //
    hCurKL = GetKeyboardLayout(0L);
}

/************************************************************************
*
*   ResetCaret - Reset caret shape to match input mode (overtype/insert)
*
************************************************************************/

void ResetCaret( HWND hWnd )
{

    HideCaret( hWnd );
    DestroyCaret();
    CreateCaret( hWnd,
 NULL,
 (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ?
   CaretWidth*2 : CaretWidth,
 cyMetrics );
    SetCaretPos( xPos * cxMetrics, yPos * cyMetrics );

    if ( !( gImeUIData.fdwProperty & IME_PROP_AT_CARET ) &&
         !( gImeUIData.fdwProperty & IME_PROP_SPECIAL_UI ) )
    {
// near caret.
        SetIMECompFormPos( hWnd );
    }

    ShowCaret( hWnd );

}

//**********************************************************************
//
// SetIMECompFormPos()
//
//**********************************************************************

void SetIMECompFormPos( HWND hWnd )
{

    HIMC hIMC = ImmGetContext(hWnd);
    POINT   point;
    COMPOSITIONFORM CompForm;

    GetCaretPos( &point );

    CompForm.dwStyle = CFS_POINT;

    CompForm.ptCurrentPos.x = (long) point.x;
    CompForm.ptCurrentPos.y = (long) point.y;

    if ( hIMC )
ImmSetCompositionWindow(hIMC,&CompForm);
    ImmReleaseContext( hWnd , hIMC);

}

/************************************************************************
*
*   SteCommand - WM_COMMAND handler
*
************************************************************************/

void SteCommand( HWND hWnd, UINT cmd, LPARAM lParam )
{
    switch( cmd )
    {
    case IDC_CLEAR:

//
// Blank out text buffer.  Return caret to home position
//

for ( yPos = FIRSTROW; yPos <= LASTROW; yPos++ )
    for ( xPos = FIRSTCOL; xPos <= LASTCOL; xPos++ )
textbuf[yPos][xPos] = ' ';
break;

    case IDC_ANSIFILL:
    case IDC_DBCSFILL:

//
// Fill text buffer with ANSI or DBCS pattern
//

for ( yPos = FIRSTROW; yPos <= LASTROW; yPos++ )
    for ( xPos = FIRSTCOL; xPos <= LASTCOL; xPos++ )
if ( cmd == IDC_ANSIFILL )
    textbuf[yPos][xPos] = 'a';
else {
    textbuf[yPos][xPos]   = HIBYTE(DBCSFillChar);
    textbuf[yPos][++xPos] = LOBYTE(DBCSFillChar);
}
break;

    //
    // The following messages are to control IME.
    //

    case IDC_OPENIME:

SteImeOpenClose( hWnd, TRUE );
goto RETURN;

    case IDC_CLOSEIME:

SteImeOpenClose( hWnd, FALSE );
goto RETURN;

    }

    yPos = FIRSTROW;
    xPos = FIRSTCOL;

    InvalidateRect( hWnd, (LPRECT)NULL, TRUE );
    ResetCaret(hWnd);

RETURN:

    return;
}

/************************************************************************
*
*   IsDBCSTrailByte - returns TRUE if the given byte is a DBCS trail byte
*
*                     The algorithm searchs backward in the string, to some
*                     known character boundary, counting consecutive bytes
*                     in the lead byte range. An odd number indicates the
*                     current byte is part of a two byte character code.
*
*   INPUT: PCHAR  - pointer to a preceding known character boundary.
*          PCHAR  - pointer to the character to test.
*
*   OUTPUT:BOOL   - indicating truth of p==trailbyte.
*
************************************************************************/

BOOL IsDBCSTrailByte( char *base, char *p )
{
    int lbc = 0;    // lead byte count

    assert(base <= p);

    while ( p > base ) {
if ( !IsDBCSLeadByte(*(--p)) )
    break;
lbc++;
    }

    return (lbc & 1);
}

//**********************************************************************
//
// BOOL MoveCaret()
//
//**********************************************************************

BOOL MoveCaret( HWND hwnd )
{
    HIMC        hIMC;
    BOOL        retVal = TRUE;

    if ( !( hIMC = ImmGetContext( hwnd ) ) )
return retVal;

    if ( ImmGetCompositionString( hIMC, GCS_CURSORPOS,
  (void FAR *)NULL, 0 ) )
retVal = FALSE;

    ImmReleaseContext( hwnd, hIMC );

    return retVal;
}

/************************************************************************
*
*   VirtualKeyHandler - WM_KEYDOWN handler
*
*
*   INPUT:  HWND - handle to the window for repainting output.
*           UINT - virtual key code.
*
************************************************************************/

void VirtualKeyHandler( HWND hWnd, UINT wParam )
{
    int i;
    HDC hdc;
    static int delta = 1;

    if ( ( gImeUIData.ImeState & IME_IN_CHOSECAND ) ||
         ( gImeUIData.ImeState & IME_IN_COMPOSITION && !MoveCaret( hWnd ) ) )
return;

    switch( wParam )
    {
    case VK_HOME:   // beginning of line
xPos = FIRSTCOL;
break;

    case VK_END:    // end of line
xPos = LASTCOL;
goto check_for_trailbyte;

    case VK_RIGHT:

if ( IsDBCSLeadByte( textbuf[yPos][xPos] ) ){
    if (xPos==LASTCOL - 1) break;  //last character don't move
    xPos += 2;                     //skip 2 for DB Character
}
else
    xPos = min( xPos+1, LASTCOL );
break;

    case VK_LEFT:

xPos = max( xPos-1, FIRSTCOL );

check_for_trailbyte:

if ( IsDBCSTrailByte( textbuf[yPos], &(textbuf[yPos][xPos]) ) )
    xPos--;
break;

    case VK_UP:

yPos = max( yPos-1, FIRSTROW );
goto Virtical_Check_Trail;

    case VK_DOWN:

yPos = min( yPos+1, LASTROW );

Virtical_Check_Trail:

if ( IsDBCSTrailByte( textbuf[yPos], &(textbuf[yPos][xPos]) ) ){
   if (xPos<LASTCOL){
      xPos+=delta;
      delta *= -1;
   }
   else
      xPos--;
}
break;

    case VK_INSERT:

//
// Change caret shape to indicate insert/overtype mode
//

fInsertMode = !fInsertMode;
CaretWidth = fInsertMode ? cxMetrics : cxOverTypeCaret;
break;

     case VK_BACK:   // backspace

if ( xPos > FIRSTCOL ) {
    xPos--;

    //
    // DB Character so backup one more to allign on boundary
    //
    if ( IsDBCSTrailByte( textbuf[yPos], &(textbuf[yPos][xPos]) ) )
xPos--;
//
// Fall Through to VK_DELETE to adjust row
//
}
else     //FIRST COLUMN  don't backup -- this would change for wrapping
   break;

    case VK_DELETE:

if ( !IsDBCSLeadByte( textbuf[yPos][xPos] ) ) {

    //
    // Move rest of line left by one, then blank out last character
    //

    for ( i = xPos; i < LASTCOL; i++ )
textbuf[yPos][i] = textbuf[yPos][i+1];
    textbuf[yPos][LASTCOL] = ' ';

} else {

    //
    // Move line left by two bytes, blank out last two bytes
    //

    for ( i = xPos; i < LASTCOL-1; i++ )
textbuf[yPos][i] = textbuf[yPos][i+2];
    textbuf[yPos][LASTCOL-1] = ' ';
    textbuf[yPos][LASTCOL]   = ' ';
}

//
// Repaint the entire line
//

hdc = GetDC( hWnd );
HideCaret( hWnd );
TextOut( hdc, 0, yPos*cyMetrics, textbuf[yPos], MAXCOL );
ReleaseDC( hWnd, hdc );
break;

    case VK_TAB:    // tab  -- tabs are column allignment not character
{
 int xTabMax = xPos + TABSTOP;
 int xPosPrev;

 do {
     xPosPrev = xPos;
     SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L );
 } while ( (xPos % TABSTOP) &&
   (xPos < xTabMax) &&
   (xPos != xPosPrev));

}
break;

    case VK_RETURN: // linefeed
yPos = min( yPos+1, LASTROW );
xPos = FIRSTCOL;
break;
    }

    ResetCaret( hWnd );
}

/************************************************************************
*
*   StoreChar - Stores one SBCS character into text buffer and advances
*               cursor
*
************************************************************************/

void StoreChar( HWND hWnd, UCHAR ch )
{
    int i;
    HDC hdc;

    //
    // If insert mode, move rest of line to the right by one
    //

    if ( fInsertMode ) {
for ( i = LASTCOL; i > xPos; i-- )
    textbuf[yPos][i] = textbuf[yPos][i-1];

//
// If the row ends on a lead byte, blank it out
// To do this we must first traverse the string
// starting from a known character boundry until
// we reach the last column. If the last column
// is a character boundry then the last character
// is either a single byte or a lead byte
//

for ( i = xPos+1; i < LASTCOL; ) {
    if ( IsDBCSLeadByte( textbuf[yPos][i] ) )
i++;
    i++;
}
if (i==LASTCOL)
   if ( IsDBCSLeadByte( textbuf[yPos][LASTCOL] ) )
       textbuf[yPos][LASTCOL] = ' ';

    } else {  // overtype mode

if ( IsDBCSLeadByte( textbuf[yPos][xPos] ) )

    //
    // Blank out trail byte
    //

    textbuf[yPos][xPos+1] = ' ';

    //
    // or shift line left on character and blank last column
    //
    // for ( i = xPos+1; i < LASTCOL; i++ )
    //     textbuf[yPos][i] = textbuf[yPos][i+1];
    // textbuf[yPos][LASTCOL] = ' ';

    }

    //
    // Store input character at current caret position
    //

    textbuf[yPos][xPos] = ch;

    //
    // Display input character.
    //

    hdc = GetDC( hWnd );
    HideCaret( hWnd );
    TextOut( hdc, xPos*cxMetrics, yPos*cyMetrics,
     &(textbuf[yPos][xPos]), MAXCOL-xPos );
    ShowCaret( hWnd );
    ReleaseDC( hWnd, hdc );

    SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L );
}

/************************************************************************
*
*   StoreDBCSChar - Stores one DBCS character into text buffer and
*                   advances cursor
*
************************************************************************/

void StoreDBCSChar( HWND hWnd, WORD ch )
{
    int i;
    HDC hdc;

    //
    // If there is no room for a DBCS character, discard it
    //

    if ( xPos == LASTCOL )
return;

    //
    // If insert mode, move rest of line to the right by two
    //

    if ( fInsertMode ) {
for ( i = LASTCOL; i > xPos+1; i-- )
    textbuf[yPos][i] = textbuf[yPos][i-2];

//
// If the row ends on a lead byte, blank it out
// To do this we must first traverse the string
// starting from a known charcter boundry until
// we reach the last column. If the last column
// is not a trail byte then it is a single byte
// or a lead byte
//

for ( i = xPos+2; i < LASTCOL; ) {
    if ( IsDBCSLeadByte( textbuf[yPos][i] ) )
i++;
    i++;
}
if (i==LASTCOL)
   if (IsDBCSLeadByte( textbuf[yPos][LASTCOL] ) )
textbuf[yPos][LASTCOL] = ' ';

    } else {  // overtype mode

if ( !IsDBCSLeadByte( textbuf[yPos][xPos] ) )

    //
    // Overtyping the current byte, plus the following byte,
    // which could be a lead byte.
    //

    if ( IsDBCSLeadByte( textbuf[yPos][xPos+1] ) )
textbuf[yPos][xPos+2] = ' ';
    }

    //
    // Store input character at current caret position
    //

    textbuf[yPos][xPos]   = LOBYTE(ch);     // lead byte
    textbuf[yPos][xPos+1] = HIBYTE(ch);     // trail byte

    //
    // Display input character.
    //

    hdc = GetDC( hWnd );
    HideCaret( hWnd );
    TextOut( hdc, xPos*cxMetrics, yPos*cyMetrics,
     &(textbuf[yPos][xPos]), MAXCOL-xPos );
    ShowCaret( hWnd );
    ReleaseDC( hWnd, hdc );

    SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L );
}

/************************************************************************
*
*   CharHandler - WM_CHAR handler
*
************************************************************************/

void CharHandler( HWND hWnd, WORD wParam )
{
    unsigned char ch = (unsigned char)wParam;

    //
    // Because DBCS characters are usually generated by IMEs (as two
    // PostMessages), if a lead byte comes in, the trail byte should
    // arrive very soon after.  We wait here for the trail byte and
    // store them into the text buffer together.

    if ( IsDBCSLeadByte( ch ) ) {

//
// Wait an arbitrary amount of time for the trail byte to
// arrive.  If it doesn't, then discard the lead byte.
//
// This could happen if the IME screwed up.  Or, more likely,
// the user generated the lead byte through ALT-numpad.
//

MSG msg;
int i = 10;

while (!PeekMessage((LPMSG)&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE)) {
    if ( --i == 0 )
return;
    Yield();
}

StoreDBCSChar( hWnd,  (WORD)(((unsigned)(msg.wParam)<<8) | (unsigned)ch ));

    } else {

switch( ch )
{
case '\r':
case '\t':
case '\b':

    //
    // Throw away.  Already handled at WM_KEYDOWN time.
    //

    break;

default:
    StoreChar( hWnd, ch );
    break;
}
    }
}

/************************************************************************
*
*   MouseHandler - WM_BUTTONDOWN handler
*
************************************************************************/

void MouseHandler( HWND hWnd, LONG lParam )
{

    if ( ( gImeUIData.ImeState & IME_IN_CHOSECAND ) ||
         ( gImeUIData.ImeState & IME_IN_COMPOSITION && !MoveCaret( hWnd ) ) )
return;

    HideCaret( hWnd );

    //
    // Calculate caret position based on fixed pitched font
    //

    yPos = MAKEPOINTS(lParam).y / cyMetrics;
    xPos = MAKEPOINTS(lParam).x / cxMetrics;

    //
    // Adjust caret position if click landed on a trail byte
    //

    if ( IsDBCSTrailByte( textbuf[yPos], &(textbuf[yPos][xPos]) ) )

//
// If click landed on the last quarter of the DBCS character,
// assume the user was aiming at the next character.
//

if ( (MAKEPOINTS(lParam).x - xPos * cxMetrics) > (cxMetrics / 2) )
    xPos++;
else
    xPos--;

    DestroyCaret();
    CreateCaret( hWnd,
 NULL,
 (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ?
   CaretWidth*2 : CaretWidth,
 cyMetrics );
    SetCaretPos( xPos * cxMetrics, yPos * cyMetrics );
    ShowCaret( hWnd );

}

/************************************************************************
*
*   InputChangeHandler - WM_INPUTLANGCHANGE handler
*
************************************************************************/

void InputChangeHandler( HWND hWnd )
{
    HIMC hIMC;

    //
    // If the old keyboard layout is IME, the ime ui data have to be free.
    //

    if (ImmIsIME(hCurKL))
    {
        //
        // If application prefers to use near caret provded by IME, or
        // IME provides special UI, then no need to clean UD data.
        //
        if ( gImeUIData.fdwProperty & IME_PROP_SPECIAL_UI )
            ;
        else if ( gImeUIData.fdwProperty & IME_PROP_AT_CARET )
            ImeUIClearData(hWnd);
        else
            ;
    }

    //
    // Set new keyboard layout.
    //
    hCurKL = GetKeyboardLayout(0L);

    //
    // Get new property.
    //

    gImeUIData.fdwProperty = ImmGetProperty( hCurKL, IGP_PROPERTY );

    // if this application set the candidate position, it need to set
    // it to default for the near caret IME

    if ( hIMC = ImmGetContext( hWnd ) ) {
        UINT i;

        for (i = 0; i < 4; i++) {
            CANDIDATEFORM CandForm;

            if ( gImeUIData.fdwProperty & IME_PROP_AT_CARET ) {
                CandForm.dwIndex = i;
                CandForm.dwStyle = CFS_CANDIDATEPOS;

#if 0           // This application do not want to set candidate window to
                // any position. Anyway, if an application need to set the
                // candiadet position, it should remove the if 0 code

                // the position you want to set
                CandForm.ptCurrentPos.x = ptAppWantPosition[i].x;
                CandForm.ptCurrentPos.y = ptAppWantPosition[i].y;

                ImmSetCandidateWindow( hIMC, &CandForm );
#endif
            } else {
                if ( !ImmGetCandidateWindow( hIMC, i, &CandForm ) ) {
                    continue;
                }

                if ( CandForm.dwStyle == CFS_DEFAULT ) {
                    continue;
                }

                CandForm.dwStyle = CFS_DEFAULT;

                ImmSetCandidateWindow( hIMC, &CandForm );
            }
        }

        ImmReleaseContext( hWnd, hIMC );
    }

    return;
}

/************************************************************************
*
*   SteWndProc - STE class window procedure
*
************************************************************************/

long WINAPI SteWndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
    int i;
    HDC hdc;
    PAINTSTRUCT ps;

    switch( msg ) {

    case WM_CREATE:
SteCreate( hWnd );
break;

    case WM_DESTROY:
PostQuitMessage(0);
break;

    case WM_CLOSE:
DestroyWindow( hWnd );
break;

    case WM_SETFOCUS:
CreateCaret( hWnd,
     NULL,
     (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ?
       CaretWidth*2 : CaretWidth,
     cyMetrics );
SetCaretPos( xPos * cxMetrics, yPos * cyMetrics );
ShowCaret( hWnd );
break;

    case WM_KILLFOCUS:
HideCaret( hWnd );
DestroyCaret();
break;

    case WM_IME_KEYDOWN:
    case WM_KEYDOWN:
VirtualKeyHandler( hWnd, wParam );
break;

    case WM_KEYUP:
break;

    case WM_CHAR:
CharHandler( hWnd, (WORD)wParam );
break;

    case WM_LBUTTONDOWN:
MouseHandler( hWnd, lParam );
break;

    case WM_MOVE:

ImeUIMoveCandWin( hWnd );
break;

    case WM_COMMAND:
SteCommand( hWnd, wParam, lParam );
break;

    case WM_PAINT:
InvalidateRect(hWnd,NULL,FALSE);  //for repaint allignment problem??
  // WinChi3.0
hdc = BeginPaint( hWnd, &ps );

//
// Refresh display from text buffer
//

for ( i = FIRSTROW; i <= LASTROW; i++ )
    TextOut( hdc, 0, i*cyMetrics, textbuf[i], MAXCOL );

EndPaint( hWnd, &ps );

RestoreImeUI( hWnd );

break;

    case WM_INPUTLANGCHANGE:

        InputChangeHandler( hWnd );

        goto call_defwinproc;

break;

    case WM_IME_SETCONTEXT:

        //
        // The application have to pass WM_IME_SETCONTEXT to DefWindowProc.
// When the application want to handle the IME at the timing of
        // focus changing, the application should use WM_SETFOCUS or
        // WM_KILLFOCUS.
        //

        if ( gImeUIData.fdwProperty & IME_PROP_SPECIAL_UI )
            goto call_defwinproc;
        else if ( gImeUIData.fdwProperty & IME_PROP_AT_CARET )
        {
    //
    // application wants to draw UI ny itself.
    //
            lParam &= ~(ISC_SHOWUICOMPOSITIONWINDOW | ISC_SHOWUIALLCANDIDATEWINDOW);
}
return DefWindowProc( hWnd, msg, wParam, lParam );

    case WM_IME_STARTCOMPOSITION:

//
        // CheckProperty is a macro, if IME already provides near caret or
// special UI then let IME handle this message.
        //
CheckProperty;
        ImeUIStartComposition( hWnd );

break;

    case WM_IME_COMPOSITION:

CheckProperty;
        ImeUIComposition( hWnd, wParam, lParam );
        break;

    case WM_IME_ENDCOMPOSITION:

CheckProperty;
        ImeUIEndComposition( hWnd );
        break;

    case WM_IME_COMPOSITIONFULL:

        //
        // Make sure the size for drawing the composition string.
        // Application should draw the composition string correctly.
        //
        break;

    case WM_IME_NOTIFY:

CheckProperty;
        if ( !ImeUINotify( hWnd, wParam, lParam ) )
        // This application does not handle all notification message.
        // So we pass those notification messages which are not hanlded
        // by this application to the DefWindowProc.
            goto call_defwinproc;
        break;

    case WM_IME_CONTROL:

        //
        // This message is not received by the application window.
        // But don't pass it to DefWindowProc().
        //
        break;

    default:
call_defwinproc:

return DefWindowProc( hWnd, msg, wParam, lParam );
    }

    return 0;
}


HALFIME.H

#define MAXROW          12
#define MAXCOL          60
#define FIRSTROW         0
#define FIRSTCOL         0
#define LASTROW         (MAXROW-1)
#define LASTCOL         (MAXCOL-1)
#define TABSTOP          4

#define ID_ICON         1
#define ID_MENU         1
#define IDS_CLASS       1
#define IDS_TITLE       2

#define IDC_CLEAR               1
#define IDC_ANSIFILL            2
#define IDC_DBCSFILL            3
#define IDC_DEFAULT_POS         4
#define IDC_NEARCARET_POS       5
#define IDC_PREFER_POS          6
#define IDC_OPENIME             7
#define IDC_CLOSEIME            8


MAIN.C

/************************************************************************
*
* Title:
*
*   STED.C - IME Half-aware Simple Text Editor (DBCS version)
*
* Purpose:
*
*   Sample program for DBCS programming and IME half-aware application.
*
* Synopsis:
*
*   This program is designed as a bare-bone example to demonstrate the
*   basic elements of DBCS-enabling, and how to design an IME half-aware
*   application.
*
*   The data structure is a fixed-size 2-dimensional byte array.  The
*   font is the fixed-pitch system font.  Rudimentary text editing
*   functions, such as the basic cursor movements, insertion, deletion,
*   have been implemented.
*
* DBCS-enabling notes:
*
*   This version of STE is DBCS-enabled with respect to character input,
*   caret shape and movement, and mouse clicking.  It should work on
*   any version of Far East Windows since 4.0.
*
*   As far as source code maintenance goes, there are generally two
*   approaches.
*
*   The first is to add DBCS enabling code under '#ifdef DBCS'.  The
*   advantage of this approach is that it keeps the DBCS enabling code
*   distinct from the SBCS, so it's easier to add them in, and also to
*   remove them later.  (For example, when you want to replace DBCS
*   enabling with Unicode enabling.)  The drawback is that because the
*   DBCS and the SBCS logic are not integrated, they can easily get out
*   of sync (as the SBCS code evolves.)
*
*   The second approach, which is adopted by this sample app, is to
*   integrate DBCS enabling with SBCS.  It takes longer to do, but
*   the resulting source is easier to maintain.  Since IsDBCSLeadByte
*   is at the heart of any DBCS-enabling logic, an additional speed up
*   for generating an SBCS-only version is to define that function as
*   FALSE, and let the compiler optimize the DBCS logic away.
*
* IME Half-Aware notes:
*
*   This version of STE is an IME half-aware application with the
*   capiabilities to open/close IME conversion engine and control
*   where the IME composition window will be located in. It should
*   work on any version of Far East Windows since 4.0.
*
*   This kind of application typically wants to control the behavior
*   of IME like opening/closing IME, setting where to show composition
*   window, setting where to show candidate lists and so on. It does
*   not display any user interface for IME.
*
* History:
*
*   17-Aug-1992     created
*   28-Sep-1992     added DBCS-enabling
*   30-Sep-1992     bug fixes
*   25-Mar-1994     added IME half-aware logics
*
************************************************************************/

#include <assert.h>
#include <windows.h>
#include "halfime.h"
#include <imm.h>

//
// Function prototype
//

void    ResetCaret( HWND );
void    SetIMECompFormPos();

/************************************************************************
*
* Global data
*
************************************************************************/

char szSteClass[10];                // window class name
char szSteTitle[40];                // window frame title
UINT  cxMetrics,                    // aver. character width
      cxOverTypeCaret,              // caret width for overtype mode
      cyMetrics;                    // character height
int xPos, yPos;                     // caret position
HFONT hfntFixed;                    // fixed-pitch font
HFONT hfntOld;                      // default font holder
BOOL fInsertMode;                   // insert/overtype mode flag
int CaretWidth;                     // insert/overtype mode caret width
BYTE textbuf[MAXROW][MAXCOL];       // text buffer
int DBCSFillChar;                   // 'Pattern/DBCS' fill character

UINT    CompWindowMode;             // Composition window mode.
COMPOSITIONFORM CompForm;           // Storage for composition window
    // structure.
DWORD fdwProperty;                  // the property of current active IME

/************************************************************************
*
*   SteRegister - standard class registration routine
*
************************************************************************/

int SteRegister( HANDLE hInstance )
{
    long WINAPI SteWndProc( HWND, UINT, UINT, LONG );

    PWNDCLASS pWndClass;

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

    pWndClass->hCursor       = LoadCursor( NULL, IDC_IBEAM );
    pWndClass->hIcon         = LoadIcon( hInstance, MAKEINTRESOURCE(ID_ICON));
    pWndClass->lpszMenuName  = MAKEINTRESOURCE(ID_MENU);
    pWndClass->hInstance     = hInstance;
    pWndClass->lpszClassName = (LPSTR)szSteClass;
    pWndClass->lpfnWndProc   = SteWndProc;
    pWndClass->hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    pWndClass->style         = CS_BYTEALIGNCLIENT | CS_CLASSDC;

    if ( !RegisterClass( (LPWNDCLASS)pWndClass ) )
return FALSE;

    LocalFree( (HANDLE)pWndClass );

    return TRUE;
}

/************************************************************************
*
*   WinMain
*
************************************************************************/

int PASCAL WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpszCmdLine,
    int nCmdShow )
{
    MSG msg;
    HWND hWnd;

    LoadString( hInstance, IDS_CLASS, szSteClass, 10 );
    LoadString( hInstance, IDS_TITLE, szSteTitle, 40 );

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

    if ( !(hWnd = CreateWindow( szSteClass, szSteTitle,
  WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, (HWND)NULL,
  (HMENU)NULL, hInstance, NULL)) )
return FALSE;

    //
    // Create window with just enough client space for the text buffer
    //

    SetWindowPos( hWnd, 0, 0, 0,
MAXCOL*cxMetrics + GetSystemMetrics(SM_CXBORDER)*2,
MAXROW*cyMetrics + GetSystemMetrics(SM_CYBORDER)*2
 + GetSystemMetrics(SM_CYCAPTION)
 + GetSystemMetrics(SM_CYMENU),
SWP_NOZORDER );

    ShowWindow( hWnd, nCmdShow );

    //
    // Any IME half-aware application should call TranslateMessage API to
    // translate messages. This is because Windows95 IME architecture
    // will call ImmToAsciiEx API, if necessary, to make IME conversion
    // engine work, and generate some IME messages during TranslateMessage
    // session. Otherwise, IME will not work.
    //

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

    return msg.wParam;
}

//**********************************************************************
//
// SteIMEOpenClose()
//
// This routines calls IMM API to open or close IME.
//
//**********************************************************************

void SteImeOpenClose( HWND hWnd, BOOL fFlag )
{
HIMC            hIMC;           // Input context handle.

//
// If fFlag is true then open IME; otherwise close it.
//

        if ( !( hIMC = ImmGetContext( hWnd ) ) )
return;

//
// When an application wants to open/close IME for some
// reasons, it should call TMMSetOpenStatus() to do that.
//

        ImmSetOpenStatus( hIMC, fFlag );

        ImmReleaseContext( hWnd, hIMC );

}

//**********************************************************************
//
// SteIMECompWindowPos()
//
// This routines inform IME where to display composition window.
// Here we control IME composition window with the following cases:
// 1: DEFAULT:   Display composition window in IME default position.
// 2: NEARCARET: Display composition window in near caret position.
// 3: PreFer:    Display composition window in application prefer position.
//
//**********************************************************************

void SteIMECompWindowPos( HWND hWnd, UINT cmd )
{
RECT            rect;           // Storage for client rect.
        HIMChImc;

//
// Set current composition mode.
//

CompWindowMode = cmd;

switch( cmd )
{
case IDC_DEFAULT_POS:

//
// Put composition window in default position
// being deterimined by IME UI. Applications
// have to set composition form style with
// CFS_DEFAULT to imform IME UI.
//

CompForm.dwStyle = CFS_DEFAULT;
break;

case IDC_NEARCARET_POS:

//
// Put composition window in near caret
// position. If IME UI receives this control
// message, it will put the composition window
                        // near to the position deesignated by ptCurrentPos.
// So for this operation, applications have to set
// composition form style with CFS_POINT and store
                        // caret position in ptCurrentPos.
//

CompForm.dwStyle = CFS_POINT;

                        GetCaretPos( (LPPOINT) &CompForm.ptCurrentPos );

break;

case IDC_PREFER_POS:

// Put composition window in application
// prefer position. Applications have to set
// composition form sytle with  CFS_POINT |
// CFS_FORCE_POSITION and store the prefer
                        // position in ptCurrentPos.
//
// The difference between CFS_POINT and CFS_POINT
// with CFS_FORCE_POSITION is if setting
// CFS_FORCE_POSITION style, IME will actually
// display its composition window in the position
// specified by ptCurrent without any adjustment;
                        // otherwise IME will shPosow its composition window
// by adjusting the position specified by
                        // ptCurrentPos.
//

CompForm.dwStyle = CFS_POINT | CFS_FORCE_POSITION;

GetClientRect( hWnd, (LPRECT) &rect);

                        CompForm.ptCurrentPos.x = 0;
                        CompForm.ptCurrentPos.y = rect.bottom;

break;
}

        hImc = ImmGetContext( hWnd );
        ImmSetCompositionWindow( hImc, (COMPOSITIONFORM FAR*)&CompForm );
        ImmReleaseContext( hWnd, hImc );

}

//**********************************************************************
//
// ResetIMECompWin()
//
// This routine informs IME the fact that caret position has been changed.
//
//**********************************************************************

void ResetIMECompWin( HWND hWnd )
{
if ( CompWindowMode == IDC_NEARCARET_POS ||
     CompWindowMode == IDC_PREFER_POS )
{
HIMC hIMC = ImmGetContext(hWnd);
if ( hIMC )
ImmSetCompositionWindow(hIMC,&CompForm);
ImmReleaseContext( hWnd , hIMC);
}
}

//**********************************************************************
//
// SetIMECompFormPos()
//
//**********************************************************************

void SetIMECompFormPos( HWND hWnd )
{

    //
    // If current composition form mode is near caret operation,
    // application should inform IME UI the caret position has been
    // changed. IME UI will make decision whether it has to adjust
    // composition window position.
    //
    //

    if ( CompWindowMode == IDC_NEARCARET_POS )
    {
HIMC hIMC = ImmGetContext(hWnd);
POINT   point;

GetCaretPos( &point );

CompForm.dwStyle = CFS_POINT;

        CompForm.ptCurrentPos.x = (long) point.x;
        CompForm.ptCurrentPos.y = (long) point.y;

if ( hIMC )
ImmSetCompositionWindow(hIMC,&CompForm);
ImmReleaseContext( hWnd , hIMC);
    }

}

/************************************************************************
*
*   SteCreate - WM_CREATE message handler
*
************************************************************************/

void SteCreate( HWND hWnd )
{
    HDC hdc;
    TEXTMETRIC tm;
    LOGFONT    lFont;
    HFONT      hFont;

    //
    // Note that this window has a class DC
    //

    hdc = GetDC( hWnd );

    //
    // Select fixed pitch system font and get its text metrics
    //

    hfntFixed = GetStockObject( SYSTEM_FIXED_FONT );
    hfntOld = SelectObject( hdc, hfntFixed );
    GetTextMetrics( hdc, &tm );
    ReleaseDC( hWnd, hdc );
    cxMetrics = tm.tmAveCharWidth;
    cyMetrics = tm.tmHeight;

    //
    // Determine the version of DBCS Windows from system font charset ID.
    // Then hardcode a DBCS character value for the 'Pattern/DBCS' command.
    // The value is the Han character for 'door' or 'gate', which is
    // left-right symmetrical.
    //

    switch( tm.tmCharSet )
    {
    case SHIFTJIS_CHARSET:
DBCSFillChar = 0x96e5;
break;

    case HANGEUL_CHARSET:
DBCSFillChar = 0xdaa6;
break;

    case CHINESEBIG5_CHARSET:
DBCSFillChar = 0xaaf9;
break;

    default:
DBCSFillChar = 0x7071;  // 'pq'
break;
    }

    //
    // Initialize caret width.  Fat in INSERT mode, skinny in OVERTYPE mode.
    //

    fInsertMode = FALSE;
    CaretWidth = cxOverTypeCaret = GetSystemMetrics( SM_CXBORDER );

    //
    // Sets the logical font to be used to display characters in the
    // composition window. Especially for at caret or near caret operation,
    // application should set composition font.
    //

    if ( ( hFont = (HFONT)SendMessage( hWnd, WM_GETFONT, 0, 0L ) ) != NULL )
    {
        if ( GetObject( hFont, sizeof(LOGFONT), (LPVOID)&lFont ) )
        {
            HIMChImc;

            if ( (  hImc = ImmGetContext( hWnd ) ) )
    {
        ImmSetCompositionFont( hImc, &lFont );
ImmReleaseContext( hWnd, hImc );
    }
}
    }

    PostMessage( hWnd, WM_COMMAND, IDC_CLEAR, 0L );

    fdwProperty = ImmGetProperty( GetKeyboardLayout(0), IGP_PROPERTY );

}

/************************************************************************
*
*   ResetCaret - Reset caret shape to match input mode (overtype/insert)
*
************************************************************************/

void ResetCaret( HWND hWnd )
{

    HideCaret( hWnd );
    DestroyCaret();
    CreateCaret( hWnd,
 NULL,
 (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ?
   CaretWidth*2 : CaretWidth,
 cyMetrics );
    SetCaretPos( xPos * cxMetrics, yPos * cyMetrics );
    ShowCaret( hWnd );

    SetIMECompFormPos( hWnd );

}

/************************************************************************
*
*   SteCommand - WM_COMMAND handler
*
************************************************************************/

void SteCommand( HWND hWnd, UINT cmd )
{
    switch( cmd )
    {
    case IDC_CLEAR:

//
// Blank out text buffer.  Return caret to home position
//

for ( yPos = FIRSTROW; yPos <= LASTROW; yPos++ )
    for ( xPos = FIRSTCOL; xPos <= LASTCOL; xPos++ )
textbuf[yPos][xPos] = ' ';
break;

    case IDC_ANSIFILL:
    case IDC_DBCSFILL:

//
// Fill text buffer with ANSI or DBCS pattern
//

for ( yPos = FIRSTROW; yPos <= LASTROW; yPos++ )
    for ( xPos = FIRSTCOL; xPos <= LASTCOL; xPos++ )
if ( cmd == IDC_ANSIFILL )
    textbuf[yPos][xPos] = 'a';
else {
    textbuf[yPos][xPos]   = HIBYTE(DBCSFillChar);
    textbuf[yPos][++xPos] = LOBYTE(DBCSFillChar);
}
break;

    //
    // The following messages are to control IME.
    //

    case IDC_OPENIME:

SteImeOpenClose( hWnd, TRUE );
goto RETURN;

    case IDC_CLOSEIME:

SteImeOpenClose( hWnd, FALSE );
goto RETURN;

    case IDC_DEFAULT_POS:
    case IDC_NEARCARET_POS:
    case IDC_PREFER_POS:

SteIMECompWindowPos( hWnd, cmd );
goto RETURN;

    }

    yPos = FIRSTROW;
    xPos = FIRSTCOL;

    InvalidateRect( hWnd, (LPRECT)NULL, TRUE );
    ResetCaret(hWnd);

RETURN:

    return;
}

/************************************************************************
*
*   IsDBCSTrailByte - returns TRUE if the given byte is a DBCS trail byte
*
*                     The algorithm searchs backward in the string, to some
*                     known character boundary, counting consecutive bytes
*                     in the lead byte range. An odd number indicates the
*                     current byte is part of a two byte character code.
*
*   INPUT: PCHAR  - pointer to a preceding known character boundary.
*          PCHAR  - pointer to the character to test.
*
*   OUTPUT:BOOL   - indicating truth of p==trailbyte.
*
************************************************************************/

BOOL IsDBCSTrailByte( char *base, char *p )
{
    int lbc = 0;    // lead byte count

    assert(base <= p);

    while ( p > base ) {
if ( !IsDBCSLeadByte(*(--p)) )
    break;
lbc++;
    }

    return (lbc & 1);
}

/************************************************************************
*
*   VirtualKeyHandler - WM_KEYDOWN handler
*
*
*   INPUT:  HWND - handle to the window for repainting output.
*           UINT - virtual key code.
*
************************************************************************/

void VirtualKeyHandler( HWND hWnd, UINT wParam )
{
    int i;
    HDC hdc;
    static int delta = 1;

    switch( wParam )
    {
    case VK_HOME:   // beginning of line
xPos = FIRSTCOL;
break;

    case VK_END:    // end of line
xPos = LASTCOL;
goto check_for_trailbyte;

    case VK_RIGHT:
if ( IsDBCSLeadByte( textbuf[yPos][xPos] ) ){
    if (xPos==LASTCOL - 1) break;  //last character don't move
    xPos += 2;                     //skip 2 for DB Character
}
else
    xPos = min( xPos+1, LASTCOL );
break;

    case VK_LEFT:
xPos = max( xPos-1, FIRSTCOL );

check_for_trailbyte:

if ( IsDBCSTrailByte( textbuf[yPos], &(textbuf[yPos][xPos]) ) )
    xPos--;
break;

    case VK_UP:
yPos = max( yPos-1, FIRSTROW );
goto Virtical_Check_Trail;

    case VK_DOWN:
yPos = min( yPos+1, LASTROW );

Virtical_Check_Trail:

if ( IsDBCSTrailByte( textbuf[yPos], &(textbuf[yPos][xPos]) ) ){
   if (xPos<LASTCOL){
      xPos+=delta;
      delta *= -1;
   }
   else
      xPos--;
}
break;

    case VK_INSERT:

//
// Change caret shape to indicate insert/overtype mode
//

fInsertMode = !fInsertMode;
CaretWidth = fInsertMode ? cxMetrics : cxOverTypeCaret;
break;

    case VK_BACK:   // backspace
if ( xPos > FIRSTCOL ) {
    xPos--;

    //
    // DB Character so backup one more to allign on boundary
    //
    if ( IsDBCSTrailByte( textbuf[yPos], &(textbuf[yPos][xPos]) ) )
xPos--;
//
// Fall Through to VK_DELETE to adjust row
//
}
else     //FIRST COLUMN  don't backup -- this would change for wrapping
   break;

    case VK_DELETE:

if ( !IsDBCSLeadByte( textbuf[yPos][xPos] ) ) {

    //
    // Move rest of line left by one, then blank out last character
    //

    for ( i = xPos; i < LASTCOL; i++ )
textbuf[yPos][i] = textbuf[yPos][i+1];
    textbuf[yPos][LASTCOL] = ' ';

} else {

    //
    // Move line left by two bytes, blank out last two bytes
    //

    for ( i = xPos; i < LASTCOL-1; i++ )
textbuf[yPos][i] = textbuf[yPos][i+2];
    textbuf[yPos][LASTCOL-1] = ' ';
    textbuf[yPos][LASTCOL]   = ' ';
}

//
// Repaint the entire line
//

hdc = GetDC( hWnd );
HideCaret( hWnd );
TextOut( hdc, 0, yPos*cyMetrics, textbuf[yPos], MAXCOL );
ReleaseDC( hWnd, hdc );
break;

    case VK_TAB:    // tab  -- tabs are column allignment not character
{
 int xTabMax = xPos + TABSTOP;
 int xPosPrev;

 do {
     xPosPrev = xPos;
     SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L );
 } while ( (xPos % TABSTOP) &&
   (xPos < xTabMax) &&
   (xPos != xPosPrev));

}
break;

    case VK_RETURN: // linefeed
yPos = min( yPos+1, LASTROW );
xPos = FIRSTCOL;
break;
    }

    ResetCaret( hWnd );
}

/************************************************************************
*
*   StoreChar - Stores one SBCS character into text buffer and advances
*               cursor
*
************************************************************************/

void StoreChar( HWND hWnd, BYTE ch )
{
    int i;
    HDC hdc;

    //
    // If insert mode, move rest of line to the right by one
    //

    if ( fInsertMode ) {
for ( i = LASTCOL; i > xPos; i-- )
    textbuf[yPos][i] = textbuf[yPos][i-1];

//
// If the row ends on a lead byte, blank it out
// To do this we must first traverse the string
// starting from a known character boundry until
// we reach the last column. If the last column
// is a character boundry then the last character
// is either a single byte or a lead byte
//

for ( i = xPos+1; i < LASTCOL; ) {
    if ( IsDBCSLeadByte( textbuf[yPos][i] ) )
i++;
    i++;
}
if (i==LASTCOL)
   if ( IsDBCSLeadByte( textbuf[yPos][LASTCOL] ) )
       textbuf[yPos][LASTCOL] = ' ';

    } else {  // overtype mode

if ( IsDBCSLeadByte( textbuf[yPos][xPos] ) )

    //
    // Blank out trail byte
    //

    textbuf[yPos][xPos+1] = ' ';

    //
    // or shift line left on character and blank last column
    //
    // for ( i = xPos+1; i < LASTCOL; i++ )
    //     textbuf[yPos][i] = textbuf[yPos][i+1];
    // textbuf[yPos][LASTCOL] = ' ';

    }

    //
    // Store input character at current caret position
    //

    textbuf[yPos][xPos] = ch;

    //
    // Display input character.
    //

    hdc = GetDC( hWnd );
    HideCaret( hWnd );
    TextOut( hdc, xPos*cxMetrics, yPos*cyMetrics,
     &(textbuf[yPos][xPos]), MAXCOL-xPos );
    ShowCaret( hWnd );
    ReleaseDC( hWnd, hdc );

    SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L );
}

/************************************************************************
*
*   StoreDBCSChar - Stores one DBCS character into text buffer and
*                   advances cursor
*
************************************************************************/

void StoreDBCSChar( HWND hWnd, WORD ch )
{
    int i;
    HDC hdc;

    //
    // If there is no room for a DBCS character, discard it
    //

    if ( xPos == LASTCOL )
return;

    //
    // If insert mode, move rest of line to the right by two
    //

    if ( fInsertMode ) {
for ( i = LASTCOL; i > xPos+1; i-- )
    textbuf[yPos][i] = textbuf[yPos][i-2];

//
// If the row ends on a lead byte, blank it out
// To do this we must first traverse the string
// starting from a known charcter boundry until
// we reach the last column. If the last column
// is not a trail byte then it is a single byte
// or a lead byte
//

for ( i = xPos+2; i < LASTCOL; ) {
    if ( IsDBCSLeadByte( textbuf[yPos][i] ) )
i++;
    i++;
}
if (i==LASTCOL)
   if (IsDBCSLeadByte( textbuf[yPos][LASTCOL] ) )
textbuf[yPos][LASTCOL] = ' ';

    } else {  // overtype mode

if ( !IsDBCSLeadByte( textbuf[yPos][xPos] ) )

    //
    // Overtyping the current byte, plus the following byte,
    // which could be a lead byte.
    //

    if ( IsDBCSLeadByte( textbuf[yPos][xPos+1] ) )
textbuf[yPos][xPos+2] = ' ';
    }

    //
    // Store input character at current caret position
    //

    textbuf[yPos][xPos]   = LOBYTE(ch);     // lead byte
    textbuf[yPos][xPos+1] = HIBYTE(ch);     // trail byte

    //
    // Display input character.
    //

    hdc = GetDC( hWnd );
    HideCaret( hWnd );
    TextOut( hdc, xPos*cxMetrics, yPos*cyMetrics,
     &(textbuf[yPos][xPos]), MAXCOL-xPos );
    ShowCaret( hWnd );
    ReleaseDC( hWnd, hdc );

    SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L );
}

/************************************************************************
*
*   CharHandler - WM_CHAR handler
*
************************************************************************/

void CharHandler( HWND hWnd, UINT wParam )
{
    unsigned char ch = (unsigned char)wParam;

    //
    // Because DBCS characters are usually generated by IMEs (as two
    // PostMessages), if a lead byte comes in, the trail byte should
    // arrive very soon after.  We wait here for the trail byte and
    // store them into the text buffer together.

    if ( IsDBCSLeadByte( ch ) ) {

//
// Wait an arbitrary amount of time for the trail byte to
// arrive.  If it doesn't, then discard the lead byte.
//
// This could happen if the IME screwed up.  Or, more likely,
// the user generated the lead byte through ALT-numpad.
//

MSG msg;
int i = 10;

while (!PeekMessage((LPMSG)&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE)) {
    if ( --i == 0 )
return;
    Yield();
}

StoreDBCSChar( hWnd, (WORD)(((unsigned)(msg.wParam)<<8) | (unsigned)ch ));

    } else {

switch( ch )
{
case '\r':
case '\t':
case '\b':

    //
    // Throw away.  Already handled at WM_KEYDOWN time.
    //

    break;

default:
    StoreChar( hWnd, ch );
    break;
}
    }
}

/************************************************************************
*
*   MouseHandler - WM_BUTTONDOWN handler
*
************************************************************************/

void MouseHandler( HWND hWnd, LONG lParam )
{
    HideCaret( hWnd );

    //
    // Calculate caret position based on fixed pitched font
    //

    yPos = MAKEPOINTS(lParam).y / cyMetrics;
    xPos = MAKEPOINTS(lParam).x / cxMetrics;

    //
    // Adjust caret position if click landed on a trail byte
    //

    if ( IsDBCSTrailByte( textbuf[yPos], &(textbuf[yPos][xPos]) ) )

//
// If click landed on the last quarter of the DBCS character,
// assume the user was aiming at the next character.
//

if ( (MAKEPOINTS(lParam).x - xPos * cxMetrics) > (cxMetrics / 2) )
    xPos++;
else
    xPos--;

    DestroyCaret();
    CreateCaret( hWnd,
 NULL,
 (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ?
   CaretWidth*2 : CaretWidth,
 cyMetrics );
    SetCaretPos( xPos * cxMetrics, yPos * cyMetrics );
    ShowCaret( hWnd );

    SetIMECompFormPos( hWnd );

}

/************************************************************************
*
*   InputChangeHandler - WM_INPUTLANGCHANGE handler
*
************************************************************************/

void InputChangeHandler( HWND hWnd )
{
    HIMC hIMC;

    fdwProperty = ImmGetProperty( GetKeyboardLayout(0), IGP_PROPERTY );

    // if this application set the candidate position, it need to set
    // it to default for the near caret IME

    if ( hIMC = ImmGetContext( hWnd ) ) {
        UINT i;

        for (i = 0; i < 4; i++) {
            CANDIDATEFORM CandForm;

            if ( fdwProperty & IME_PROP_AT_CARET ) {
                CandForm.dwIndex = i;
                CandForm.dwStyle = CFS_CANDIDATEPOS;

#if 0           // This application do not want to set candidate window to
                // any position. Anyway, if an application need to set the
                // candiadet position, it should remove the if 0 code

                // the position you want to set
                CandForm.ptCurrentPos.x = ptAppWantPosition[i].x;
                CandForm.ptCurrentPos.y = ptAppWantPosition[i].y;

                ImmSetCandidateWindow( hIMC, &CandForm );
#endif
            } else {
                if ( !ImmGetCandidateWindow( hIMC, i, &CandForm ) ) {
                    continue;
                }

                if ( CandForm.dwStyle == CFS_DEFAULT ) {
                    continue;
                }

                CandForm.dwStyle = CFS_DEFAULT;

                ImmSetCandidateWindow( hIMC, &CandForm );
            }
        }

        ImmReleaseContext( hWnd, hIMC );
    }

    return;
}

/************************************************************************
*
*   SteWndProc - STE class window procedure
*
************************************************************************/

long WINAPI SteWndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
    int i;
    HDC hdc;
    PAINTSTRUCT ps;

    switch( msg ) {

    case WM_CREATE:
SteCreate( hWnd );
break;

    case WM_MOVE:

ResetIMECompWin( hWnd );
break;
    case WM_DESTROY:
PostQuitMessage(0);
break;

    case WM_CLOSE:
DestroyWindow( hWnd );
break;

    case WM_SETFOCUS:
CreateCaret( hWnd,
     NULL,
     (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ?
       CaretWidth*2 : CaretWidth,
     cyMetrics );
SetCaretPos( xPos * cxMetrics, yPos * cyMetrics );
ShowCaret( hWnd );
break;

    case WM_KILLFOCUS:
HideCaret( hWnd );
DestroyCaret();
break;

    case WM_KEYDOWN:
VirtualKeyHandler( hWnd, wParam );
break;

    case WM_CHAR:
CharHandler( hWnd, wParam );
break;

    case WM_LBUTTONDOWN:
MouseHandler( hWnd, lParam );
break;

    case WM_COMMAND:
SteCommand( hWnd, wParam );
break;

    case WM_PAINT:
InvalidateRect(hWnd,NULL,FALSE);  //for repaint allignment problem??
  // WinChi3.0
hdc = BeginPaint( hWnd, &ps );

//
// Refresh display from text buffer
//

for ( i = FIRSTROW; i <= LASTROW; i++ )
    TextOut( hdc, 0, i*cyMetrics, textbuf[i], MAXCOL );

EndPaint( hWnd, &ps );
break;

    case WM_INPUTLANGCHANGE:
        InputChangeHandler( hWnd );
return DefWindowProc( hWnd, msg, wParam, lParam );

    default:

return DefWindowProc( hWnd, msg, wParam, lParam );
    }

    return 0;
}


COMP.C

/**********************************************************************/
/*                                                                    */
/*      COMP.C                                                        */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"

#define DEBUG 1

LRESULT HandleStartComposition(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    COMPOSITIONFORM cpf;
    HIMC hIMC;

#ifdef DEBUG
    OutputDebugString("WM_STARTCOMPOSITIONSTRING!!!\r\n");
#endif

    if (fdwProperty & IME_PROP_SPECIAL_UI)
    {
        // Normally, we need to set the composition window
        // position to caret position for a special UI IME
    }
    else if (fdwProperty & IME_PROP_AT_CARET)
    {
        // If an application show composition string by itself, we do not
        // need to set the position of composition window for an at caret
        // IME.

        return 1;
    }
    else
    {
        // Normally, we need to set the composition window
        // position to caret position for a near caret IME
    }

    hIMC = ImmGetContext(hWnd);

    if (!hIMC)
    {
        return 1;
    }

    cpf.dwStyle = CFS_POINT;
    cpf.ptCurrentPos.x = ptImeUIPos.x;
    cpf.ptCurrentPos.y = ptImeUIPos.y;

    ImmSetCompositionWindow(hIMC,&cpf);

    ImmReleaseContext(hWnd,hIMC);

    return 1;
}

LRESULT HandleEndComposition(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
#ifdef DEBUG
    OutputDebugString("WM_ENDCOMPOSITIONSTRING!!!\r\n");
#endif
    dwCompStrLen      = 0;
    dwCompAttrLen     = 0;
    dwCompClsLen      = 0;
    dwCompReadStrLen  = 0;
    dwCompReadAttrLen = 0;
    dwCompReadClsLen  = 0;
    dwResultStrLen      = 0;
    dwResultClsLen      = 0;
    dwResultReadStrLen  = 0;
    dwResultReadClsLen  = 0;
    InvalidateRect(hWnd,NULL,TRUE);

    return 1;
}

void MakePaintString(HWND hWnd, LPMYSTR lpStr,DWORD dwStrLen,LPDWORD lpCls,DWORD dwClsLen,LPMYSTR lpPaintStr)
{
    LPMYSTR lpPaintStart = lpPaintStr;

    lpPaintStr += Mylstrlen(lpPaintStr);

    if (dwStrLen)
    {
        if (dwClsLen)
        {
            lpCls[127] = 0;

            while (*(lpCls+1) && *lpCls < dwStrLen)
            {
                DWORD dwTextLen = *(lpCls+1) - *lpCls;
                LPMYSTR lpT = lpStr + *lpCls;

                memcpy(lpPaintStr,lpT,dwTextLen * sizeof(MYCHAR));
                lpPaintStr += dwTextLen;
                *lpPaintStr = MYTEXT(',');
                lpPaintStr++;
                lpCls++;
            }
            *lpPaintStr = MYTEXT('\0');
        }
        else
        {
            Mylstrcpy(lpPaintStr,lpStr);
            Mylstrcat(lpPaintStr,MYTEXT(","));
        }
    }

}

LRESULT HandleComposition(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    HIMC hIMC;
    BOOL fRedraw = FALSE;

    hIMC = ImmGetContext(hWnd);

    if (lParam & GCS_COMPSTR)
    {
        dwCompStrLen = MyImmGetCompositionString(hIMC,GCS_COMPSTR,szCompStr,sizeof(szCompStr));
        dwCompStrLen /= sizeof(MYCHAR);
        szCompStr[dwCompStrLen] = MYTEXT('\0');
        fRedraw = TRUE;
    }
    else
    {
        dwCompStrLen = 0;
        szCompStr[0] = MYTEXT('\0');
    }

    if (lParam & GCS_COMPATTR)
    {
        dwCompAttrLen = MyImmGetCompositionString(hIMC,GCS_COMPATTR,bCompAttr,sizeof(bCompAttr));
        fRedraw = TRUE;
    }
    else
    {
        dwCompAttrLen = 0;
        bCompAttr[0] = 0;
    }

    if (lParam & GCS_COMPCLAUSE)
    {
        dwCompClsLen = MyImmGetCompositionString(hIMC,GCS_COMPCLAUSE,dwCompCls,sizeof(dwCompCls));
        fRedraw = TRUE;
    }
    else
    {
        dwCompClsLen = 0;
        dwCompCls[0] = 0;
        dwCompCls[1] = 0;
    }

    if (lParam & GCS_COMPREADSTR)
    {
        dwCompReadStrLen = MyImmGetCompositionString(hIMC,GCS_COMPREADSTR,szCompReadStr,sizeof(szCompReadStr));
        dwCompReadStrLen /= sizeof(MYCHAR);
        szCompReadStr[dwCompReadStrLen] = MYTEXT('\0');
        fRedraw = TRUE;
    }
    else
    {
        dwCompReadStrLen = 0;
        szCompReadStr[0] = MYTEXT('\0');
    }

    if (lParam & GCS_COMPREADATTR)
    {
        dwCompReadAttrLen = MyImmGetCompositionString(hIMC,GCS_COMPREADATTR,bCompReadAttr,sizeof(bCompReadAttr));
        fRedraw = TRUE;
    }
    else
    {
        dwCompReadAttrLen = 0;
        bCompReadAttr[0] = 0;
    }

    if (lParam & GCS_COMPREADCLAUSE)
    {
        dwCompReadClsLen = MyImmGetCompositionString(hIMC,GCS_COMPREADCLAUSE,dwCompReadCls,sizeof(dwCompReadCls));
        fRedraw = TRUE;
    }
    else
    {
        dwCompReadClsLen = 0;
        dwCompReadCls[0] = 0;
        dwCompReadCls[1] = 0;
    }

    if (lParam & GCS_RESULTSTR)
    {
        RECT rc;
        HDC hIC;
        SIZE sz0,sz1;
        HFONT hOldFont;

        if (lParam & GCS_RESULTCLAUSE)
        {
            dwResultClsLen = MyImmGetCompositionString(hIMC,GCS_RESULTCLAUSE,dwResultCls,sizeof(dwResultCls));
        }
        else
        {
            dwResultClsLen = 0;
            dwResultCls[0] = 0;
            dwResultCls[1] = 0;
        }

        dwResultStrLen = MyImmGetCompositionString(hIMC,GCS_RESULTSTR,szResultStr,sizeof(szResultStr));
        dwResultStrLen /= sizeof(MYCHAR);
        szResultStr[dwResultStrLen] = MYTEXT('\0');

        // szPaintResult may overflow..
        GetClientRect(hWnd,&rc);
        hIC = CreateIC("DISPLAY", NULL, NULL, NULL);
        hOldFont = SelectObject(hIC,hFont);
#ifdef USEWAPI
        GetTextExtentPointW(hIC,szPaintResult,Mylstrlen(szPaintResult),&sz0);
        GetTextExtentPointW(hIC,szResultStr,Mylstrlen(szResultStr),&sz1);
#else
        GetTextExtentPoint(hIC,szPaintResult,Mylstrlen(szPaintResult),&sz0);
        GetTextExtentPoint(hIC,szResultStr,Mylstrlen(szResultStr),&sz1);
#endif
        if (sz0.cx + sz1.cx >= rc.right)
        {
            szPaintResult[0] = MYTEXT('\0');
            szPaintResultRead[0] = MYTEXT('\0');
        }
        SelectObject(hIC,hOldFont);
        DeleteDC(hIC);

        MakePaintString(hWnd,szResultStr,dwResultStrLen,dwResultCls,dwResultClsLen,szPaintResult);

        fRedraw = TRUE;
    }
    else
    {
        dwResultStrLen = 0;
        szResultStr[0] = MYTEXT('\0');
        dwResultClsLen = 0;
        dwResultCls[0] = 0;
        dwResultCls[1] = 0;
    }

    if (lParam & GCS_RESULTREADSTR)
    {
        if (lParam & GCS_RESULTREADCLAUSE)
        {
            dwResultReadClsLen = MyImmGetCompositionString(hIMC,GCS_RESULTREADCLAUSE,dwResultReadCls,sizeof(dwResultReadCls));
            fRedraw = TRUE;
        }
        else
        {
            dwResultReadClsLen = 0;
            dwResultReadCls[0] = 0;
            dwResultReadCls[1] = 0;
        }
        dwResultReadStrLen = MyImmGetCompositionString(hIMC,GCS_RESULTREADSTR,szResultReadStr,sizeof(szResultReadStr));
        dwResultReadStrLen /= sizeof(MYCHAR);
        szResultReadStr[dwResultReadStrLen] = MYTEXT('\0');
        MakePaintString(hWnd,szResultReadStr,dwResultReadStrLen,dwResultReadCls,dwResultReadClsLen,szPaintResultRead);
        fRedraw = TRUE;
    }
    else
    {
        dwResultReadStrLen = 0;
        szResultReadStr[0] = MYTEXT('\0');
        dwResultReadClsLen = 0;
        dwResultReadCls[0] = 0;
        dwResultReadCls[1] = 0;
    }

    if (fRedraw)
    {
        InvalidateRect(hWnd,NULL,TRUE);
        UpdateWindow(hWnd);
    }
    return 1;
}

LRESULT HandleChar(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    RECT rc;
    HDC hIC;
    SIZE sz0,sz1;
    HFONT hOldFont;
    LPMYSTR lp;
    // is the previous received char is a DBCS lead byte char ?
    static BOOL fIsPrevLeadByte = FALSE;

    GetClientRect(hWnd,&rc);
    hIC = CreateIC("DISPLAY", NULL, NULL, NULL);
    hOldFont = SelectObject(hIC,hFont);
#ifdef USEWAPI
    GetTextExtentPointW(hIC,szPaintResult,Mylstrlen(szPaintResult),&sz0);
    GetTextExtentPointW(hIC,szResultStr,Mylstrlen(szResultStr),&sz1);
#else
    GetTextExtentPoint(hIC,szPaintResult,Mylstrlen(szPaintResult),&sz0);
    GetTextExtentPoint(hIC,szResultStr,Mylstrlen(szResultStr),&sz1);
#endif
    if (sz0.cx + sz1.cx >= rc.right)
    {
        szPaintResult[0] = MYTEXT('\0');
        szPaintResultRead[0] = MYTEXT('\0');
    }
    SelectObject(hIC,hOldFont);
    DeleteDC(hIC);

    lp = szPaintResult + Mylstrlen(szPaintResult);

#ifndef USEWAPI
    if (fIsPrevLeadByte) {
        // remove , and append second byte for showing DBCS char
        if (*(lp - 1) == ',') {
            lp--;
        }
    }
#endif

    // append second byte
    *lp++ = (MYCHAR)(BYTE)wParam;
    *lp++ = MYTEXT(',');
    *lp++ = MYTEXT('\0');

    lp = szPaintResultRead + Mylstrlen(szPaintResultRead);

#ifndef USEWAPI
    if (fIsPrevLeadByte) {
        // remove , and append second byte for showing DBCS char
        if (*(lp - 1) == ',') {
            lp--;
        }

        fIsPrevLeadByte = FALSE;
    } else {
        fIsPrevLeadByte = IsDBCSLeadByte((BYTE)wParam);
    }
#endif

    *lp++ = (BYTE)wParam;
    *lp++ = MYTEXT(',');
    *lp++ = MYTEXT('\0');

    InvalidateRect(hWnd,NULL,TRUE);
    UpdateWindow(hWnd);

    return 1;
}

LRESULT HandleNotify(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    HIMC hIMC;
    BOOL fOpen;
    DWORD dwConvMode,dwSentMode;

    switch (wParam)
    {
        case IMN_OPENSTATUSWINDOW:
        case IMN_CLOSESTATUSWINDOW:
            break;

        case IMN_SETOPENSTATUS:
            SetStatusItems(hWnd);

            hIMC = ImmGetContext(hWnd);
            fOpen = ImmGetOpenStatus(hIMC);
            UpdateShowOpenStatusButton(fOpen);

            ImmReleaseContext(hWnd,hIMC);
            break;

        case IMN_SETCONVERSIONMODE:
            hIMC = ImmGetContext(hWnd);
            fOpen = ImmGetOpenStatus(hIMC);
            ImmGetConversionStatus(hIMC,&dwConvMode,&dwSentMode);
            if (fOpen)
            {
                SetConvModeParts(dwConvMode);
                UpdateModeButton(dwConvMode);
            }
            else
                ClearConvModeParts();
            ImmReleaseContext(hWnd,hIMC);
            break;

        case IMN_OPENCANDIDATE:
            if (!fShowCand || (lParam != 0x01))
            {
                if (fdwProperty & IME_PROP_SPECIAL_UI)
                {
                    // Normally, we only need to set the composition window
                    // position for a special UI IME
                }
                else if (fdwProperty & IME_PROP_AT_CARET)
                {
                    CANDIDATEFORM cdf;
                    HIMC          hIMC;

                    hIMC = ImmGetContext(hWnd);

                    cdf.dwIndex = 0;
                    cdf.dwStyle = CFS_CANDIDATEPOS;
                    cdf.ptCurrentPos.x = ptImeUIPos.x;
                    cdf.ptCurrentPos.y = ptImeUIPos.y;
                    ImmSetCandidateWindow(hIMC,&cdf);

                    ImmReleaseContext(hWnd,hIMC);
                }
                else
                {
                    // Normally, we only need to set the composition window
                    // position for a near caret IME
                }

                return (DefWindowProc(hWnd, message, wParam, lParam));
            }

        case IMN_CHANGECANDIDATE:

#ifdef _DEBUG
{
char szDev[80];
DWORD dwSize;
LPCANDIDATELIST lpC;

hIMC = ImmGetContext(hWnd);
if (dwSize = ImmGetCandidateList(hIMC,0x0,NULL,0))
{
    lpC = (LPCANDIDATELIST)GlobalAlloc(GPTR,dwSize);

    ImmGetCandidateList(hIMC,0x0,lpC,dwSize);

    OutputDebugString("DumpCandList!!!\r\n");
    wsprintf((LPSTR)szDev,"dwCount %d\r\n",lpC->dwCount);
    OutputDebugString((LPSTR)szDev);
    wsprintf((LPSTR)szDev,"dwSelection %d\r\n",lpC->dwSelection);
    OutputDebugString((LPSTR)szDev);
    wsprintf((LPSTR)szDev,"dwPageStart %d\r\n",lpC->dwPageStart);
    OutputDebugString((LPSTR)szDev);
    wsprintf((LPSTR)szDev,"dwPageSize %d\r\n",lpC->dwPageSize);
    OutputDebugString((LPSTR)szDev);
    GlobalFree((HANDLE)lpC);
}
}
#endif
            if (fShowCand && (lParam == 0x01))
            {
                DWORD dwSize;

                if (!lpCandList)
                    lpCandList = (LPCANDIDATELIST)GlobalAlloc(GPTR,sizeof(CANDIDATELIST));

                hIMC = ImmGetContext(hWnd);
                if (dwSize = ImmGetCandidateList(hIMC,0x0,NULL,0))
                {
                    GlobalFree((HANDLE)lpCandList);
                    lpCandList = (LPCANDIDATELIST)GlobalAlloc(GPTR,dwSize);

                    ImmGetCandidateList(hIMC,0x0,lpCandList,dwSize);

                }
                else
                {
                    memset(lpCandList, 0, sizeof(CANDIDATELIST));
                }

                InvalidateRect(hWndCandList,NULL,TRUE);
                UpdateWindow(hWndCandList);

                ImmReleaseContext(hWnd,hIMC);
            }
            else
            {
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }
            break;

        case IMN_CLOSECANDIDATE:
            if (fShowCand && (lParam == 0x01))
            {
                if (!lpCandList)
                    lpCandList = (LPCANDIDATELIST)GlobalAlloc(GPTR,sizeof(CANDIDATELIST));

                memset(lpCandList, 0, sizeof(CANDIDATELIST));
                InvalidateRect(hWndCandList,NULL,TRUE);
                UpdateWindow(hWndCandList);
            }
            else
            {
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }
            break;

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

    return 0;
}


DATA.C

/**********************************************************************/
/*                                                                    */
/*      DATA.C                                                        */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "wapi.h"

HANDLE hInst;
HWND hWndMain;
HWND hWndCompStr;
HWND hWndToolBar;
HWND hWndStatus;
HWND hWndCandList;

LOGFONT lf;
HFONT hFont = 0;

int nStatusHeight = 0;
int nToolBarHeight = 0;
BOOL fShowCand = FALSE;
DWORD fdwProperty = 0;

DWORD  dwCompStrLen      = 0;
DWORD  dwCompAttrLen     = 0;
DWORD  dwCompClsLen      = 0;
DWORD  dwCompReadStrLen  = 0;
DWORD  dwCompReadAttrLen = 0;
DWORD  dwCompReadClsLen  = 0;
DWORD  dwResultStrLen      = 0;
DWORD  dwResultClsLen      = 0;
DWORD  dwResultReadStrLen  = 0;
DWORD  dwResultReadClsLen  = 0;

MYCHAR szCompStr[512];
BYTE   bCompAttr[512];
DWORD  dwCompCls[128];
MYCHAR szCompReadStr[512];
BYTE   bCompReadAttr[512];
DWORD  dwCompReadCls[128];
MYCHAR szResultStr[512];
DWORD  dwResultCls[128];
MYCHAR szResultReadStr[512];
DWORD  dwResultReadCls[128];
MYCHAR szPaintResult[512];
MYCHAR szPaintResultRead[512];

LPCANDIDATELIST lpCandList = NULL;

POINT ptImeUIPos;


IMEAPPS.C

/**********************************************************************/
/*                                                                    */
/*      IMEAPPS.C                                                     */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "commdlg.h"
#include "commctrl.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"

/**********************************************************************/
/*                                                                    */
/*    WinMain(HINSTANCE, HINSTANCE, LPSTR, int)                             */
/*                                                                    */
/**********************************************************************/
int APIENTRY WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

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

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

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

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

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = MainWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(hInstance,"MyIcon");
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(LTGRAY_BRUSH);
    wc.lpszMenuName  = "ImeAppsMenu";
    wc.lpszClassName = "ImeAppsWClass";

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

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = CompStrWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 4;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(LTGRAY_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "CompStrWndClass";

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

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = CandListWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(LTGRAY_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "CandListWndClass";

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

    hFont = GetStockObject(DEFAULT_GUI_FONT);

    return TRUE;
}

/**********************************************************************/
/*                                                                    */
/*    InitInstance(HANDLE, int)                                       */
/*                                                                    */
/**********************************************************************/
BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
#ifdef USEWAPI
    char szTitle[] =  "ImeApps W API version";
#else
    char szTitle[] =  "ImeApps";
#endif

    hInst = hInstance;

    ptImeUIPos.x = 0;
    ptImeUIPos.y = 0;

    if (!(hWndMain = CreateWindowEx(0L,
    "ImeAppsWClass", (LPSTR)szTitle,
    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, NULL, hInstance, NULL)))
return FALSE;

    /* display each windows */
    ShowWindow (hWndMain, nCmdShow);
    UpdateWindow (hWndMain);

    return TRUE;

}

/**********************************************************************/
/*                                                                    */
/*    InitIMEUIPosition(HWND)                                         */
/*                                                                    */
/**********************************************************************/
void InitIMEUIPosition(hWnd)
HWND hWnd;
{
    HDC hDC;
    int y;
    SIZE sz;
    HFONT hOldFont;
    HFONT hDefFont;
    int height, defheight;

    hDefFont = GetStockObject(DEFAULT_GUI_FONT);

    hOldFont = NULL;

    y = ORG_Y;

    hDC = GetDC(hWnd);

    if (hFont)
        hOldFont = SelectObject(hDC,hDefFont);

    // Get the height of the default gui font.
    GetTextExtentPoint(hDC,"A",1,&sz);
    defheight = sz.cy + 1;

    // Get the height of the font.
    SelectObject(hDC,hFont);
    GetTextExtentPoint(hDC,"A",1,&sz);
    height = sz.cy + 1;

    SelectObject(hDC,hDefFont);
    y += defheight;

    y += height;

    y += height;

    y += defheight;

    y += height;

    y += height;

    ptImeUIPos.y = y;

    SelectObject(hDC,hOldFont);
    ReleaseDC(hWnd, hDC);

    return;
}

/**********************************************************************/
/*                                                                    */
/*    MainWndProc(HWND, UINT, WPARAM, LPARAM)                         */
/*                                                                    */
/**********************************************************************/
LRESULT CALLBACK MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
    FARPROC lpProc;
    RECT rect;
    HMENU hMenu;
    HIMC hIMC;
    BOOL fOpen;

    switch (message) {
case WM_CREATE:

            // Create Status Window
            CreateTBar(hWnd);
            CreateStatus(hWnd);

            // Create Child Window
            GetClientRect(hWnd, &rect);
            if (!(hWndCompStr = CreateWindowEx(WS_EX_CLIENTEDGE,
            "CompStrWndClass", NULL,
            WS_CHILD | WS_VISIBLE,
            // 0, 0, rect.right, rect.bottom - nStatusHeight,
            0, 0, 0, 0,
            hWnd, NULL, hInst, NULL)))
        return FALSE;

            InitIMEUIPosition(hWndCompStr);

            if (!(hWndCandList = CreateWindowEx(WS_EX_CLIENTEDGE,
            "CandListWndClass", NULL,
            WS_CHILD | WS_VISIBLE,
            0, 0, 0, 0,
            hWnd, NULL, hInst, NULL)))
        return FALSE;

            ShowWindow(hWndCompStr, SW_SHOW);
            ShowWindow(hWndCandList, SW_SHOWNA);

            SetStatusItems(hWnd);

            hMenu = GetMenu(hWnd);
            CheckMenuItem(hMenu,IDM_SHOWCAND,
                                (fShowCand ? MF_CHECKED : MF_UNCHECKED));

    break;

case WM_COMMAND:
    switch(LOWORD(wParam))
    {
case IDM_ABOUT:
    lpProc = MakeProcInstance(AboutDlg, hInst);
    DialogBox(hInst, "ABOUTBOX", hWnd, (DLGPROC)lpProc);
    FreeProcInstance(lpProc);
    break;

case IDM_FONT:
                    {
                    CHOOSEFONT cf;
                    LOGFONT lfT;

                    /* Set all structure fields to zero. */

                    memset(&cf, 0, sizeof(CHOOSEFONT));
                    memcpy(&lfT, &lf, sizeof(LOGFONT));

                    cf.lStructSize = sizeof(CHOOSEFONT);
                    cf.hwndOwner = hWnd;
                    cf.lpLogFont = &lfT;
                    cf.Flags = CF_SCREENFONTS | CF_EFFECTS;
                    cf.nFontType = SCREEN_FONTTYPE;

                    if (ChooseFont(&cf))
                    {
                        if (hFont)
                            DeleteObject(hFont);

                        memcpy(&lf, &lfT, sizeof(LOGFONT));
                        hFont = CreateFontIndirect(&lf);
                        InvalidateRect(hWndCompStr,NULL,TRUE);
                        UpdateWindow(hWndCompStr);
                    }
                    }
                    break;

                case IDM_SHOWCAND:
                    hMenu = GetMenu(hWnd);
                    fShowCand = !fShowCand;
                    CheckMenuItem(hMenu,IDM_SHOWCAND,
                                (fShowCand ? MF_CHECKED : MF_UNCHECKED));
                    MoveCompCandWindow(hWnd);
                    UpdateShowCandButton();
                    break;

                case IDM_OPENSTATUS:
                    hIMC = ImmGetContext(hWndCompStr);
                    fOpen = ImmGetOpenStatus(hIMC);
                    ImmSetOpenStatus(hIMC,!fOpen);
                    UpdateShowOpenStatusButton(!fOpen);
                    ImmReleaseContext(hWndCompStr,hIMC);

                    hMenu = GetMenu(hWnd);
                    CheckMenuItem(hMenu,IDM_OPENSTATUS,
                                (fOpen ? MF_UNCHECKED : MF_CHECKED));
                    break;

                case IDM_NATIVEMODE:
                case IDM_FULLHALF:
                case IDM_ROMAN:
                case IDM_CHARCODE:
                case IDM_HANJA:
                case IDM_SOFTKBD:
                case IDM_EUDC:
                case IDM_SYMBOL:
                    HandleModeCommand(hWnd,wParam,lParam);
                    break;

                case IDM_CONVERT:
                case IDM_CANCEL:
                case IDM_REVERT:
                case IDM_COMPLETE:
                case IDM_OPENCAND:
                case IDM_CLOSECAND:
                case IDM_NEXTCAND:
                case IDM_PREVCAND:
                    HandleConvertCommand(hWnd,wParam,lParam);
    break;

                case IDM_NEXTCLAUSE:
                case IDM_PREVCLAUSE:
                    HandleChangeAttr(hWnd,(LOWORD(wParam) == IDM_NEXTCLAUSE));
    break;

                default:
    break;

    }
    break;

        case WM_SETFOCUS:
            SetFocus(hWndCompStr);
    break;

        case WM_NOTIFY:
            SetTooltipText(hWnd, lParam);
            break;

        case WM_SIZE:
            SendMessage(hWndStatus,message,wParam,lParam);
            SendMessage(hWndToolBar,message,wParam,lParam);

            if (wParam != SIZE_MINIMIZED)
                MoveCompCandWindow(hWnd);

    break;

        case WM_IME_NOTIFY:
            switch (wParam)
            {
                case IMN_OPENSTATUSWINDOW:
                case IMN_CLOSESTATUSWINDOW:
                    break;

                case IMN_SETCONVERSIONMODE:
            return (DefWindowProc(hWnd, message, wParam, lParam));
            }
    break;

case WM_DESTROY:
            if (hFont)
                DeleteObject(hFont);
    PostQuitMessage(0);
    break;

default:
    return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return 0L;
}
/**********************************************************************/
/*                                                                    */
/*    CompStrWndProc(HWND, UINT, WPARAM, LPARAM)                      */
/*                                                                    */
/**********************************************************************/
LRESULT CALLBACK CompStrWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
    HIMC hIMC;
    HIMC hOldIMC;

    switch (message) {
case WM_CREATE:
            hIMC = ImmCreateContext();
            hOldIMC = ImmAssociateContext(hWnd,hIMC);
            SetWindowLong(hWnd, 0, (LONG)hOldIMC);
            fdwProperty = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
    break;

        case WM_CHAR:
            HandleChar(hWnd,wParam,lParam);
    break;

        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
            if (hIMC = ImmGetContext(hWnd))
            {
                HMENU hMenu;

                InitMenuItemIDTable();
                hMenu = CreateImeMenu(hWnd, hIMC, NULL,(message == WM_RBUTTONUP));

                if (hMenu)
                {
                    DWORD dwItemData;
                    DWORD dwPos = (DWORD)GetMessagePos();
                    int nCmd;

                    nCmd = TrackPopupMenuEx(hMenu,
                           TPM_RETURNCMD | TPM_NONOTIFY |
                           TPM_LEFTBUTTON | TPM_LEFTALIGN | TPM_TOPALIGN,
                           LOWORD(dwPos), HIWORD(dwPos),
                           hWnd, NULL);

                    if (nCmd)
                    {
                        nCmd -= IDM_STARTIMEMENU;
                        dwItemData = FindItemData(nCmd);
                        ImmNotifyIME(hIMC, NI_IMEMENUSELECTED, nCmd, dwItemData);
                    }
                }
                EndMenuItemIDTable();

                DestroyMenu(hMenu);
            }
    break;

        case WM_IME_SETCONTEXT:
            if (fShowCand)
                lParam &= ~ISC_SHOWUICANDIDATEWINDOW;

            if (fdwProperty & IME_PROP_SPECIAL_UI)
            {
            }
            else if (fdwProperty & IME_PROP_AT_CARET)
            {
                lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
            }
            else
            {
            }

    return (DefWindowProc(hWnd, message, wParam, lParam));

        case WM_IME_STARTCOMPOSITION:
            // Normally, we should not call into HandleStartComposition
            // for IME_PROP_SPECIAL_UI and not IME_PROP_AT_CARET IMEs
            // we should pass this message to DefWindowProc directly for
            // this kind of IMEs

            HandleStartComposition(hWnd,wParam,lParam);

            // pass this message to DefWindowProc for IME_PROP_SPECIAL_UI
            // and not IME_PROP_AT_CARET IMEs

            if (fdwProperty & IME_PROP_SPECIAL_UI)
            {
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }
            else if (fdwProperty & IME_PROP_AT_CARET)
            {
            }
            else
            {
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }

    break;

        case WM_IME_ENDCOMPOSITION:
            // Normally, we should not call into HandleEndComposition
            // for IME_PROP_SPECIAL_UI and not IME_PROP_AT_CARET IMEs
            // we should pass this message to DefWindowProc directly for
            // this kind of IMEs

            HandleEndComposition(hWnd,wParam,lParam);

            // pass this message to DefWindowProc for IME_PROP_SPECIAL_UI
            // and not IME_PROP_AT_CARET IMEs

            if (fdwProperty & IME_PROP_SPECIAL_UI)
            {
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }
            else if (fdwProperty & IME_PROP_AT_CARET)
            {
            }
            else
            {
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }

    break;

        case WM_IME_COMPOSITION:
            // Normally, we should not call into HandleComposition
            // for IME_PROP_SPECIAL_UI and not IME_PROP_AT_CARET IMEs
            // we should pass this message to DefWindowProc directly for
            // this kind of IMEs

            HandleComposition(hWnd,wParam,lParam);

            // pass this message to DefWindowProc for IME_PROP_SPECIAL_UI
            // and not IME_PROP_AT_CARET IMEs

            if (fdwProperty & IME_PROP_SPECIAL_UI)
            {
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }
            else if (fdwProperty & IME_PROP_AT_CARET)
            {
            }
            else
            {
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }

    break;

case WM_PAINT:
            HandlePaint(hWnd,wParam,lParam);
    break;

        case WM_IME_NOTIFY:
            {
                LRESULT lRet;

                // Normally, we should not call into HandleNotify
                // for IME_PROP_SPECIAL_UI and not IME_PROP_AT_CARET IMEs
                // we should pass this message to DefWindowProc directly for
                // this kind of IMEs

                lRet = HandleNotify(hWnd, message, wParam, lParam);

                // pass this message to DefWindowProc for IME_PROP_SPECIAL_UI
                // and not IME_PROP_AT_CARET IMEs

                if (fdwProperty & IME_PROP_SPECIAL_UI)
                {
                    return (DefWindowProc(hWnd, message, wParam, lParam));
                }
                else if (fdwProperty & IME_PROP_AT_CARET)
                {
                }
                else
                {
                    return (DefWindowProc(hWnd, message, wParam, lParam));
                }

                return lRet;
            }

        case WM_DESTROY:
            hOldIMC = (HIMC)GetWindowLong(hWnd, 0);
            hIMC = ImmAssociateContext(hWnd, hOldIMC);
            ImmDestroyContext(hIMC);
    break;

        case WM_INPUTLANGCHANGE:
            fdwProperty = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);

            if (hIMC = ImmGetContext(hWnd))
            {
                CANDIDATEFORM cdf;

                if (fdwProperty & IME_PROP_AT_CARET)
                {
                    cdf.dwIndex = 0;
                    cdf.dwStyle = CFS_CANDIDATEPOS;
                    cdf.ptCurrentPos.x = ptImeUIPos.x;
                    cdf.ptCurrentPos.y = ptImeUIPos.y;
                    ImmSetCandidateWindow(hIMC, &cdf);
                }
                else
                {
                    UINT i;

                    // The candidate position should be decided by a near caret
                    // IME. There are 4 candidate form in the input context

                    for (i = 0; i < 4; i++)
                    {
                        if (!ImmGetCandidateWindow(hIMC, i, &cdf))
                        {
                            continue;
                        }

                        if (cdf.dwStyle == CFS_DEFAULT)
                        {
                            continue;
                        }

                        cdf.dwStyle = CFS_DEFAULT;

                        ImmSetCandidateWindow(hIMC, &cdf);
                    }

                }

                ImmReleaseContext(hWnd, hIMC);
            }

    return (DefWindowProc(hWnd, message, wParam, lParam));

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

/**********************************************************************/
/*                                                                    */
/*    CandListWndProc(HWND, UINT, WPARAM, LPARAM)                     */
/*                                                                    */
/**********************************************************************/
LRESULT CALLBACK CandListWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
    switch (message) {

case WM_PAINT:
            HandleCandPaint(hWnd,wParam,lParam);
    break;

        case WM_SETFOCUS:
            SetFocus(hWndCompStr);
    break;

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

/**********************************************************************/
/*                                                                    */
/*    About(HWND, UINT, WPARAM, LPARAM)                               */
/*                                                                    */
/**********************************************************************/
LRESULT CALLBACK AboutDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WPARAM wParam;
LPARAM lParam;
{
    switch (message) {
case WM_INITDIALOG:
    return (TRUE);

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


IMEAPPS.H

/**********************************************************************/
/*                                                                    */
/*      IMEAPPS.H                                                     */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "wapi.h"

#define ORG_X                     10
#define ORG_Y                     10

#define RESULT_X                  10
#define RESULT_Y                  10
#define RESULTREAD_X              10
#define RESULTREAD_Y              30

#define COMPSTR_X                 10
#define COMPSTR_Y                 50
#define COMPREAD_X                10
#define COMPREAD_Y                60

#define IMESTATUS_CY              20
#define CAND_CX                   130

extern HANDLE hInst;
extern HWND hWndMain;
extern HWND hWndToolBar;
extern HWND hWndStatus;
extern HWND hWndCompStr;
extern HWND hWndCandList;

extern LOGFONT lf;
extern HFONT hFont;

extern int nStatusHeight;
extern BOOL fShowCand;
extern DWORD  fdwProperty;

extern DWORD  dwCompStrLen;
extern DWORD  dwCompAttrLen;
extern DWORD  dwCompClsLen;
extern DWORD  dwCompReadStrLen;
extern DWORD  dwCompReadAttrLen;
extern DWORD  dwCompReadClsLen;
extern DWORD  dwResultStrLen;
extern DWORD  dwResultClsLen;
extern DWORD  dwResultReadStrLen;
extern DWORD  dwResultReadClsLen;

extern MYCHAR szCompStr[512];
extern BYTE   bCompAttr[512];
extern DWORD  dwCompCls[128];
extern MYCHAR szCompReadStr[512];
extern BYTE   bCompReadAttr[512];
extern DWORD  dwCompReadCls[128];
extern MYCHAR szResultStr[512];
extern DWORD  dwResultCls[128];
extern MYCHAR szResultReadStr[512];
extern DWORD  dwResultReadCls[128];
extern MYCHAR szPaintResult[512];
extern MYCHAR szPaintResultRead[512];

extern LPCANDIDATELIST lpCandList;
extern POINT ptImeUIPos;

/* imeapps.c */
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK CompStrWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK CandListWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK AboutDlg(HWND, UINT, WPARAM, LPARAM);
LRESULT HandleStartComposition(HWND,WPARAM,LPARAM);
LRESULT HandleEndComposition(HWND,WPARAM,LPARAM);
LRESULT HandleComposition(HWND,WPARAM,LPARAM);
LRESULT HandleChar(HWND,WPARAM,LPARAM);
LRESULT HandleNotify(HWND,UINT,WPARAM,LPARAM);
LRESULT HandlePaint(HWND, WPARAM, LPARAM);
LRESULT HandleCandPaint(HWND, WPARAM, LPARAM);

// setcomp.c
LRESULT HandleChangeAttr(HWND hWnd,BOOL fNext);

// subs.c
void MoveCompCandWindow(HWND hWnd);
BOOL CreateStatus();
void SetStatusItems(HWND hWnd);
void SetOpenStatusParts(BOOL fOpen);
void SetConvModeParts(DWORD dwConvMode);
void ClearConvModeParts();

// toolbar.c
BOOL CreateTBar(HWND);
LRESULT SetTooltipText(HWND hWnd, LPARAM lparam);
void UpdateButton(UINT iID, UINT iFlags);
void UpdateShowOpenStatusButton(BOOL fOpen);
void UpdateShowCandButton();
void UpdateModeButton(DWORD dwConv);

// mode.c
LRESULT HandleModeCommand(HWND hWnd,WPARAM wParam,LPARAM lParam);
LRESULT HandleConvertCommand(HWND, WPARAM, LPARAM);

// imemenu.c
void EndMenuItemIDTable();
void InitMenuItemIDTable();
DWORD FindItemData(UINT wID);
HMENU CreateImeMenu(HWND hWnd, HIMC hIMC, LPMYIMEMENUITEMINFO lpImeParentMenu, BOOL fRight);


IMEMENU.C

/**********************************************************************/
/*                                                                    */
/*      IMEMENU.C                                                     */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "commdlg.h"
#include "commctrl.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"

LPDWORD lpdwIDTable = NULL;
int nIDTable = 0;

/**********************************************************************/
/*                                                                    */
/*    EndMenuItemIDTable()                                            */
/*                                                                    */
/**********************************************************************/
void EndMenuItemIDTable()
{
    if (lpdwIDTable)
        GlobalFree((HANDLE)lpdwIDTable);

    nIDTable = 0;
}

/**********************************************************************/
/*                                                                    */
/*    InitMenuItemIDTable()                                           */
/*                                                                    */
/**********************************************************************/
void InitMenuItemIDTable()
{
    EndMenuItemIDTable();
    lpdwIDTable = GlobalAlloc(GPTR, 100 * 2 * sizeof(DWORD));
}

/**********************************************************************/
/*                                                                    */
/*    AddIDTableEntry()                                               */
/*                                                                    */
/**********************************************************************/
void AddIDTableEntry(UINT wID, DWORD dwItemData)
{
    *(lpdwIDTable+(nIDTable*2)) = wID;
    *(lpdwIDTable+(nIDTable*2)+1) = dwItemData;
    nIDTable++;
}

/**********************************************************************/
/*                                                                    */
/*    FindItemData()                                                  */
/*                                                                    */
/**********************************************************************/
DWORD FindItemData(UINT wID)
{
    int i;
    for (i = 0; i < nIDTable ; i++)
    {
        if (*(lpdwIDTable+(i*2)) == wID)
            return *(lpdwIDTable+(i*2)+1);
    }
}

/**********************************************************************/
/*                                                                    */
/*    FillMenuItemInfo()                                              */
/*                                                                    */
/**********************************************************************/
void FillMenuItemInfo(HWND hWnd, HIMC hIMC, LPMENUITEMINFO lpmii, LPMYIMEMENUITEMINFO lpIme, BOOL fRight)
{
    FillMemory((PVOID)lpmii, sizeof(MENUITEMINFO), 0);
    lpmii->cbSize = sizeof(MENUITEMINFO);
    lpmii->fMask = 0;

    // Set fType;
    if (lpIme->fType)
    {
        lpmii->fMask |= MIIM_TYPE;
        lpmii->fType = 0;

        if (lpIme->fType & IMFT_RADIOCHECK)
            lpmii->fType |= MFT_RADIOCHECK;

        if (lpIme->fType & IMFT_SEPARATOR)
            lpmii->fType |= MFT_SEPARATOR;

    }

    lpmii->fMask |= MIIM_ID;
    lpmii->wID = lpIme->wID + IDM_STARTIMEMENU;

    if (lpIme->fType & IMFT_SUBMENU)
    {
        lpmii->fMask |= MIIM_SUBMENU;
        lpmii->hSubMenu = CreateImeMenu(hWnd, hIMC, lpIme, fRight);
    }

    lpmii->fMask |= MIIM_STATE;
    lpmii->fState = lpIme->fState;

    if (lpIme->hbmpChecked &&  lpIme->hbmpUnchecked)
    {
       lpmii->fMask |= MIIM_CHECKMARKS;
       lpmii->hbmpChecked = lpIme->hbmpChecked;
       lpmii->hbmpUnchecked = lpIme->hbmpUnchecked;
    }

    lpmii->fMask |= MIIM_DATA;
    lpmii->dwItemData = lpIme->dwItemData;

    if (lpIme->hbmpItem)
    {
       lpmii->fMask |= MIIM_BITMAP;
       lpmii->hbmpItem = lpIme->hbmpItem;
    }

#ifdef USEWAPI
    {
        BOOL bUDC;
        int i;
        char   szTemp[IMEMENUITEM_STRING_SIZE];

        i = WideCharToMultiByte(CP_ACP,
                                0,
                                lpIme->szString,
                                wcslen(lpIme->szString),
                                szTemp,
                                IMEMENUITEM_STRING_SIZE,
                                (LPSTR)NULL,
                                &bUDC);
        szTemp[i] = '\0';

        if (lstrlen(szTemp))
        {
            lpmii->fMask |= MIIM_STRING;
            lstrcpy((LPSTR)lpIme->szString, szTemp);
            lpmii->dwTypeData = (LPSTR)lpIme->szString;
            lpmii->cch = lstrlen((LPSTR)lpIme->szString);
        }
    }
#else
    if (lstrlen(lpIme->szString))
    {
        lpmii->fMask |= MIIM_STRING;
        lpmii->dwTypeData = lpIme->szString;
        lpmii->cch = lstrlen(lpIme->szString);
    }
#endif

}

/**********************************************************************/
/*                                                                    */
/*    CreateImeMenu()                                                 */
/*                                                                    */
/**********************************************************************/
HMENU CreateImeMenu(HWND hWnd, HIMC hIMC, LPMYIMEMENUITEMINFO lpImeParentMenu, BOOL fRight)
{
    HMENU hMenu;
    DWORD dwSize, dwNum, dwI;
    MENUITEMINFO mii;
    LPMYIMEMENUITEMINFO lpImeMenu;

    dwNum = MyImmGetImeMenuItems(hIMC, fRight ? IGIMIF_RIGHTMENU : 0,
                                 IGIMII_CMODE |
                                 IGIMII_SMODE |
                                 IGIMII_CONFIGURE |
                                 IGIMII_TOOLS |
                                 IGIMII_HELP |
                                 IGIMII_OTHER,
                                 lpImeParentMenu, NULL, 0);

    if (!dwNum)
        return 0;

    dwSize = dwNum * sizeof(MYIMEMENUITEMINFO);

    lpImeMenu = (LPMYIMEMENUITEMINFO)GlobalAlloc(GPTR, dwSize);

    dwNum = MyImmGetImeMenuItems(hIMC, fRight ? IGIMIF_RIGHTMENU : 0,
                                 IGIMII_CMODE |
                                 IGIMII_SMODE |
                                 IGIMII_CONFIGURE |
                                 IGIMII_TOOLS |
                                 IGIMII_HELP |
                                 IGIMII_OTHER,
                                 lpImeParentMenu, lpImeMenu, dwSize);

    hMenu = CreatePopupMenu();

    for (dwI = 0 ; dwI < dwNum; dwI++)
    {
        AddIDTableEntry(lpImeMenu->wID, lpImeMenu->dwItemData);
        FillMenuItemInfo(hWnd, hIMC, &mii, lpImeMenu + dwI, fRight);
        InsertMenuItem(hMenu, dwI, TRUE, &mii);
    }

    GlobalFree((HANDLE)lpImeMenu);

    return hMenu;
}


MODE.C

/**********************************************************************/
/*                                                                    */
/*      MODE.C                                                        */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"

LRESULT HandleModeCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    HIMC hIMC = ImmGetContext(hWndCompStr);
    DWORD dwConv, dwSent;
    DWORD dwTemp;

    ImmGetConversionStatus(hIMC,&dwConv,&dwSent);

    switch(LOWORD(wParam))
    {
        case IDM_NATIVEMODE:
            dwTemp = dwConv & ~IME_CMODE_LANGUAGE;

            switch (dwConv & IME_CMODE_LANGUAGE)
            {
                case IME_CMODE_ALPHANUMERIC:
                    dwTemp |= IME_CMODE_NATIVE;
                    break;

                case IME_CMODE_NATIVE:
                    dwTemp |= (IME_CMODE_NATIVE | IME_CMODE_KATAKANA);
                    break;

                case (IME_CMODE_NATIVE | IME_CMODE_KATAKANA):
                default:
                    break;
            }
            dwConv = dwTemp;
            break;

        case IDM_FULLHALF:
            if (dwConv & IME_CMODE_FULLSHAPE)
                dwConv &= ~IME_CMODE_FULLSHAPE;
            else
                dwConv |= IME_CMODE_FULLSHAPE;
            break;

        case IDM_ROMAN:
            if (dwConv & IME_CMODE_ROMAN)
                dwConv &= ~IME_CMODE_ROMAN;
            else
                dwConv |= IME_CMODE_ROMAN;
            break;

        case IDM_CHARCODE:
            if (dwConv & IME_CMODE_CHARCODE)
                dwConv &= ~IME_CMODE_CHARCODE;
            else
                dwConv |= IME_CMODE_CHARCODE;
            break;

        case IDM_HANJA:
            if (dwConv & IME_CMODE_HANJACONVERT)
                dwConv &= ~IME_CMODE_HANJACONVERT;
            else
                dwConv |= IME_CMODE_HANJACONVERT;
            break;

        case IDM_SOFTKBD:
            if (dwConv & IME_CMODE_SOFTKBD)
                dwConv &= ~IME_CMODE_SOFTKBD;
            else
                dwConv |= IME_CMODE_SOFTKBD;
            break;

        case IDM_EUDC:
            if (dwConv & IME_CMODE_EUDC)
                dwConv &= ~IME_CMODE_EUDC;
            else
                dwConv |= IME_CMODE_EUDC;
            break;

        case IDM_SYMBOL:
            if (dwConv & IME_CMODE_SYMBOL)
                dwConv &= ~IME_CMODE_SYMBOL;
            else
                dwConv |= IME_CMODE_SYMBOL;
            break;
    }

    ImmSetConversionStatus(hIMC,dwConv,dwSent);

    ImmReleaseContext(hWndCompStr,hIMC);

    return 1L;
}

LRESULT HandleConvertCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
    HIMC hIMC = ImmGetContext(hWndCompStr);
    DWORD dwSel;
    DWORD dwSize;
    LPCANDIDATELIST lpCL;

    switch(wParam)
    {
        case IDM_CONVERT:
            ImmNotifyIME(hIMC,NI_COMPOSITIONSTR,CPS_CONVERT,0);
            break;

        case IDM_CANCEL:
            ImmNotifyIME(hIMC,NI_COMPOSITIONSTR,CPS_CANCEL,0);
            break;

        case IDM_REVERT:
            ImmNotifyIME(hIMC,NI_COMPOSITIONSTR,CPS_REVERT,0);
            break;

        case IDM_COMPLETE:
            ImmNotifyIME(hIMC,NI_COMPOSITIONSTR,CPS_COMPLETE,0);
            break;

        case IDM_OPENCAND:
            ImmNotifyIME(hIMC,NI_OPENCANDIDATE,0,0);
            break;

        case IDM_CLOSECAND:
            ImmNotifyIME(hIMC,NI_CLOSECANDIDATE,0,0);
            break;

        case IDM_NEXTCAND:
        case IDM_PREVCAND:
            if (dwSize = ImmGetCandidateList(hIMC,0x0,NULL,0))
            {
                lpCL = (LPCANDIDATELIST)GlobalAlloc(GPTR,dwSize);

                ImmGetCandidateList(hIMC,0x0,lpCL,dwSize);

                dwSel = lpCL->dwSelection;

                if (wParam == IDM_NEXTCAND)
                {
                   if (++dwSel >= lpCL->dwCount)
                      dwSel = 0;
                }
                else
                {
                   if (dwSel)
                      dwSel--;
                   else
                      dwSel = lpCL->dwCount - 1;
                }
                GlobalFree((HANDLE)lpCL);

                ImmNotifyIME(hIMC,NI_SELECTCANDIDATESTR,0,dwSel);
            }
            break;

    }

    ImmReleaseContext(hWndCompStr,hIMC);

    return 1;
}


PAINT.C

/**********************************************************************/
/*                                                                    */
/*      PAINT.C                                                       */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"

#define DEBUG 1

void SetAttrColor(HDC hDC, BYTE bAttr)
{
    switch (bAttr)
    {
        case ATTR_INPUT:
            SetTextColor(hDC,RGB(0,0,0));
            SetBkMode(hDC,TRANSPARENT);
            break;
        case ATTR_TARGET_CONVERTED:
            SetTextColor(hDC,RGB(255,255,255));
            SetBkMode(hDC,OPAQUE);
            SetBkColor(hDC,RGB(0,0,255));
            break;
        case ATTR_CONVERTED:
            SetTextColor(hDC,RGB(0,0,255));
            SetBkMode(hDC,TRANSPARENT);
            break;
        case ATTR_TARGET_NOTCONVERTED:
            SetTextColor(hDC,RGB(255,255,255));
            SetBkMode(hDC,OPAQUE);
            SetBkColor(hDC,RGB(0,255,0));
            break;
        case ATTR_INPUT_ERROR:
            SetTextColor(hDC,RGB(255,255,0));
            SetBkMode(hDC,TRANSPARENT);
            break;
        case ATTR_FIXEDCONVERTED:
            SetTextColor(hDC,RGB(255,0,0));
            SetBkMode(hDC,TRANSPARENT);
            break;
        default:
            SetTextColor(hDC,RGB(0,0,0));
            SetBkMode(hDC,TRANSPARENT);
            break;
    }
}

LRESULT HandlePaint(HWND hWnd,WPARAM wParam, LPARAM lParam)
{
    HDC hDC;
    PAINTSTRUCT ps;
    UINT i;
    int x = ORG_X;
    int y = ORG_Y;
    SIZE sz;
    HFONT hOldFont = 0;
    HFONT hDefFont = GetStockObject(DEFAULT_GUI_FONT);
    int height,defheight;
    const char  szResult[] = "Result String";
    const char  szComp[] = "Composition String";
    RECT rc;

    GetClientRect(hWnd,&rc);

    hDC = BeginPaint(hWnd, &ps);

    if (hFont)
        hOldFont = SelectObject(hDC,hDefFont);

    // Get the height of the default gui font.
    GetTextExtentPoint(hDC,"A",1,&sz);
    defheight = sz.cy + 1;

    // Get the height of the font.
    SelectObject(hDC,hFont);
    GetTextExtentPoint(hDC,"A",1,&sz);
    height = sz.cy + 1;

    SelectObject(hDC,hDefFont);
    SetTextColor(hDC,RGB(0,0,0));
    SetBkMode(hDC,TRANSPARENT);
    TextOut(hDC,ORG_X,y,szResult,lstrlen(szResult));
    y += defheight;

    if (Mylstrlen(szPaintResult))
    {
        x = ORG_X;
        SelectObject(hDC,hFont);
        SetTextColor(hDC,RGB(255,0,0));
        SetBkMode(hDC,TRANSPARENT);
#ifdef USEWAPI
        TextOutW(hDC,x,y,szPaintResult,Mylstrlen(szPaintResult));
#else
        TextOut(hDC,x,y,szPaintResult,Mylstrlen(szPaintResult));
#endif
    }

    y += height;

    if (Mylstrlen(szPaintResultRead))
    {
        x = ORG_X;
        SelectObject(hDC,hFont);
        SetTextColor(hDC,RGB(255,0,0));
        SetBkMode(hDC,TRANSPARENT);
#ifdef USEWAPI
        TextOutW(hDC,x,y,szPaintResultRead,Mylstrlen(szPaintResultRead));
#else
        TextOut(hDC,x,y,szPaintResultRead,Mylstrlen(szPaintResultRead));
#endif
    }
#if 0
    if (dwResultReadStrLen)
    {
        x = ORG_X;
        SelectObject(hDC,hFont);
        SetTextColor(hDC,RGB(0,0,0));
        SetBkMode(hDC,TRANSPARENT);

        if (dwResultReadClsLen)
        {
            dwResultReadCls[127] = 0;
            i = 1;

            SetTextColor(hDC,RGB(255,0,0));
            while (dwResultReadCls[i] && dwResultReadCls[i-1] < dwResultReadStrLen)
            {
                DWORD dwTextLen = dwResultReadCls[i] - dwResultReadCls[i-1];
                LPSTR lpStart = szResultReadStr + dwResultReadCls[i-1];

                TextOut(hDC,x,y,lpStart,dwTextLen);
                GetTextExtentPoint(hDC,lpStart,dwTextLen,&sz);
                x += sz.cx;

                TextOut(hDC,x,y,",",1);
                GetTextExtentPoint(hDC,",",1,&sz);
                x += (sz.cx + 2);

                i++;
            }
        }
        else
        {
            SetTextColor(hDC,RGB(255,0,0));
            SetBkMode(hDC,TRANSPARENT);
            TextOut(hDC,x,y,szResultReadStr,dwResultReadStrLen);
        }

    }
#endif

    y += height;

    SelectObject(hDC,hDefFont);
    SetTextColor(hDC,RGB(0,0,0));
    SetBkMode(hDC,TRANSPARENT);
    TextOut(hDC,ORG_X,y,szComp,lstrlen(szComp));
    y += defheight;

    if (dwCompStrLen)
    {
        x = ORG_X;
        SelectObject(hDC,hFont);
        SetTextColor(hDC,RGB(0,0,0));

        if (dwCompClsLen && dwCompAttrLen)
        {
            dwCompCls[127] = 0;
            i = 1;

            while (dwCompCls[i] && dwCompCls[i-1] < dwCompStrLen)
            {
                DWORD dwTextLen = dwCompCls[i] - dwCompCls[i-1];
                LPMYSTR lpStart = szCompStr + dwCompCls[i-1];

                SetAttrColor(hDC, bCompAttr[dwCompCls[i-1]]);
#ifdef USEWAPI
                TextOutW(hDC,x,y,lpStart,dwTextLen);
                GetTextExtentPointW(hDC,lpStart,dwTextLen,&sz);
#else
                TextOut(hDC,x,y,lpStart,dwTextLen);
                GetTextExtentPoint(hDC,lpStart,dwTextLen,&sz);
#endif
                x += sz.cx;

                SetTextColor(hDC,RGB(0,0,0));
                SetBkMode(hDC,TRANSPARENT);
#ifdef USEWAPI
                TextOutW(hDC,x,y,MYTEXT(","),1);
                GetTextExtentPointW(hDC,MYTEXT(","),1,&sz);
#else
                TextOut(hDC,x,y,",",1);
                GetTextExtentPoint(hDC,",",1,&sz);
#endif
                x += (sz.cx + 2);

                i++;
            }
        }
        else
        {
            SetBkMode(hDC,TRANSPARENT);
#ifdef USEWAPI
            TextOutW(hDC,x,y,szCompStr,dwCompStrLen);
#else
            TextOut(hDC,x,y,szCompStr,dwCompStrLen);
#endif
        }

    }

    y += height;

    if (dwCompReadStrLen)
    {
        x = ORG_X;
        SelectObject(hDC,hFont);
        SetTextColor(hDC,RGB(0,0,0));
        SetBkMode(hDC,TRANSPARENT);

        if (dwCompReadClsLen && dwCompReadAttrLen)
        {
            dwCompReadCls[127] = 0;
            i = 1;

            while (dwCompReadCls[i] && dwCompReadCls[i-1] < dwCompReadStrLen)
            {
                DWORD dwTextLen = dwCompReadCls[i] - dwCompReadCls[i-1];
                LPMYSTR lpStart = szCompReadStr + dwCompReadCls[i-1];

                SetAttrColor(hDC, bCompReadAttr[dwCompReadCls[i-1]]);
#ifdef USEWAPI
                TextOutW(hDC,x,y,lpStart,dwTextLen);
                GetTextExtentPointW(hDC,lpStart,dwTextLen,&sz);
#else
                TextOut(hDC,x,y,lpStart,dwTextLen);
                GetTextExtentPoint(hDC,lpStart,dwTextLen,&sz);
#endif
                x += sz.cx;

                SetTextColor(hDC,RGB(0,0,0));
                SetBkMode(hDC,TRANSPARENT);
#ifdef USEWAPI
                TextOutW(hDC,x,y,MYTEXT(","),1);
                GetTextExtentPointW(hDC,MYTEXT(","),1,&sz);
#else
                TextOut(hDC,x,y,",",1);
                GetTextExtentPoint(hDC,",",1,&sz);
#endif
                x += (sz.cx + 2);

                i++;
            }
        }
        else
        {
            SetBkMode(hDC,TRANSPARENT);
#ifdef USEWAPI
            TextOutW(hDC,x,y,szCompReadStr,dwCompReadStrLen);
#else
            TextOut(hDC,x,y,szCompReadStr,dwCompReadStrLen);
#endif
        }

    }

    y += height;

    ptImeUIPos.y = y;

    SelectObject(hDC,hOldFont);
    EndPaint(hWnd,&ps);
    return 1;
}

LRESULT HandleCandPaint(HWND hWnd,WPARAM wParam, LPARAM lParam)
{
    HDC hDC;
    PAINTSTRUCT ps;
    UINT i;
    int x = ORG_X;
    int y = ORG_Y;
    SIZE sz;
    HFONT hOldFont = 0;
    HFONT hDefFont = GetStockObject(DEFAULT_GUI_FONT);
    int height,defheight;
    LPDWORD lpdwOffset;
    RECT rect;
    const char  szCand[] = "Candidate List";
    const char  szCandNull[] = "";
    const char  szCandRead[] = "Reading";
    const char  szCandCode[] = "Code";
    const char  szCandMean[] = "Meaning";
    const char  szCandRadi[] = "Radical";
    const char  szCandStrk[] = "Stroke";

    GetClientRect(hWnd,&rect);

    hDC = BeginPaint(hWnd, &ps);

    if (!lpCandList)
        goto pt_cand_10;

    if (hFont)
        hOldFont = SelectObject(hDC,hDefFont);

    // Get the height of the default gui font.
    GetTextExtentPoint(hDC,"A",1,&sz);
    defheight = sz.cy + 1;

    // Get the height of the font.
    SelectObject(hDC,hFont);
    GetTextExtentPoint(hDC,"A",1,&sz);
    height = sz.cy + 1;

    SelectObject(hDC,hDefFont);
    SetTextColor(hDC,RGB(0,0,0));
    SetBkMode(hDC,TRANSPARENT);
    TextOut(hDC,ORG_X,y,szCand,lstrlen(szCand));
    y += defheight;

    switch (lpCandList->dwStyle)
    {
        case IME_CAND_READ:
            TextOut(hDC,ORG_X,y,szCandRead,lstrlen(szCandRead));
            break;

        case IME_CAND_CODE:
            TextOut(hDC,ORG_X,y,szCandCode,lstrlen(szCandCode));
            break;

        case IME_CAND_MEANING:
            TextOut(hDC,ORG_X,y,szCandMean,lstrlen(szCandMean));
            break;

        case IME_CAND_RADICAL:
            TextOut(hDC,ORG_X,y,szCandRadi,lstrlen(szCandRadi));
            break;

        case IME_CAND_STROKE:
            TextOut(hDC,ORG_X,y,szCandStrk,lstrlen(szCandStrk));
            break;

        default:
            break;

    }
    y += defheight;

    if (!lpCandList->dwCount)
        goto pt_cand_10;

    lpdwOffset = &lpCandList->dwOffset[0];

    lpdwOffset += lpCandList->dwPageStart;

    for (i = lpCandList->dwPageStart;
         (i < lpCandList->dwCount) &&
         (i < lpCandList->dwPageStart + lpCandList->dwPageSize) &&
         (y <= rect.bottom + height); i++)
    {
        LPSTR lpstr = (LPSTR)lpCandList + *lpdwOffset++;

        x = ORG_X;

        SelectObject(hDC,hFont);
        if (i != lpCandList->dwSelection)
        {
            SetTextColor(hDC,RGB(0,0,0));
            SetBkMode(hDC,TRANSPARENT);
        }
        else
        {
            SetTextColor(hDC,RGB(255,255,255));
            SetBkColor(hDC,RGB(0,0,255));
            SetBkMode(hDC,OPAQUE);
        }

        TextOut(hDC,x,y,lpstr,lstrlen(lpstr));
        y += height;
    }

    SelectObject(hDC,hOldFont);

pt_cand_10:
    EndPaint(hWnd,&ps);
    return 1;
}


PCH.H

#include "windows.h"
#include "commdlg.h"
#include "commctrl.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"


RESOURCE.H

/**********************************************************************/
/*                                                                    */
/*      RESOURCE.H                                                    */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#define STATUS_ID                       10001
#define TOOLBAR_ID                      10002

#define IDC_STATIC                      -1
#define IDB_BMP                         700 // Bitmap ID for the toolbar.

#define IDM_FONT                        10001
#define IDM_ABOUT                       10002

#define IDM_OPENSTATUS                  10010

#define IDM_NATIVEMODE                  10011
#define IDM_FULLHALF                    10012
#define IDM_ROMAN                       10013
#define IDM_CHARCODE                    10014
#define IDM_HANJA                       10015
#define IDM_SOFTKBD                     10016
#define IDM_EUDC                        10017
#define IDM_SYMBOL                      10018

#define IDM_CONVERT                     10020
#define IDM_CANCEL                      10021
#define IDM_REVERT                      10022
#define IDM_COMPLETE                    10023
#define IDM_SHOWCAND                    10024
#define IDM_OPENCAND                    10025
#define IDM_CLOSECAND                   10026
#define IDM_NEXTCAND                    10027
#define IDM_PREVCAND                    10028
#define IDM_NEXTCLAUSE                  10029
#define IDM_PREVCLAUSE                  10030

#define IDM_STARTIMEMENU                20000


SETCOMP.C

/**********************************************************************/
/*                                                                    */
/*      comp.C                                                        */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"

DWORD GetTargetClause()
{
    int nMaxClause = (int)(dwCompClsLen / sizeof(DWORD)) - 1;
    int nCnt;

    if (nMaxClause <= 0)
        return (DWORD)-1L;

    for (nCnt = 0; nCnt < nMaxClause; nCnt++)
    {
        DWORD dwCls = dwCompCls[nCnt];

        if ((bCompAttr[dwCompCls[nCnt]] == ATTR_TARGET_CONVERTED) ||
            (bCompAttr[dwCompCls[nCnt]] == ATTR_TARGET_NOTCONVERTED))
            return nCnt;
    }

    return (DWORD)-1L;
}

LRESULT HandleChangeAttr(HWND hWnd,BOOL fNext)
{
    BYTE bAttr[512];
    BYTE bAttrRead[512];
    UINT i,j;
    UINT uClause;
    UINT uMaxClause;
    UINT uCnt;
    UINT uCntRead;
    BYTE bAt;
    BOOL fAttrOK = FALSE;
    BOOL fAttrReadOK = FALSE;
    HIMC hIMC;
    BOOL bRet = FALSE;

    uMaxClause = (dwCompClsLen / sizeof(DWORD)) - 1;
    uClause = GetTargetClause();

    if (uClause == (DWORD)-1L)
    {
         return 0L;
    }

    if (fNext)
    {
        if (uClause + 1 <= uMaxClause)
            uClause++;
        else
            return 0L;
    }
    else
    {
        if (uClause > 0)
            uClause--;
        else
            return 0L;
    }

    uCnt = 0;

    if (uClause < uMaxClause)
    {
        for (i=0; i < uMaxClause; i++)
        {
            if (i == uClause)
            {
                switch (bCompAttr[dwCompCls[i]])
                {
                    case ATTR_INPUT:
                        bAt = ATTR_TARGET_NOTCONVERTED;
                        break;
                        break;
                    case ATTR_CONVERTED:
                        bAt = ATTR_TARGET_CONVERTED;
                        break;
                    default:
                        bAt = bCompAttr[dwCompCls[i]];
                        break;
                }
            }
            else
            {
                switch (bCompAttr[dwCompCls[i]])
                {
                    case ATTR_TARGET_CONVERTED:
                        bAt = ATTR_CONVERTED;
                        break;
                        break;
                    case ATTR_TARGET_NOTCONVERTED:
                        bAt = ATTR_INPUT;
                        break;
                    default:
                        bAt = bCompAttr[dwCompCls[i]];
                        break;
                }
            }

            for (j = 0; j < (dwCompCls[i+1] - dwCompCls[i]); j++)
            {
                bAttr[uCnt++] = bAt;
            }
        }
        fAttrOK = TRUE;
    }

    uCntRead = 0;

    if (uClause < uMaxClause)
    {
        for (i=0; i < uMaxClause; i++)
        {
            if (i == uClause)
            {
                switch (bCompReadAttr[dwCompReadCls[i]])
                {
                    case ATTR_INPUT:
                        bAt = ATTR_TARGET_NOTCONVERTED;
                        break;
                        break;
                    case ATTR_CONVERTED:
                        bAt = ATTR_TARGET_CONVERTED;
                        break;
                    default:
                        bAt = bCompReadAttr[dwCompReadCls[i]];
                        break;
                }
            }
            else
            {
                switch (bCompReadAttr[dwCompReadCls[i]])
                {
                    case ATTR_TARGET_CONVERTED:
                        bAt = ATTR_CONVERTED;
                        break;
                        break;
                    case ATTR_TARGET_NOTCONVERTED:
                        bAt = ATTR_INPUT;
                        break;
                    default:
                        bAt = bCompReadAttr[dwCompReadCls[i]];
                        break;
                }
            }

            for (j = 0; j < (dwCompReadCls[i+1] - dwCompReadCls[i]); j++)
            {
                bAttrRead[uCntRead++] = bAt;
            }
        }
        fAttrReadOK = TRUE;
    }

    if (fAttrReadOK && fAttrOK)
    {
        hIMC = ImmGetContext(hWndCompStr);
#ifdef USEWAPI
        bRet = ImmSetCompositionStringW(hIMC,SCS_CHANGEATTR,bAttr,uCnt,
                                                    bAttrRead,uCntRead);
#else
        bRet = ImmSetCompositionString(hIMC,SCS_CHANGEATTR,bAttr,uCnt,
                                                    bAttrRead,uCntRead);
#endif
        // bRet = ImmSetCompositionString(hIMC,SCS_CHANGEATTR,NULL,0,
        //                                             bAttrRead,uCntRead);
        // bRet = ImmSetCompositionString(hIMC,SCS_CHANGEATTR,bAttr,uCnt,
        //                                             NULL,0);
        ImmReleaseContext(hWndCompStr,hIMC);
    }
    else
    {
#ifdef DEBUG
        OutputDebugString("Can not call ImmSetCompositionString\r\n");
        bRet = TRUE;
#endif
    }

#ifdef DEBUG
    if (!bRet)
        OutputDebugString("ImmSetCompositionString return FALSE\r\n");
#endif

    return 1;
}


STATUS.C

/**********************************************************************/
/*                                                                    */
/*      STATUS.C                                                      */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "commctrl.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"

const char szImeNull[]     = "";
const char szImeOpen[]     = "On";
const char szImeClose[]    = "Off";
const char szImeAlpha[]    = "Alpha";
const char szImeNative[]   = "Native";
const char szImeKatakana[] = "Kata";
const char szImeHalf[]     = "Half";
const char szImeFull[]     = "Full";
const char szImeRoman[]    = "Roman";
const char szImeCode[]     = "Code";
const char szImeHanja[]    = "Hanja";
const char szImeSoftKbd[]  = "SoftKbd";
const char szImeNoConv[]   = "NoConv";
const char szImeEUDC[]     = "EUDC";
const char szImeSymbol[]   = "Symbol";

#define SBITEM_OPENSTATUS    0
#define SBITEM_NATIVEMODE    1
#define SBITEM_FULLSHAPE     2
#define SBITEM_ROMAN         3
#define SBITEM_CHARCODE      4
#define SBITEM_HANJA         5
#define SBITEM_SOFTKBD       6
#define SBITEM_NOCONVERSION  7
#define SBITEM_EUDC          8
#define SBITEM_SYMBOL        9

#define SBITEM_CMODEFIRST    1
#define SBITEM_CMODELAST     9

#define NUM_PARTS 10
int nPartsWidthTbl[NUM_PARTS] = {30,50,30,30,30,30,30,30,30,30};

BOOL CreateStatus(HWND hWnd)
{
    int *pPartsWidth;
    int i;
    int nWidth = 0;

    hWndStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE, NULL ,
                                                   hWnd,  STATUS_ID);

    if (!hWndStatus)
        return FALSE;

    pPartsWidth = (int *)LocalAlloc(LPTR,sizeof(int) * NUM_PARTS);

    for (i = 0; i < NUM_PARTS; i++)
    {
        nWidth += nPartsWidthTbl[i];
        pPartsWidth[i] = nWidth;
    }

    SendMessage(hWndStatus, SB_SETPARTS,
                    (WPARAM)NUM_PARTS,(LPARAM)pPartsWidth);

    LocalFree((HANDLE)pPartsWidth);

    return TRUE;
}

void SetStatusItems(HWND hWnd)
{
    HIMC hIMC;
    BOOL fOpen;
    DWORD dwConvMode,dwSentMode;

    hIMC = ImmGetContext(hWnd);

    fOpen = ImmGetOpenStatus(hIMC);

    SetOpenStatusParts(fOpen);

    ImmGetConversionStatus(hIMC,&dwConvMode,&dwSentMode);

    if (fOpen)
        SetConvModeParts(dwConvMode);
    else
        ClearConvModeParts();

    ImmReleaseContext(hWnd,hIMC);

}

void SetOpenStatusParts(BOOL fOpen)
{
    if (fOpen)
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_OPENSTATUS,(LPARAM)&szImeOpen);
    else
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_OPENSTATUS,(LPARAM)&szImeClose);
}

void SetConvModeParts(DWORD dwConvMode)
{
    switch (dwConvMode & IME_CMODE_LANGUAGE)
    {
        case IME_CMODE_ALPHANUMERIC:
            SendMessage(hWndStatus,SB_SETTEXT,
                    SBITEM_NATIVEMODE,(LPARAM)&szImeAlpha);
            break;

        case IME_CMODE_NATIVE:
            SendMessage(hWndStatus,SB_SETTEXT,
                    SBITEM_NATIVEMODE,(LPARAM)&szImeNative);

            break;

        case (IME_CMODE_NATIVE | IME_CMODE_KATAKANA):
            SendMessage(hWndStatus,SB_SETTEXT,
                    SBITEM_NATIVEMODE,(LPARAM)&szImeKatakana);

            break;

        default:
            SendMessage(hWndStatus,SB_SETTEXT,
                    SBITEM_NATIVEMODE,(LPARAM)&szImeNull);
            break;
    }

    if (dwConvMode & IME_CMODE_ROMAN)
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_ROMAN,(LPARAM)&szImeRoman);
    else
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_ROMAN,(LPARAM)&szImeNull);

    if (dwConvMode & IME_CMODE_CHARCODE)
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_CHARCODE,(LPARAM)&szImeCode);
    else
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_CHARCODE,(LPARAM)&szImeNull);

    if (dwConvMode & IME_CMODE_FULLSHAPE)
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_FULLSHAPE,(LPARAM)&szImeFull);
    else
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_FULLSHAPE,(LPARAM)&szImeHalf);

    if (dwConvMode & IME_CMODE_HANJACONVERT)
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_HANJA,(LPARAM)&szImeHanja);
    else
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_HANJA,(LPARAM)&szImeNull);

    if (dwConvMode & IME_CMODE_SOFTKBD)
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_SOFTKBD,(LPARAM)&szImeSoftKbd);
    else
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_SOFTKBD,(LPARAM)&szImeNull);

    if (dwConvMode & IME_CMODE_NOCONVERSION)
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_NOCONVERSION,(LPARAM)&szImeNoConv);
    else
        SendMessage(hWndStatus,SB_SETTEXT,
                SBITEM_NOCONVERSION,(LPARAM)&szImeNull);

    if (dwConvMode & IME_CMODE_EUDC)
        SendMessage(hWndStatus,SB_SETTEXT,SBITEM_EUDC,(LPARAM)&szImeEUDC);
    else
        SendMessage(hWndStatus,SB_SETTEXT,SBITEM_EUDC,(LPARAM)&szImeNull);

    if (dwConvMode & IME_CMODE_SYMBOL)
        SendMessage(hWndStatus,SB_SETTEXT,SBITEM_SYMBOL,(LPARAM)&szImeSymbol);
    else
        SendMessage(hWndStatus,SB_SETTEXT,SBITEM_SYMBOL,(LPARAM)&szImeNull);
}

void ClearConvModeParts()
{
    int i;

    for (i = SBITEM_CMODEFIRST; i <= SBITEM_CMODELAST; i++)
        SendMessage(hWndStatus,SB_SETTEXT, i,(LPARAM)&szImeNull);

}


SUBS.C

/**********************************************************************/
/*                                                                    */
/*      SUBS.C                                                        */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "commctrl.h"
#include "imm.h"
#include "resource.h"
#include "imeapps.h"

void MoveCompCandWindow(HWND hWnd)
{
    RECT rc,rcClient;
    int cx;

    GetClientRect(hWnd, &rcClient);

    GetWindowRect(hWndToolBar,&rc);
    ScreenToClient(hWnd,(LPPOINT)&rc.right);
    rcClient.top = rc.bottom;

    GetWindowRect(hWndStatus,&rc);
    ScreenToClient(hWnd,(LPPOINT)&rc.left);
    rcClient.bottom = rc.top;

    if (fShowCand)
    {
        if (rcClient.right >= CAND_CX * 3)
            cx = rcClient.right-rcClient.left - CAND_CX;
        else
            cx = (rcClient.right-rcClient.left) * 2 / 3;

        MoveWindow(hWndCompStr,
                   rcClient.left,
                   rcClient.top,
                   cx,
                   rcClient.bottom-rcClient.top,
                   TRUE);
        MoveWindow(hWndCandList,
                   cx,
                   rcClient.top,
                   rcClient.right-rcClient.left - cx,
                   rcClient.bottom-rcClient.top,
                   TRUE);
    }
    else
    {
        MoveWindow(hWndCompStr,
                   rcClient.left,
                   rcClient.top,
                   rcClient.right-rcClient.left,
                   rcClient.bottom-rcClient.top,
                   TRUE);
        MoveWindow(hWndCandList,0,0,0,0,TRUE);
    }

}


TOOLBAR.C

/**********************************************************************/
/*                                                                    */
/*      TOOLBAR.C                                                     */
/*                                                                    */
/*      Copyright (c) 1995-1997  Microsoft Corporation                */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "commctrl.h"
#include "resource.h"
#include "imeapps.h"

// Global Variables for toolbar control.

#define NUMIMAGES       33
#define IMAGEWIDTH      16
#define IMAGEHEIGHT     17
#define BUTTONWIDTH     0
#define BUTTONHEIGHT    0

#define IDTBB_FONT       0
#define IDTBB_OPEN       1
#define IDTBB_CLOSE      2
#define IDTBB_MODE       3
#define IDTBB_SHOWCAND   4
#define IDTBB_NOSHOWCAND 5
#define IDTBB_CONVERT    6
#define IDTBB_CANCEL     7
#define IDTBB_REVERT     8
#define IDTBB_COMPLETE   9
#define IDTBB_NEXTCAND   10
#define IDTBB_PREVCAND   11
#define IDTBB_NEXTCLAUSE 12
#define IDTBB_PREVCLAUSE 13
#define IDTBB_OPENCAND   14
#define IDTBB_CLOSECAND  15
#define IDTBB_ALPHA      16
#define IDTBB_NATIVE     17
#define IDTBB_KATAKANA   18
#define IDTBB_FULL       19
#define IDTBB_HALF       20
#define IDTBB_ROMAN      21
#define IDTBB_NOROMAN    22
#define IDTBB_CHARCODE   23
#define IDTBB_NOCHARCODE 24
#define IDTBB_HANJA      25
#define IDTBB_NOHANJA    26
#define IDTBB_SOFTKBD    27
#define IDTBB_NOSOFTKBD  28
#define IDTBB_EUDC       29
#define IDTBB_NOEUDC     30
#define IDTBB_SYMBOL     31
#define IDTBB_NOSYMBOL   32

TBBUTTON tbButton[] =           // Array defining the toolbar buttons
{
    {IDTBB_FONT, IDM_FONT,          TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},

    {0, 0,                          TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0},

    {IDTBB_OPEN, IDM_OPENSTATUS,    TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},

    {0, 0,                          TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0},

    {IDTBB_NATIVE,   IDM_NATIVEMODE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_FULL,     IDM_FULLHALF,   TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_ROMAN,    IDM_ROMAN,      TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_CHARCODE, IDM_CHARCODE,   TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_HANJA,    IDM_HANJA,      TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_SOFTKBD,  IDM_SOFTKBD,    TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_EUDC,     IDM_EUDC,       TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_SYMBOL,   IDM_SYMBOL,     TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},

    {0, 0,                          TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0},

    {IDTBB_CONVERT,  IDM_CONVERT,   TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_CANCEL,   IDM_CANCEL,    TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_REVERT,   IDM_REVERT,    TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_COMPLETE, IDM_COMPLETE,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},

    {0, 0,                          TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0},

    {IDTBB_NEXTCLAUSE, IDM_NEXTCLAUSE,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_PREVCLAUSE, IDM_PREVCLAUSE,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},

    {0, 0,                          TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0},

    {IDTBB_SHOWCAND, IDM_SHOWCAND,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_OPENCAND, IDM_OPENCAND,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_CLOSECAND,IDM_CLOSECAND, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_NEXTCAND, IDM_NEXTCAND,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {IDTBB_PREVCAND, IDM_PREVCAND,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},

};

BOOL CreateTBar(HWND hWnd)
{
    HIMC hIMC;
    BOOL fOpen;

    hWndToolBar = CreateToolbarEx(hWnd,
                                  WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS,
                                  TOOLBAR_ID,
                                  NUMIMAGES,
                                  hInst,
                                  IDB_BMP,
                                  tbButton,
                                  sizeof(tbButton)/sizeof(TBBUTTON),
                                  BUTTONWIDTH,
                                  BUTTONHEIGHT,
                                  IMAGEWIDTH,
                                  IMAGEHEIGHT,
                                  sizeof(TBBUTTON));

    UpdateShowCandButton();

    hIMC = ImmGetContext(hWndCompStr);
    fOpen = ImmGetOpenStatus(hIMC);
    UpdateShowOpenStatusButton(fOpen);
    ImmReleaseContext(hWndCompStr,hIMC);

    return (hWndToolBar != 0);
}

LRESULT SetTooltipText(HWND hWnd, LPARAM lParam)
{
    LPTOOLTIPTEXT lpToolTipText;
    static char   szBuffer[64];

    lpToolTipText = (LPTOOLTIPTEXT)lParam;
    if (lpToolTipText->hdr.code == TTN_NEEDTEXT)
    {
        LoadString(hInst,
                   lpToolTipText->hdr.idFrom,   // string ID == command ID
                   szBuffer,
                   sizeof(szBuffer));

        lpToolTipText->lpszText = szBuffer;
    }
    return 0;
}

void UpdateButton(UINT iID, UINT iFlags)
{
    int iCurrentFlags;

    iCurrentFlags = (int)SendMessage(hWndToolBar,
                                     TB_GETSTATE,
                                     iID, 0L);

    if (iCurrentFlags & TBSTATE_PRESSED)
        iFlags |= TBSTATE_PRESSED;

    SendMessage(hWndToolBar,
                TB_SETSTATE,
                iID,
                MAKELPARAM(iFlags, 0));
    return;
}

void UpdateShowOpenStatusButton(BOOL fOpen)
{

    SendMessage(hWndToolBar,
                TB_CHANGEBITMAP,
                IDM_OPENSTATUS,
                MAKELPARAM((fOpen?IDTBB_OPEN:IDTBB_CLOSE), 0));
    return;
}

void UpdateShowCandButton()
{

    SendMessage(hWndToolBar,
                TB_CHANGEBITMAP,
                IDM_SHOWCAND,
                MAKELPARAM((fShowCand?IDTBB_SHOWCAND:IDTBB_NOSHOWCAND), 0));
    return;
}

void UpdateModeButton(DWORD dwConv)
{
    int nIDTBB;

    switch (dwConv & IME_CMODE_LANGUAGE)
    {
        case IME_CMODE_ALPHANUMERIC:
            nIDTBB = IDTBB_ALPHA;
            break;

        case IME_CMODE_NATIVE:
            nIDTBB = IDTBB_NATIVE;
            break;

        case (IME_CMODE_NATIVE | IME_CMODE_KATAKANA):
            nIDTBB = IDTBB_KATAKANA;
            break;

        default:
            nIDTBB = IDTBB_ALPHA;
            break;

    }
    SendMessage(hWndToolBar, TB_CHANGEBITMAP, IDM_NATIVEMODE,
                MAKELPARAM(nIDTBB, 0));

    if (dwConv & IME_CMODE_FULLSHAPE)
        nIDTBB = IDTBB_FULL;
    else
        nIDTBB = IDTBB_HALF;
    SendMessage(hWndToolBar, TB_CHANGEBITMAP, IDM_FULLHALF,
                MAKELPARAM(nIDTBB, 0));

    if (dwConv & IME_CMODE_ROMAN)
        nIDTBB = IDTBB_ROMAN;
    else
        nIDTBB = IDTBB_NOROMAN;
    SendMessage(hWndToolBar, TB_CHANGEBITMAP, IDM_ROMAN,
                MAKELPARAM(nIDTBB, 0));

    if (dwConv & IME_CMODE_CHARCODE)
        nIDTBB = IDTBB_CHARCODE;
    else
        nIDTBB = IDTBB_NOCHARCODE;
    SendMessage(hWndToolBar, TB_CHANGEBITMAP, IDM_CHARCODE,
                MAKELPARAM(nIDTBB, 0));

    if (dwConv & IME_CMODE_HANJACONVERT)
        nIDTBB = IDTBB_HANJA;
    else
        nIDTBB = IDTBB_NOHANJA;
    SendMessage(hWndToolBar, TB_CHANGEBITMAP, IDM_HANJA,
                MAKELPARAM(nIDTBB, 0));

    if (dwConv & IME_CMODE_SOFTKBD)
        nIDTBB = IDTBB_SOFTKBD;
    else
        nIDTBB = IDTBB_NOSOFTKBD;
    SendMessage(hWndToolBar, TB_CHANGEBITMAP, IDM_SOFTKBD,
                MAKELPARAM(nIDTBB, 0));

    if (dwConv & IME_CMODE_EUDC)
        nIDTBB = IDTBB_EUDC;
    else
        nIDTBB = IDTBB_NOEUDC;
    SendMessage(hWndToolBar, TB_CHANGEBITMAP, IDM_EUDC,
                MAKELPARAM(nIDTBB, 0));

    if (dwConv & IME_CMODE_SYMBOL)
        nIDTBB = IDTBB_SYMBOL;
    else
        nIDTBB = IDTBB_NOSYMBOL;
    SendMessage(hWndToolBar, TB_CHANGEBITMAP, IDM_SYMBOL,
                MAKELPARAM(nIDTBB, 0));

    return;
}


TOOLBAR.H

// Global variable for Toolbar window
extern HWND hWndToolbar;

// Function protoptype for Creating Toolbar


WAPI.H

/* Unicode/Ansi stubs */
#ifdef USEWAPI
typedef LPWSTR            LPMYSTR;
typedef WCHAR             MYCHAR;
#define MYTEXT(x)         L ## x
#define Mylstrlen(x)      lstrlenW(x)
#define Mylstrcpy(x, y)   MylstrcpyW((x), (y))
#define Mylstrcat(x, y)   MylstrcatW((x), (y))
#define Mylstrcmp(x, y)   MylstrcmpW((x), (y))
#define MyCharPrev(x, y)  MyCharPrevW((x), (y))
#define MyCharNext(x)     MyCharNextW(x)
#define LPMYIMEMENUITEMINFO LPIMEMENUITEMINFOW
#define MYIMEMENUITEMINFO IMEMENUITEMINFOW
#define MyImmGetImeMenuItems  ImmGetImeMenuItemsW
#define MyImmGetCompositionString ImmGetCompositionStringW
#else
typedef LPTSTR            LPMYSTR;
typedef TCHAR             MYCHAR;
#define MYTEXT(x)         x
#define Mylstrlen(x)      lstrlen(x)
#define Mylstrcpy(x, y)   lstrcpy((x), (y))
#define Mylstrcat(x, y)   lstrcat((x), (y))
#define Mylstrcmp(x, y)   lstrcmp((x), (y))
#define MyCharPrev(x, y)  AnsiPrev((x), (y))
#define MyCharNext(x)     AnsiNext(x)
#define LPMYIMEMENUITEMINFO LPIMEMENUITEMINFO
#define MYIMEMENUITEMINFO IMEMENUITEMINFO
#define MyImmGetImeMenuItems  ImmGetImeMenuItems
#define MyImmGetCompositionString ImmGetCompositionString
#endif

#ifdef USEWAPI
int PASCAL MylstrcmpW(LPWSTR lp0, LPWSTR lp1);
int PASCAL MylstrcpyW(LPWSTR lp0, LPWSTR lp1);
int PASCAL MylstrcatW(LPWSTR lp0, LPWSTR lp1);
LPWSTR PASCAL MyCharPrevW(LPWSTR lpStart, LPWSTR lpCur);
LPWSTR PASCAL MyCharNextW(LPWSTR lp);
#endif


WSUBS.C

#include "windows.h"

#ifdef USEWAPI
int PASCAL MylstrcmpW(LPWSTR lp0, LPWSTR lp1)
{

    while(*lp0)
    {
        if (*lp0 != *lp1)
        {
            return (-1);
        }
        lp0++;
        lp1++;

    }
    if (!*lp1)
        return 0;

    return 1;
}
int PASCAL MylstrcpyW(LPWSTR lp0, LPWSTR lp1)
{
    int n = 0;

    while(*lp1)
    {
        *lp0 = *lp1;
        lp0++;
        lp1++;
        n++;
    }
    *lp0 = *lp1;
    return n;
}
int PASCAL MylstrcatW(LPWSTR lp0, LPWSTR lp1)
{
    int n = 0;

    while(*lp0)
        lp0++;

    return MylstrcpyW(lp0, lp1);
}
LPWSTR PASCAL MyCharPrevW(LPWSTR lpStart, LPWSTR lpCur)
{
    LPWSTR lpRet = lpStart;
    if (lpCur > lpStart)
        lpRet = lpCur - 1;

    return lpRet;
}
LPWSTR PASCAL MyCharNextW(LPWSTR lp)
{
    return lp++;
}
#endif


DATA.C

/**********************************************************************/
/*                                                                    */
/*      DATA.C                                                        */
/*                                                                    */
/*      Copyright (c) 1995-1996  Microsoft Corporation                     */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "resource.h"

HANDLE hInst;
HWND hWndMain;

HWND hWndDef1;
HWND hWndDef2;
HWND hWndIMC1;
HWND hWndIMC2;
HWND hWndIMCUI1;
HWND hWndIMCUI2;


DLGS.C

/**********************************************************************/
/*                                                                    */
/*      DLGS.C                                                        */
/*                                                                    */
/*      Copyright (c) 1995-1996  Microsoft Corporation                     */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "resource.h"
#include "multiui.h"

/**********************************************************************/
/*                                                                    */
/*    About(HWND, UINT, WPARAM, LPARAM)                               */
/*                                                                    */
/**********************************************************************/
BOOL FAR PASCAL AboutDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WPARAM wParam;
LPARAM lParam;
{
    switch (message) {
        case WM_INITDIALOG:
            return (TRUE);

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


MULTIUI.C

/**********************************************************************/
/*                                                                    */
/*      MULTIUI.C                                                     */
/*                                                                    */
/*      Copyright (c) 1995-1996  Microsoft Corporation                     */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "resource.h"
#include "multiui.h"

/**********************************************************************/
/*                                                                    */
/*    WinMain(HANDLE, HANDLE, LPSTR, int)                             */
/*                                                                    */
/**********************************************************************/
int APIENTRY WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;

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

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

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

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

    wc.style         = (UINT)NULL;
    wc.lpfnWndProc   = MainWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(hInstance,"MyIcon");
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = "MultiUiMenu";
    wc.lpszClassName = "MultiUiWClass";

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

    wc.style         = CS_DBLCLKS;
    wc.lpfnWndProc   = NoUINoIMCWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = WNDEXTRA_NOUINOIMC;
    wc.hInstance     = hInstance;
    wc.hIcon         = 0;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "NoUINoIMCWClass";

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

    wc.style         = CS_DBLCLKS;
    wc.lpfnWndProc   = NoUIOwnIMCWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = WNDEXTRA_NOUIOWNIMC;
    wc.hInstance     = hInstance;
    wc.hIcon         = 0;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "NoUIOwnIMCWClass";

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

    wc.style         = CS_DBLCLKS;
    wc.lpfnWndProc   = OwnUIOwnIMCWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = WNDEXTRA_OWNUIOWNIMC;
    wc.hInstance     = hInstance;
    wc.hIcon         = 0;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "OwnUIOwnIMCWClass";

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

    return TRUE;
}

/**********************************************************************/
/*                                                                    */
/*    InitInstance(HANDLE, int)                                       */
/*                                                                    */
/**********************************************************************/
BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    RECT rc;
    int iDesc = 0;
    HDC hIC;
    TEXTMETRIC tm;
    HFONT hFont = GetDefaultGUIFont();

    char szTitle[20] =  "MultiUi TestTool";

    hInst = hInstance;

    if (!(hWndMain = CreateWindow(
    "MultiUiWClass", (LPSTR)szTitle,
    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, NULL, hInstance, NULL)))
return FALSE;

    GetClientRect(hWndMain,&rc);

    rc.right  /= 2;
    rc.bottom /= 3;

    hIC = CreateIC("DISPLAY", NULL, NULL, NULL);

    SelectObject(hIC, hFont);

    GetTextMetrics(hIC,&tm);

    if (rc.bottom >= tm.tmHeight)
        iDesc = tm.tmHeight + tm.tmExternalLeading;

    DeleteDC(hIC);

    if (!(hWndDef1 = CreateWindowEx(WS_EX_CLIENTEDGE, "NoUINoIMCWClass", "",
            WS_CHILD|WS_VISIBLE,
    rc.left, rc.top + iDesc, rc.right, rc.bottom - iDesc,
    hWndMain, NULL, hInstance, NULL)))
return FALSE;

    if (!(hWndDef2 = CreateWindowEx(WS_EX_CLIENTEDGE, "NoUINoIMCWClass", "",
            WS_CHILD|WS_VISIBLE,
    rc.right, rc.top + iDesc, rc.right, rc.bottom - iDesc,
    hWndMain, NULL, hInstance, NULL)))
return FALSE;

    if (!(hWndIMC1 = CreateWindowEx(WS_EX_CLIENTEDGE, "NoUIOwnIMCWClass", "",
            WS_CHILD|WS_VISIBLE,
    rc.left, rc.bottom + iDesc, rc.right, rc.bottom - iDesc,
    hWndMain, NULL, hInstance, NULL)))
return FALSE;

    if (!(hWndIMC2 = CreateWindowEx(WS_EX_CLIENTEDGE, "NoUIOwnIMCWClass", "",
            WS_CHILD|WS_VISIBLE,
    rc.right, rc.bottom + iDesc, rc.right, rc.bottom - iDesc,
    hWndMain, NULL, hInstance, NULL)))
return FALSE;

    if (!(hWndIMCUI1 = CreateWindowEx(WS_EX_CLIENTEDGE, "OwnUIOwnIMCWClass", "",
            WS_CHILD|WS_VISIBLE,
    rc.left, rc.bottom * 2 + iDesc, rc.right, rc.bottom - iDesc,
    hWndMain, NULL, hInstance, NULL)))
return FALSE;

    if (!(hWndIMCUI2 = CreateWindowEx(WS_EX_CLIENTEDGE, "OwnUIOwnIMCWClass", "",
            WS_CHILD|WS_VISIBLE,
    rc.right, rc.bottom * 2 + iDesc, rc.right, rc.bottom - iDesc,
    hWndMain, NULL, hInstance, NULL)))
return FALSE;

    /* display each windows */
    ShowWindow (hWndMain, nCmdShow);
    UpdateWindow (hWndMain);

    return TRUE;

}

/**********************************************************************/
/*                                                                    */
/*    MainWndProc(HWND, UINT, WPARAM, LPARAM)                         */
/*                                                                    */
/**********************************************************************/
long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
    PAINTSTRUCT ps;
    HDC  hDC;
    FARPROC lpProc;
    RECT rc;
    int iDesc = 0;
    HDC hIC;
    TEXTMETRIC tm;
    HFONT hFont;
    HFONT hOldFont;
    HIMC hIMC;
    HWND hIMEWnd;
    char szDesc[128];

    switch (message) {
case WM_CREATE:
    break;

case WM_SIZE:
            switch (wParam)
            {
                case SIZENORMAL:
                case SIZEFULLSCREEN:
                    GetClientRect(hWndMain,&rc);
                    rc.right  /= 2;
                    rc.bottom /= 3;

                    hFont = GetDefaultGUIFont();
                    hIC = CreateIC("DISPLAY", NULL, NULL, NULL);

                    SelectObject(hIC, hFont);

                    GetTextMetrics(hIC,&tm);

                    iDesc = 0;
                    if (rc.bottom >= tm.tmHeight)
                        iDesc = tm.tmHeight + tm.tmExternalLeading;

                    DeleteDC(hIC);

                    MoveWindow(hWndDef1,
                               rc.left,rc.top + iDesc,
                               rc.right,rc.bottom - iDesc,TRUE);
                    MoveWindow(hWndDef2,
                               rc.right,rc.top + iDesc,
                               rc.right,rc.bottom - iDesc,TRUE);
                    MoveWindow(hWndIMC1,
                               rc.left,rc.bottom + iDesc,
                               rc.right,rc.bottom - iDesc,TRUE);
                    MoveWindow(hWndIMC2,
                               rc.right,rc.bottom + iDesc,
                               rc.right,rc.bottom - iDesc,TRUE);
                    MoveWindow(hWndIMCUI1,
                               rc.left,rc.bottom * 2 + iDesc,
                               rc.right,rc.bottom - iDesc,TRUE);
                    MoveWindow(hWndIMCUI2,
                               rc.right,rc.bottom * 2 + iDesc,
                               rc.right,rc.bottom - iDesc,TRUE);

                    InvalidateRect(hWnd,NULL,TRUE);
                    break;

                case SIZEICONIC:
            return (DefWindowProc(hWnd, message, wParam, lParam));
                    break;

            }
    break;

case WM_PAINT:
    hDC = BeginPaint (hWnd, &ps);
            hFont = GetDefaultGUIFont();
            hOldFont = SelectObject(hDC, hFont);

            GetClientRect(hWndMain,&rc);
            rc.right  /= 2;
            rc.bottom /= 3;

            wsprintf(szDesc,"%08lX Default IMC and Default IME window"
                     ,(DWORD)hWndDef1);
            TextOut(hDC, rc.left,rc.top,szDesc,lstrlen(szDesc));

            wsprintf(szDesc,"%08lX Default IMC and Default IME window"
                     ,(DWORD)hWndDef2);
            TextOut(hDC, rc.right,rc.top,szDesc,lstrlen(szDesc));

            hIMC = (HIMC)GetWindowLong(hWndIMC1,MYGWL_IMC);
            wsprintf(szDesc,"%08lX IMC[%08lX] and Default IME window"
                     ,(DWORD)hWndIMC1, (DWORD)hIMC);
            TextOut(hDC, rc.left,rc.bottom,szDesc,lstrlen(szDesc));

            hIMC = (HIMC)GetWindowLong(hWndIMC2,MYGWL_IMC);
            wsprintf(szDesc,"%08lX IMC[%08lX] and Default IME window"
                     ,(DWORD)hWndIMC2, (DWORD)hIMC);
            TextOut(hDC, rc.right,rc.bottom,szDesc,lstrlen(szDesc));

            hIMC = (HIMC)GetWindowLong(hWndIMCUI1,MYGWL_IMC);
            hIMEWnd = (HWND)GetWindowLong(hWndIMCUI1,MYGWL_IMEWND);
            wsprintf(szDesc,"%08lX IMC[%08lX] and IME window[%08lX]"
                     ,(DWORD)hWndIMCUI1, (DWORD)hIMC ,(DWORD)hIMEWnd);
            TextOut(hDC, rc.left,rc.bottom * 2,szDesc,lstrlen(szDesc));

            hIMC = (HIMC)GetWindowLong(hWndIMCUI2,MYGWL_IMC);
            hIMEWnd = (HWND)GetWindowLong(hWndIMCUI2,MYGWL_IMEWND);
            wsprintf(szDesc,"%08lX IMC[%08lX] and IME window[%08lX]"
                     ,(DWORD)hWndIMCUI2, (DWORD)hIMC ,(DWORD)hIMEWnd);
            TextOut(hDC, rc.right,rc.bottom * 2,szDesc,lstrlen(szDesc));

            SelectObject(hDC, hOldFont);
    EndPaint (hWnd, &ps);
    break;

case WM_COMMAND:
    switch(WMCOMMANDWPARAM(wParam))
    {
case IDM_ABOUT:
    lpProc = MakeProcInstance(AboutDlg, hInst);
    DialogBox(hInst, "ABOUTBOX", hWnd, (DLGPROC)lpProc);
    FreeProcInstance(lpProc);
    break;

    }
    break;

case WM_DESTROY:
    PostQuitMessage(0);
    break;

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


MULTIUI.H

/**********************************************************************/
/*                                                                    */
/*      MULTCON.H  - Dispatch IME Sub Task                            */
/*                                                                    */
/*      Copyright (c) 1995-1996  Microsoft Corporation                     */
/*                                                                    */
/**********************************************************************/

#define WMCOMMANDWPARAM(w) LOWORD(w)

#define DEFIMC1     0
#define DEFIMC2     1
#define IMC1        2
#define IMC2        3
#define IMCUI1      4
#define IMCUI2      5

#define MAXSIZE_STR             1024

#define WNDEXTRA_NOUINOIMC      8
#define WNDEXTRA_NOUIOWNIMC     12
#define WNDEXTRA_OWNUIOWNIMC    16

#define MYGWL_FONT   0
#define MYGWL_LPSTR  4
#define MYGWL_IMC    8
#define MYGWL_IMEWND 12

extern HANDLE hInst;
extern HWND hWndMain;

extern HWND hWndDef1;
extern HWND hWndDef2;
extern HWND hWndIMC1;
extern HWND hWndIMC2;
extern HWND hWndIMCUI1;
extern HWND hWndIMCUI2;

/* multiui.c */
int  APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);
long FAR PASCAL MainWndProc(HWND, UINT, WPARAM, LPARAM);

/* dlgs.c     */
BOOL FAR PASCAL AboutDlg(HWND, UINT, WPARAM, LPARAM);

/* multiwnd.c */
long FAR PASCAL NoUINoIMCWndProc(HWND, UINT, WPARAM, LPARAM);
long FAR PASCAL NoUIOwnIMCWndProc(HWND, UINT, WPARAM, LPARAM);
long FAR PASCAL OwnUIOwnIMCWndProc(HWND, UINT, WPARAM, LPARAM);
HFONT GetDefaultGUIFont(void);


MULTIWND.C

/**********************************************************************/
/*                                                                    */
/*      MULTIWND.C                                                    */
/*                                                                    */
/*      Copyright (c) 1995-1996  Microsoft Corporation                     */
/*                                                                    */
/**********************************************************************/

#include "windows.h"
#include "imm.h"
#include "resource.h"
#include "multiui.h"

/**********************************************************************/
/*                                                                    */
/*    InitStringBuffer(HWND)                                          */
/*                                                                    */
/**********************************************************************/
void FAR PASCAL InitStringBuffer(hWnd)
HWND hWnd;
{
    LPSTR lpStr = NULL;

    if (lpStr = GlobalAlloc(GPTR,MAXSIZE_STR))
    {
        SetWindowLong(hWnd,MYGWL_LPSTR,(LONG)lpStr);
    }
    else
    {
        SetWindowLong(hWnd,MYGWL_LPSTR,(LONG)0L);
    }
}

/**********************************************************************/
/*                                                                    */
/*    DestroyStringBuffer(HWND)                                       */
/*                                                                    */
/**********************************************************************/
void FAR PASCAL DestroyStringBuffer(hWnd)
HWND hWnd;
{
    LPSTR lpStr = NULL;

    lpStr = (LPSTR)GetWindowLong(hWnd,MYGWL_LPSTR);
    if (lpStr)
    {
        GlobalFree((HANDLE)lpStr);
    }
}

/**********************************************************************/
/*                                                                    */
/*    MyDrawString(HWND,WPARAM,LPARAM)                                */
/*                                                                    */
/**********************************************************************/
void FAR PASCAL MyDrawString(hWnd,wParam,lParam)
HWND hWnd;
WPARAM wParam;
LPARAM lParam;
{
    LPSTR lpStr = NULL;
    HDC   hDC;
    PAINTSTRUCT ps;
    HFONT hOldFont;
    HFONT hFont;
    DWORD dwHeight;
    TEXTMETRIC      tm;
    RECT rc;

    lpStr = (LPSTR)GetWindowLong(hWnd,MYGWL_LPSTR);
    if (!lpStr)
        return;

    hFont = (HFONT)GetWindowLong(hWnd,MYGWL_FONT);
    if (!hFont)
        return;

    hDC = BeginPaint(hWnd, &ps);
    hOldFont = SelectObject(hDC, hFont);

    lpStr = (LPSTR)GetWindowLong(hWnd,MYGWL_LPSTR);
    // Get dwHeight.
    GetTextMetrics(hDC,&tm);
    dwHeight = tm.tmHeight + tm.tmExternalLeading;

    GetClientRect(hWnd,&rc);
    rc.bottom -= dwHeight;

    DrawText(hDC,lpStr,-1,&rc,DT_LEFT | DT_WORDBREAK);

    SelectObject(hDC, hOldFont);
    EndPaint(hWnd, &ps);
}

/**********************************************************************/
/*                                                                    */
/*    MySetCompositionFont(HWND)                                      */
/*                                                                    */
/**********************************************************************/
void FAR PASCAL MySetCompositionFont(hWnd)
HWND hWnd;
{
    HIMC hIMC;

    if (hIMC = ImmGetContext(hWnd))
    {
        LOGFONT lf;
        HFONT   hFont = (HFONT)GetWindowLong(hWnd,MYGWL_FONT);
        GetObject(hFont,sizeof(lf),&lf);

        ImmSetCompositionFont(hIMC,&lf);
        ImmReleaseContext(hWnd, hIMC);
    }
}

/**********************************************************************/
/*                                                                    */
/*    MySetCompositionForm(HWND)                                      */
/*                                                                    */
/**********************************************************************/
void FAR PASCAL MySetCompositionForm(hWnd)
HWND hWnd;
{
    RECT            rc;
    HIMC            hIMC;
    HDC             hDC;
    TEXTMETRIC      tm;
    DWORD           dwHeight;
    HFONT           hFont = (HFONT)GetWindowLong(hWnd,MYGWL_FONT);
    HFONT           hOldFont;
    COMPOSITIONFORM CompForm;

    if (!hFont)
        return;

    if (hIMC = ImmGetContext(hWnd))
    {
        // Get dwHeight.
        hDC = GetDC(hWnd);
        hOldFont = SelectObject(hDC, hFont);
        GetTextMetrics(hDC,&tm);
        dwHeight = tm.tmHeight + tm.tmExternalLeading;
        SelectObject(hDC, hOldFont);
        ReleaseDC(hWnd,hDC);

        GetClientRect(hWnd,&rc);
        CompForm.dwStyle = CFS_POINT;
        CompForm.ptCurrentPos.x = rc.left;
        CompForm.ptCurrentPos.y = rc.bottom - dwHeight;
        ImmSetCompositionWindow(hIMC,&CompForm);

        ImmReleaseContext(hWnd, hIMC);
    }
}

/**********************************************************************/
/*                                                                    */
/*    MyChangeFont(HWND)                                              */
/*                                                                    */
/**********************************************************************/
void FAR PASCAL MyChangeFont(hWnd)
HWND hWnd;
{
    LOGFONT lf;
    CHOOSEFONT cf;
    HFONT hFont;

    /* Set all structure fields to zero. */
    memset(&cf, 0, sizeof(CHOOSEFONT));

    cf.lStructSize = sizeof(CHOOSEFONT);
    cf.hwndOwner = hWnd;
    cf.lpLogFont = &lf;
    cf.Flags = CF_SCREENFONTS | CF_EFFECTS;
    cf.rgbColors = RGB(0, 255, 255); /* light blue */
    cf.nFontType = SCREEN_FONTTYPE;

    if (ChooseFont(&cf))
    {
        hFont = (HFONT)GetWindowLong(hWnd,MYGWL_FONT);
        DeleteObject(hFont);

        hFont = CreateFontIndirect(&lf);
        SetWindowLong(hWnd,MYGWL_FONT,(LONG)hFont);
        MySetCompositionForm(hWnd);
        MySetCompositionFont(hWnd);
    }
    SetFocus(hWnd);
}

/**********************************************************************/
/*                                                                    */
/*    GetDefaultGUIFont(void)                                         */
/*                                                                    */
/**********************************************************************/
HFONT GetDefaultGUIFont(void)
{
    static HFONT hFont;
    OSVERSIONINFO VerInfo;

    if (hFont)
        return hFont;

    VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&VerInfo);

    if (VerInfo.dwMajorVersion < 4) {
        NONCLIENTMETRICS ncm;

        ncm.cbSize = sizeof(NONCLIENTMETRICS);
        SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
        hFont = CreateFontIndirect(&ncm.lfStatusFont);

        if (!hFont)
            hFont = GetStockObject(SYSTEM_FONT);
    } else {
        hFont = GetStockObject(DEFAULT_GUI_FONT);
    }

    return hFont;
}

/**********************************************************************/
/*                                                                    */
/*    OverFlowText(HWND,LPSTR,LPSTR)                                  */
/*                                                                    */
/**********************************************************************/
BOOL FAR PASCAL OverFlowText(hWnd,lpStr,lpResultStr)
HWND hWnd;
LPSTR lpStr;
LPSTR lpResultStr;
{
    BOOL            bRet = FALSE;
    RECT            rc;
    HDC             hDC;
    TEXTMETRIC      tm;
    int             nHeight;
    HFONT           hFont = (HFONT)GetWindowLong(hWnd,MYGWL_FONT);
    HFONT           hOldFont;
    LPSTR           lpTemp;

    if (!hFont)
        return FALSE;

    if (lstrlen(lpStr) + lstrlen(lpResultStr) > MAXSIZE_STR)
    {
        bRet = TRUE;

    }
    else
    {
        lpTemp = GlobalAlloc(GPTR,MAXSIZE_STR);

        if (lpTemp)
        {
            lstrcpy(lpTemp,lpStr);
            lstrcat(lpTemp,lpResultStr);

            GetClientRect(hWnd,&rc);

            hDC = GetDC(hWnd);
            hOldFont = SelectObject(hDC, hFont);
            GetTextMetrics(hDC,&tm);

            rc.bottom -= tm.tmHeight + tm.tmExternalLeading;
            nHeight = rc.bottom;

            if (nHeight < DrawText(hDC,lpTemp,-1,&rc,DT_LEFT | DT_WORDBREAK | DT_CALCRECT))
            {
                bRet = TRUE;
            }

            SelectObject(hDC, hOldFont);
            ReleaseDC(hWnd,hDC);

            GlobalFree((HANDLE)lpTemp);
        }
    }

    return bRet;
}

/**********************************************************************/
/*                                                                    */
/*    AddResultString(HWND)                                           */
/*                                                                    */
/**********************************************************************/
void FAR PASCAL AddResultString(hWnd)
HWND hWnd;
{
    HIMC  hIMC;
    DWORD dwLen;
    LPSTR lpStr;
    LPSTR lpResultStr;

    lpStr = (LPSTR)GetWindowLong(hWnd,MYGWL_LPSTR);

    hIMC = ImmGetContext(hWnd);

    if (!hIMC)
        return;

    dwLen = ImmGetCompositionString(hIMC,GCS_RESULTSTR,NULL,0L);

    if (dwLen)
    {
        lpResultStr = GlobalAlloc(GPTR,dwLen+1);

        if (lpResultStr)
        {
            ImmGetCompositionString(hIMC,GCS_RESULTSTR,
                                                  lpResultStr,dwLen+1);

            if (OverFlowText(hWnd,lpStr,lpResultStr))
                lstrcpy(lpStr,lpResultStr);
            else
                lstrcat(lpStr,lpResultStr);

            InvalidateRect(hWnd,NULL,TRUE);

            GlobalFree((HANDLE)lpResultStr);
        }
    }

    ImmReleaseContext(hWnd,hIMC);

}

/**********************************************************************/
/*                                                                    */
/*    NoUINoIMCWndProc(HWND, UINT, WPARAM, LPARAM)                    */
/*                                                                    */
/**********************************************************************/
long FAR PASCAL NoUINoIMCWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
    HFONT hFont;

    switch (message)
    {
case WM_CREATE:
            hFont = GetDefaultGUIFont();
            SetWindowLong(hWnd,MYGWL_FONT,(LONG)hFont);

            InitStringBuffer(hWnd);

    break;

        case WM_LBUTTONDBLCLK:
            MyChangeFont(hWnd);
    break;

        case WM_LBUTTONDOWN:
            SetFocus(hWnd);
    break;

        case WM_SETFOCUS:
            MySetCompositionFont(hWnd);
            MySetCompositionForm(hWnd);
    break;

        case WM_IME_COMPOSITION:
            if (lParam & GCS_RESULTSTR)
            {
                AddResultString(hWnd);
            }
    return (DefWindowProc(hWnd, message, wParam, lParam));
    break;

        case WM_SIZE:
            switch (wParam)
            {
                case SIZENORMAL:
                case SIZEFULLSCREEN:
                    if (hWnd == GetFocus())
                    {
                        MySetCompositionForm(hWnd);
                    }
                    InvalidateRect(hWnd,NULL,TRUE);
                    break;

                case SIZEICONIC:
            return (DefWindowProc(hWnd, message, wParam, lParam));
                    break;
            }
    break;

case WM_PAINT:
            MyDrawString(hWnd,wParam,lParam);
    break;

case WM_DESTROY:
            hFont = (HFONT)GetWindowLong(hWnd,MYGWL_FONT);
            DeleteObject(hFont);

            DestroyStringBuffer(hWnd);

    break;

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

}

/**********************************************************************/
/*                                                                    */
/*    NoUIOwnIMCWndProc(HWND, UINT, WPARAM, LPARAM)                   */
/*                                                                    */
/**********************************************************************/
long FAR PASCAL NoUIOwnIMCWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
    HIMC hIMC;
    HFONT hFont;

    switch (message)
    {
case WM_CREATE:
            hFont = GetDefaultGUIFont();
            SetWindowLong(hWnd,MYGWL_FONT,(LONG)hFont);

            hIMC = ImmCreateContext();
            ImmAssociateContext(hWnd,hIMC);

            // Save hIMC into WndExtra.
            SetWindowLong(hWnd,MYGWL_IMC,(LONG)hIMC);

            MySetCompositionFont(hWnd);
            MySetCompositionForm(hWnd);

            InitStringBuffer(hWnd);

    break;

        case WM_LBUTTONDBLCLK:
            MyChangeFont(hWnd);
    break;

        case WM_LBUTTONDOWN:
            SetFocus(hWnd);
    break;

        case WM_IME_COMPOSITION:
            if (lParam & GCS_RESULTSTR)
            {
                AddResultString(hWnd);
            }
    return (DefWindowProc(hWnd, message, wParam, lParam));
    break;

        case WM_SIZE:
            switch (wParam)
            {
                case SIZENORMAL:
                case SIZEFULLSCREEN:
                    MySetCompositionForm(hWnd);
                    InvalidateRect(hWnd,NULL,TRUE);
                    break;

                case SIZEICONIC:
            return (DefWindowProc(hWnd, message, wParam, lParam));
                    break;
            }
    break;

case WM_PAINT:
            MyDrawString(hWnd,wParam,lParam);
    break;

case WM_DESTROY:
            hFont = (HFONT)GetWindowLong(hWnd,MYGWL_FONT);
            DeleteObject(hFont);

            hIMC = (HIMC)GetWindowLong(hWnd,MYGWL_IMC);
            ImmDestroyContext(hIMC);

            DestroyStringBuffer(hWnd);

    break;

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

}

/**********************************************************************/
/*                                                                    */
/*    OwnUIOwnIMCWndProc(HWND, UINT, WPARAM, LPARAM)                  */
/*                                                                    */
/**********************************************************************/
long FAR PASCAL OwnUIOwnIMCWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
    HFONT hFont;
    HIMC  hIMC = 0L;
    HWND  hIMEWnd = (HWND)GetWindowLong(hWnd,MYGWL_IMEWND);

    if (IsWindow(hIMEWnd) && ImmIsUIMessage(hIMEWnd,message,wParam,lParam))
    {
        switch (message)
        {
            case WM_IME_COMPOSITION:
                if (lParam & GCS_RESULTSTR)
                {
                    AddResultString(hWnd);
                }
        break;

            default:
        break;
        }

return 0L;
    }

    switch (message)
    {
case WM_CREATE:
            hFont = GetDefaultGUIFont();
            SetWindowLong(hWnd,MYGWL_FONT,(LONG)hFont);

            hIMC = ImmCreateContext();
            ImmAssociateContext(hWnd,hIMC);

            // Save hIMC into WndExtra.
            SetWindowLong(hWnd,MYGWL_IMC,(LONG)hIMC);

            if (!(hIMEWnd = CreateWindow("Ime", "",
                    WS_POPUP | WS_DISABLED,
            0,0,0,0,
            hWnd, NULL, hInst, NULL)))
        return -1;

            // Save hIMEWnd into WndExtra.
            SetWindowLong(hWnd,MYGWL_IMEWND,(LONG)hIMEWnd);

            MySetCompositionFont(hWnd);
            MySetCompositionForm(hWnd);

            InitStringBuffer(hWnd);

    break;

        case WM_LBUTTONDBLCLK:
            MyChangeFont(hWnd);
    break;

        case WM_LBUTTONDOWN:
            SetFocus(hWnd);
    break;

        case WM_SIZE:
            switch (wParam)
            {
                case SIZENORMAL:
                case SIZEFULLSCREEN:
                    MySetCompositionForm(hWnd);
                    InvalidateRect(hWnd,NULL,TRUE);
                    break;

                case SIZEICONIC:
            return (DefWindowProc(hWnd, message, wParam, lParam));
                    break;
            }
    break;

case WM_PAINT:
            MyDrawString(hWnd,wParam,lParam);
    break;

case WM_DESTROY:
            hFont = (HFONT)GetWindowLong(hWnd,MYGWL_FONT);
            DeleteObject(hFont);

            hIMC = (HIMC)GetWindowLong(hWnd,MYGWL_IMC);
            ImmDestroyContext(hIMC);

            DestroyStringBuffer(hWnd);

    break;

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

}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by multcon.RC
//
#define IDM_ABOUT                        40003

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40002
#define _APS_NEXT_CONTROL_VALUE         1003
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

mousinfo sample

Demonstrate the use of the new wheel-mouse, and the new TrackMouseEvent API.


MOUSINFO.C

///////////////////////////////////////////////////////////////////////////
//
// MODULE:      MOUSINFO.C
//
// DESCRIPTION: SDK sample for handling the new WM_MOUSEWHEEL message and
//              the new TrackMouseEvent() API.
//
//              Applet displays MouseButton, MouseWheel, MouseMovement, and
//              any mouse messages in the title bar.
//
// PLATFORMS:   WinNT 4.0
//
//              Copyright 1995 - 1998 Microsoft Corporation.
//                          All rights reserved
//
//
///////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include "mousinfo.h"

// Globals
HINSTANCE ghInst;

//
// Array of Mouse Message Strings
//
char garMsgStrings[][20] = {
    "                   ",
    "WM_LBUTTONUP       ",
    "WM_RBUTTONUP       ",
    "WM_MBUTTONUP       ",
    "WM_LBUTTONDOWN     ",
    "WM_RBUTTONDOWN     ",
    "WM_MBUTTONDOWN     ",
    "WM_LBUTTONDBLCLK   ",
    "WM_MBUTTONDBLCLK   ",
    "WM_RBUTTONDBLCLK   ",
    "WM_MOUSEWHEEL      ",
    "WM_MOUSEHOVER      ",
    "WM_MOUSELEAVE      "
    };

//////////////////////////////////////////////////////////////////////////
//
// FUNCTION:int WINAPI WinMain(HANDLE,HANDLE,LPSTR,int)
//
// DESCRIPTION:Your basic WinMain()
//
/////////////////////////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR  lpCmdLine,
                    int    nCmdShow)
{
    MSG msg;
    WNDCLASS  wc;
    HWND hwndMain;

    ghInst = hInstance;

    //
    // Register our main window, only if first instance
    //

    if( !hPrevInstance )
    {
        wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
        wc.lpfnWndProc = (WNDPROC)MainWndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = ghInst;
        wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(MOUSINFO_ICON));
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = GetStockObject(WHITE_BRUSH);
        wc.lpszMenuName =  MAKEINTRESOURCE(IDM_MENU);
        wc.lpszClassName = szClassName;

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

  //
  // Create the main window...
  //  with an initial size of 0
  //

  hwndMain = CreateWindow(
      szClassName,
      szAppName,
      WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_THICKFRAME,
      0,0,0,0,
      NULL, NULL, ghInst, NULL );

  //
  // Show the window
  //
  ShowWindow(hwndMain,SW_SHOW);
  UpdateWindow(hwndMain);

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

  return (msg.wParam);
}

//////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION:LRESULT WINAPI MainWndProc (HWND,UINT,WPARAM,LPARAM)
//
// DESCRIPTION:Your standard Window Procedure.
//
// MESSAGE:Handles the following Windows messages:
//
//WM_CREATE
//WM_DESTROY
//WM_SYSCOMMAND
//WM_LBUTTONUP
//WM_RBUTTONUP
//WM_MBUTTONUP
//WM_LBUTTONDOWN
//WM_RBUTTONDOWN
//WM_MBUTTONDOWN
//WM_LBUTTONDBLCLK
//WM_MBUTTONDBLCLK
//WM_RBUTTONDBLCLK
//WM_MOUSEMOVE
//
//And the new mouse message
//
//WM_MOUSEWHEEL
//
//              And the new TrackMouseEvent() Messages
//
//              WM_MOUSEHOVER       // mouse hovered specified delay over client area
//              WM_MOUSELEAVE       // mouse has left the client area
//
//
//////////////////////////////////////////////////////////////////////////////////
LRESULT WINAPI MainWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    TEXTMETRIC tm;
    HDC hDC;

    int MsgIndex = NO_MESSAGE;          // Index into garMsgStrings[] array
  char szBuff[128];                   // String buffer
    static int LB=0,RB=0,MB=0;          // State of the mouse buttons
    static int X=0,Y=0;                 // Mouse position
    static char Roller=' ';             // State of the mouse wheel. '+' or '-'
    static int fHasWheel=FALSE;         // Mouse has a wheel?? TRUE if yes, FALSE if no

    switch (message)
    {

        case WM_CREATE:
    //
            // See if we have a mouse with a wheel attached to the system.
// Note that SM_MOUSEWHEELPRESENT is a new flag for GetSystemMetrics
//  and returns TRUE if a mouse with a wheel is present, FALSE if not.
//
            fHasWheel = GetSystemMetrics( SM_MOUSEWHEELPRESENT );

            //
            // Size the window so that it will display all the text
            //
            hDC = GetDC(hwnd);
            GetTextMetrics(hDC,&tm);
            SetWindowPos(hwnd,HWND_BOTTOM,0,0,tm.tmMaxCharWidth * MAX_TITLE_LEN,100,SWP_SHOWWINDOW);
            ReleaseDC(hwnd, hDC);

return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

  //
        // Handle WM_MOUSE messages.
        // First we setup up some variables and then let
        //  control fall through to the end of the switch
        //  construct, where we then update the title bar.
        //
        case WM_LBUTTONUP:    LB=0; MsgIndex = LBU; break;
case WM_RBUTTONUP:      RB=0; MsgIndex = RBU; break;
case WM_MBUTTONUP:      MB=0; MsgIndex = MBU; break;
case WM_LBUTTONDOWN:LB=1; MsgIndex = LBD; break;
case WM_RBUTTONDOWN:    RB=1; MsgIndex = RBD; break;
case WM_MBUTTONDOWN:MB=1; MsgIndex = MBD; break;
case WM_LBUTTONDBLCLK:  LB=0; MsgIndex = LB2; break;
case WM_MBUTTONDBLCLK:  MB=0; MsgIndex = MB2; break;
case WM_RBUTTONDBLCLK:  RB=0; MsgIndex = RB2; break;
        case WM_MOUSEHOVER:           MsgIndex = MH;  break;
        case WM_MOUSELEAVE:           MsgIndex = ML;  break;

        case WM_MOUSEMOVE:MsgIndex=NO_MESSAGE; Roller = ' ';  break;
        //
        // Handle WM_MOUSEWHEEL message.
        // We handle this message a little differently since the
        //  roller movement info is in the HIWORD of wParam.
        //
        // The MouseWheel has 18 'detents'. As the wheel is rolled
        //  and a detent is encountered, the OS will send a WM_MOUSEWHEEL
        //  message with the HIWORD of wParam set to a value of +/- 120.
        //  '+' if the wheel is being rolled forward (away from the user),
        //  '-' if the wheel is being rolled backwards (towards the user).
        case WM_MOUSEWHEEL:
            //
            // Mouse Wheel is being rolled forward
            //
            if( (short)HIWORD(wParam) > 0 )
                Roller = '+';

            //
            // Mouse Wheel is being rolled backward
            //
            if( (short)HIWORD(wParam) < 0 )
                Roller = '-';

            MsgIndex = MW;
            break;

        case WM_COMMAND:
            switch(wParam)
            {
                //
                // Call the TrackMouseEvent() API, and set up a WM_MOUSEHOVER
                //  "one-shot" event. Exactly one and only one  WM_MOUSEHOVER
                //  message will be sent to the window specified in the hwndTrack
                //  member of the TRACKMOUSEEVENT structure, when the mouse has
                //  'hovered' over the client area an amount of time equal to that
                //  specified in the dwHoverTime member of TRACKMOUSEEVENT.
                //
                // NOTE that this message will be generated only once. The application
                //  must call the TrackMouseEvent() API again in order for the system
                //  to generate another WM_MOUSEHOVER message.
                //
                case IDM_HOVER:
                {
                    TRACKMOUSEEVENT tme;

                    tme.cbSize      = sizeof(TRACKMOUSEEVENT);
                    tme.dwFlags     = TME_HOVER;
                    tme.hwndTrack   = hwnd;
                    tme.dwHoverTime = HOVER_DEFAULT;

                    TrackMouseEvent(&tme);

                    return 0;
                }

                //
                // Call the TrackMouseEvent() API, and set up a WM_MOUSELEAVE
                //  "one-shot" event. Exactly one and only one  WM_MOUSELEAVE
                //  message will be sent to the window specified in the hwndTrack
                //  member of the TRACKMOUSEEVENT structure, when the mouse has
                //  left the client area.
                //
                // NOTE that this message will be generated only once. The application
                //  must call the TrackMouseEvent() API again in order for the system
                //  to generate another WM_MOUSELEAVE message. Also note that if the
                //  mouse pointer is not over the application, a call to TrackMouseEvent()
                //  will result in the immediate posting of a WM_MOUSELEAVE
                //  message.
                //
                case IDM_LEAVE:
                {
                    TRACKMOUSEEVENT tme;

                    tme.cbSize      = sizeof(TRACKMOUSEEVENT);
                    tme.dwFlags     = TME_LEAVE;
                    tme.hwndTrack   = hwnd;

                    TrackMouseEvent(&tme);

                    return 0;
                }

                case IDM_ABOUT:
                {
                    char buff[128];

                    wsprintf(buff,"MousInfo.Exe\r\n"
                                  "Copyright 1995-96, Microsoft Corp.\r\n"
                                  "All rights reserved\r\n\r\n" );

                    MessageBox(hwnd,buff,szAppName,MB_OK);
                    return 0;
                }

            }
            break;

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

    }  // end switch (message)

//
    // If the mouse has a wheel, display the state of the mouse buttons,
    //   wheel and position in the title bar.
    //
    if( fHasWheel )
        wsprintf(szBuff,"L:%d M:%d R:%d W:%c x:%04d y:%04d %s",
      LB,MB,RB,Roller,
                  LOWORD(lParam),HIWORD(lParam),
                  garMsgStrings[MsgIndex] );
    //
    // Else if mouse does not have a wheel, do not display the state
    //  of the wheel (but do display everything else!).
    //
    else
        wsprintf(szBuff,"L:%d M:%d R:%d x:%04d y:%04d %s",
      LB,MB,RB,LOWORD(lParam),HIWORD(lParam),
                  garMsgStrings[MsgIndex] );

    SetWindowText(hwnd,szBuff);

    return (DefWindowProc(hwnd, message, wParam, lParam));

}


MOUSINFO.H

///////////////////////////////////////////////////////////////////////////
//
// MODULE:      MOUSINFO.H
//
// DESCRIPTION: Include file for the MousInfo.Exe Tool
//
//
//              Copyright 1995 - 1998 Microsoft Corporation.
//                       All rights reserved
//
//
///////////////////////////////////////////////////////////////////////////

char szAppName[] = "MousInfo";
char szClassName[] = "MousInfoClass";

// Maximum # of chars in title bar
#define MAX_TITLE_LEN 44

// Index into Button Message Array
#define NO_MESSAGE  0
#define LBU         1   // Left Button Up
#define RBU         2   // Right Button Up
#define MBU         3   // Middle Button Up
#define LBD         4   // Left Button Down
#define RBD         5   // Right Button Down
#define MBD         6   // Middle Button Down
#define LB2         7   // Left Button Double Click
#define MB2         8   // Right Button Double Click
#define RB2         9   // Middle Button Double Click
#define MW          10  // Mouse Wheel
#define MH          11  // Mouse Hover
#define ML          12  // Mouse Leave

// Resource defines
#define MOUSINFO_ICON 102

#define IDM_MENU    0x0900

#define IDM_TRACK   0x1000
#define IDM_HOVER   0x1001
#define IDM_LEAVE   0x1002

#define IDM_ABOUT   0x2000

// Function Proto's
intWINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
LRESULT WINAPI MainWndProc(HWND, UINT, WPARAM, LPARAM);


MENU.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  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 APIENTRY WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
    )
{

    HWND  hWnd;
    MSG msg;                                 /* message                      */

    UNREFERENCED_PARAMETER( lpCmdLine );

    /* Register main window class if this is the first instance of the app. */
    if (!hPrevInstance)
        if (!MenuInit (hInstance))
            return 0;

    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 0;

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

    while (GetMessage (&msg, NULL, 0, 0)){
        /* 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 (HANDLE hInstance)
{
    HANDLE    hMemory;
    PWNDCLASS pWndClass;
    BOOL      bSuccess;

    /* Initialize the menu window class */
    hMemory   = LocalAlloc(LPTR, sizeof(WNDCLASS));
    if(!hMemory){
        MessageBox(NULL, "<MenuInit> Not enough memory.", NULL, MB_OK | MB_ICONHAND);
        return(FALSE);
    }

    pWndClass = (PWNDCLASS) LocalLock(hMemory);

    pWndClass->style         = 0;
    pWndClass->lpfnWndProc   = (WNDPROC) 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 APIENTRY About(
        HWND hDlg,
        UINT message,
        UINT wParam,
        LONG lParam)

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

        case WM_COMMAND:
            // LOWORD added for portability
            if (LOWORD(wParam) == IDOK){
                EndDialog(hDlg,0);
                return(TRUE);
            }
            break;
    }
    return(FALSE);
        UNREFERENCED_PARAMETER(lParam);
}

/****************************************************************************
 *                                                                          *
 *  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 APIENTRY ShrinkBitmap (
        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 APIENTRY HandleCreate (HWND hwnd)
{
    HMENU   hMenu;
    HMENU   hWndMenu;

    /* 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, (DWORD)hMenu, "States");

    return;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : HandlePaint ( hwnd )                                       *
 *                                                                          *
 *  PURPOSE    : Handles the repainting of the main app's client area.      *
 *                                                                          *
 ****************************************************************************/
VOID APIENTRY HandlePaint (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 APIENTRY HandleChangeColors(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 APIENTRY HandleDrawItem(
        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);

        }
    }
        UNREFERENCED_PARAMETER(hwnd);
}

/****************************************************************************
 *                                                                          *
 *  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 APIENTRY HandlePopupMenu (
        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 APIENTRY MenuWndProc (
        HWND hWnd,
        UINT message,
        UINT wParam,
        LONG lParam)

{
    HMENU hMenu;
    RECT rc;
    POINT pt;

    switch (message){
        case WM_SYSCOMMAND:
            /* Show the About ... dialog */
            if (wParam == ID_ABOUT){
                DialogBox (hInst,
                           "AboutBox",
                           hWnd,
                           About);

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

        case WM_COMMAND:
            // LOWORD added for portability
            switch (LOWORD(wParam)){
                 case IDM_EXIT:
                   DestroyWindow (hWnd);
                   break;

                 case IDM_ABOUT:
                   /* Bring up the About.. dialog box */
                   DialogBox (hInst,
                              "AboutBox",
                              hWnd,
                              About);

                   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, (WORD)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, (WORD)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);

            // Temporary porting macro
            LONG2POINT(lParam, pt);
            if (PtInRect ((LPRECT)&rc, pt))
                HandlePopupMenu (hWnd, pt);
            break;

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


MENU.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// Porting macro
#ifdef WIN32
#define LONG2POINT(l, pt)  ((pt).x = (SHORT)LOWORD(l), (pt).y = (SHORT)HIWORD(l))
#else
#define LONG2POINT(l, pt)  ((pt).x = (INT)LOWORD(l), (pt).y = (INT)HIWORD(l))
#endif

/* Defines for top-level menu positions */
#define IDFILE_POS     0
#define IDCOLORS_POS   1
#define IDSTATES_POS   2
#define IDHELP_POS     3

#define MEASUREITEMWIDTH  40
#define MEASUREITEMHEIGHT 40

/* Dialog ID for the About.. dialog */
#define ID_ABOUT 100

/* Defines for menu items */
#define IDM_EXIT   202
#define IDM_ABOUT  203

/* Colors menu IDs */
#define IDM_COLOROWNERDR 500
#define IDM_BLACK        501
#define IDM_BLUE         502
#define IDM_RED          503
#define IDM_GREEN        504

/* Floating popup menu IDs */
#define IDM_FONT         600
#define IDM_SIZE         601
#define IDM_STYLE        602

/* States menu IDs */
#define IDM_STATE1       610
#define IDM_STATE2       611
#define IDM_STATE3       612
#define IDM_STATE4       613

/* BitBlt Raster operation */
#define ROP_PSDPxax     0x00B8074AL

BOOL NEAR PASCAL MenuInit(HANDLE);
LONG APIENTRY MenuWndProc(HWND, UINT, UINT, LONG);
BOOL APIENTRY About(HWND, UINT, UINT, LONG);


README.TXT

                       Readme for owncombo.exe

The owncombo.exe example application illustrates the use of functions
and messages for combo boxes and owner draw.  The user interface of
this example application is fairly self-explanatory except, perhaps,
for the text combo box dialog.  This readme provides an explanation
of this portion of the application's user interface, to help you better
interpret the source code.

The text combo box dialog buttons send various messages to the combo box
and the edit control.  These buttons allow the user to vary the data sent
with each message.

The following actions are performed by the buttons:

Unsl All:  This button unselects any selection in the combo box.

Sel No:    This button takes an integer value from the edit control and
           attempts to select an entry in the combo box given this index
           value.

Sel Txt:   This button takes a text string from the edit control and
           attempts to select the item with the given text prefix.

Find Txt:  This button searches for the text given in the edit control
           and returns the item number where it was found in the combo
           box.

Cb Dir:    This appends a directory listing of the current directory
           into the combo box.

Clr It:    This clears all the items in the combo box.

Add It:    This takes the string given in the edit control and adds it
           to the combo box.

Del It:    This deletes the currently selected item from the combo box.

Cpy It:    This copies the currently selected item in the combo box to
           the edit control.

Combo Notifications Check Box:

           When this box is checked, a message box will appear showing
           what notification codes the combo box is returning to the app in
           response to the messages sent by the buttons.


OWNCOMBO.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  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 APIENTRY WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
    )
{

    HWND  hWnd;
    MSG   msg;

    UNREFERENCED_PARAMETER( lpCmdLine );

    if (!hPrevInstance)
        if (!OwnComboInit (hInstance))
            return (0);

    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 (0);

    ShowWindow (hWnd, nCmdShow);

    while (GetMessage (&msg, NULL, 0, 0)){
        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 (HANDLE hInstance)
{
    HANDLE     hMemory;
    PWNDCLASS  pWndClass;
    BOOL       bSuccess;

    /* Allocate for and fill class structure. */
    hMemory = LocalAlloc (LPTR, sizeof (WNDCLASS));
    if(!hMemory){
        MessageBox(NULL, "<OwnComboInit> Not enough memory.", NULL, MB_OK | MB_ICONHAND);
        return(FALSE);
    }

    pWndClass = (PWNDCLASS) LocalLock (hMemory);

    pWndClass->style         = 0;
    pWndClass->lpfnWndProc   = (WNDPROC) 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 APIENTRY About (
    HWND         hDlg,
    UINT     message,
    UINT         wParam,
    LONG         lParam)
{

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

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK){
                EndDialog (hDlg,0);
                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 APIENTRY OwnComboWndProc (
    HWND         hWnd,
    UINT     message,
    UINT         wParam,
    LONG         lParam)
{
    switch (message){
        case WM_COMMAND:
            switch (LOWORD(wParam)){
                case IDM_EXIT:
                    DestroyWindow (hWnd);
                    break;

                case IDM_ABOUT:
                    /* Bring up the about box */
                    DialogBox (hInst,
                               "AboutBox",
                               hWnd,
                               About);

                    break;

                case IDM_LISTBOX:
                    /* Bring up the list box example */
                    DialogBox (hInst,
                               "ListBoxDialog",
                               hWnd,
                               About);
                    break;

                case IDM_MULTILISTBOX:
                    /* Bring up the multiple selection list box example */
                    DialogBox (hInst,
                               "MultiListBoxDialog",
                               hWnd,
                               About);
                    break;

                case IDM_COMBOBOX:
                    /* Bring up the combo box example */
                    DialogBox (hInst,
                               "ComboBoxDialog",
                               hWnd,
                               About);
                    break;

                case IDM_OWNERCOMBOBOX:
                    /* Bring up the owner-draw dropdown list box example */
                    DialogBox (hInst,
                               "OwnerComboBoxDialog",
                               hWnd,
                               About);
                    break;
            }
            break;

        case WM_DESTROY:
            PostQuitMessage (0);
            break;

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

/****************************************************************************
 *                                                                          *
 *  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 APIENTRY HandleSelectionState(
        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 APIENTRY HandleFocusState(
        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 APIENTRY DrawEntireItem(
        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 APIENTRY ListBoxExample (
    HWND hDlg,
    UINT message,
    UINT wParam,
    LONG lParam)
{
    LPDRAWITEMSTRUCT    lpdis;
    LPMEASUREITEMSTRUCT lpmis;

    /* Vars for WM_DRAWITEM */

    switch (message){
        case WM_COMMAND:
            switch (LOWORD(wParam)){
                case IDOK:
                   EndDialog (hDlg, 0);
                   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, 0);
            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 APIENTRY ComboBoxExample(
    HWND hDlg,
    UINT message,
    UINT 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 (LOWORD(wParam)){
                case IDOK:
                    EndDialog (hDlg,0);
                    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, 0, 0L);
                    if (wIndex == CB_ERR)
                        MessageBox (hDlg, (LPSTR)"No Selection", NULL, MB_OK);
                    else
                        SendMessage (hWndCombo, CB_SETCURSEL, (WPARAM)-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,
                                                (WPARAM)-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,(WPARAM)-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, 0);
            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 APIENTRY OwnerComboBoxExample (
    HWND hDlg,
    UINT message,
    UINT wParam,
    LONG lParam)
{
    LPDRAWITEMSTRUCT    lpdis;
    LPMEASUREITEMSTRUCT lpmis;

    switch (message){
        case WM_COMMAND:
            switch (LOWORD(wParam)){
                case IDOK:
                   EndDialog (hDlg, 0);
                   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, 0);
            return(TRUE);
            break;

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


OWNCOMBO.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/* Defines for menu items */
#define IDM_EXIT    201
#define IDM_ABOUT   202

#define IDM_LISTBOX       300
#define IDM_MULTILISTBOX  301
#define IDM_COMBOBOX      302
#define IDM_OWNERCOMBOBOX 303

#define IDM_HELP          400

/* Defines for dialog box ownerdraw controls. */
#define ID_LISTBOX  1000
#define ID_BLACK    1003
#define ID_WHITE    1004
#define ID_RED      1005
#define ID_BLUE     1006
#define ID_GREEN    1007

/* Defines for combo box example */
#define ID_COMBOBOX     1000
#define ID_SINGLEEDIT   1001
#define ID_TEXT1        1002
#define ID_TEXT2        1003
#define ID_STEPSBOX     1004
#define ID_UNSLBUTTON   1005
#define ID_NUMSELBUTTON 1006
#define ID_TXTSELBUTTON 1007
#define ID_FNDSELBUTTON 1008
#define ID_CLRBUTTON    1009
#define ID_ADDBUTTON    1010
#define ID_DELETEBUTTON 1011
#define ID_CPYBUTTON    1012
#define ID_CBDIRBUTTON  1013

LONG FAR  PASCAL OwnComboWndProc(HWND, UINT, UINT, LONG);
BOOL NEAR PASCAL OwnComboInit(HANDLE);
BOOL FAR  PASCAL About(HWND, UINT, UINT, LONG);
BOOL FAR  PASCAL ListBoxExample(HWND, UINT, UINT, LONG);
BOOL FAR  PASCAL ComboBoxExample(HWND, UINT, UINT, LONG);
BOOL FAR  PASCAL OwnerComboBoxExample(HWND, UINT, UINT, LONG);


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by wizard.RC
//
#define IDS_WORKHABITS                  1
#define IDS_WORKHABITS2                 2
#define IDS_WORKHABITS3                 3
#define IDS_WORKHABITS4                 4
#define IDS_TEAMWORK                    5
#define IDS_TEAMWORK2                   6
#define IDS_TEAMWORK3                   7
#define IDS_TEAMWORK4                   8
#define IDS_RELIABILITY                 9
#define IDS_RELIABILITY2                10
#define IDS_RELIABILITY3                11
#define IDS_RELIABILITY4                12
#define IDS_GOALS                       13
#define IDS_GOALS2                      14
#define IDS_GOALS3                      15
#define IDS_GOALS4                      16
#define IDS_ADAPTATION                  17
#define IDS_ADAPTATION2                 18
#define IDS_ADAPTATION3                 19
#define IDS_ADAPTATION4                 20
#define IDD_WORKHABITS                  101
#define IDD_TEAMWORK                    102
#define IDD_RELIABILITY                 103
#define IDD_GOALS                       104
#define IDD_ADAPTATION                  105
#define IDD_INFO                        106
#define IDC_WORKHAB1                    1000
#define IDC_WORKHAB2                    1001
#define IDC_WORKHAB3                    1002
#define IDC_WORKHAB4                    1003
#define IDC_TEAMWORK1                   1004
#define IDC_TEAMWORK2                   1005
#define IDC_TEAMWORK3                   1006
#define IDC_TEAMWORK4                   1007
#define IDC_RELIABILITY1                1008
#define IDC_RELIABILITY2                1009
#define IDC_RELIABILITY3                1010
#define IDC_RELIABILITY4                1011
#define IDC_GOALS1                      1012
#define IDC_GOALS2                      1013
#define IDC_GOALS3                      1014
#define IDC_GOALS4                      1015
#define IDC_ADAPTATION1                 1016
#define IDC_ADAPTATION2                 1017
#define IDC_ADAPTATION3                 1018
#define IDC_ADAPTATION4                 1019
#define IDE_NAME                        1020
#define IDE_TITLE                       1021
#define IDE_PROJECT                     1022
#define IDE_DEPARTMENT                  1023
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        107
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1024
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


WIZARD.C

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM:Wizard.c
//
//PURPOSE:Demonstrates how to manipulate a wizard control
//
//PLATFORMS:Windows 95
//
//FUNCTIONS:
//WinMain() - calls initialization function, processes message loop
//InitApplication() - Initializes window data nd registers window
//InitInstance() -saves instance handle and creates main window
//MainWindProc() Processes messages
//About() - Process messages for "About" dialog box
//YourInfo() - Process the "Your Information" Wizard page
//WorkHabits() - Processes the "Work Habits" Wizard page
//TeamWork() - Processes the "Team Work" Wizard page
//Reliability() - Processes the "Reliability" Wizard page
//Goals() - Processes the "Goal Attainment" Wizard page
// Adaptation() - Processes the "Adaptability to Change" Wizard page
//FillInPropertyPage() - Fills in a PROPSHEETPAGE structure
//CreateWizard() - Creates the wizard
//GenerateReview() - Generates the resulting review
//
//SPECIAL INSTRUCTIONS: N/A
//

#include <windows.h>    // includes basic windows functionality
#include <string.h>     // includes the string functions
#include <prsht.h>      // includes the property sheet functionality
#include "resource.h"   // includes the definitions for the resources
#include "wizard.h"     // includes the application-specific information

REVIEWINFO rvInfo;      // a structure containing the review information
HWND hwndEdit;          // handle to the main MLE
TCHAR lpReview[MAX_BUF]; // Buffer for the review

//
//
//   FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//
//   PURPOSE: Main entry point for the application.
//
//   COMMENTS:
//
//This function calls the initialization functions and processes
//the main message loop.
//
int APIENTRY WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
MSG msg;

      // save off the current instance
rvInfo.hInst = hInstance;

      // if the initialization fails, return.
if (!InitApplication(hInstance))
return (FALSE);

// 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,
NULL,
0,
0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (msg.wParam);

}

//
//
//   FUNCTION: InitApplication(HANDLE)
//
//   PURPOSE: Initializes window data and registers window class
//
//   COMMENTS:
//
//This function registers the window class for the main window.
//
BOOL InitApplication(HANDLE hInstance)
{
        WNDCLASS  wcSample;

// Fill in window class structure with parameters that describe the
// main window.

        wcSample.style = 0;
        wcSample.lpfnWndProc = (WNDPROC)MainWndProc;
        wcSample.cbClsExtra = 0;
        wcSample.cbWndExtra = 0;
        wcSample.hInstance = hInstance;
        wcSample.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(EXE_ICON));
        wcSample.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcSample.hbrBackground = GetStockObject(WHITE_BRUSH);
        wcSample.lpszMenuName =  TEXT("WizardMenu");
        wcSample.lpszClassName = TEXT("SampleWClass");

        return (RegisterClass(&wcSample));

}

//
//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Creates the main window.
//
//   COMMENTS: N/A
//
//
BOOL InitInstance(
HANDLE          hInstance,
int             nCmdShow)
{
HWND hWndMain;

hWndMain = CreateWindow(
                TEXT("SampleWClass"),
                TEXT("Wizard Sample"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);

/* If window could not be created, return "failure" */
if (!hWndMain)
return (FALSE);

/* Make the window visible; update its client area; and return "success" */
ShowWindow(hWndMain, nCmdShow);
UpdateWindow(hWndMain);
return (TRUE);

}

//
//   FUNCTION: MainWndProc(HWND, UINT, UINT, LONG)
//
//  PURPOSE:  Processes messages for the main window procedure
//
//    MESSAGES:
//
//WM_CREATE - creates the main MLE for the window
//WM_COMMAND - processes the menu commands for the application
//WM_SIZE - sizes the MLE to fill the client area of the window
//WM_DESTROY - posts a quit message and returns
//
LONG APIENTRY MainWndProc(
HWND hWnd,                // window handle
UINT message,             // type of message
UINT wParam,              // additional information
LONG lParam)              // additional information
{

switch (message)
{
        case WM_CREATE:
// Create an MLE for the file contents.
hwndEdit = CreateWindow(
                TEXT("EDIT"),
NULL,
                WS_CHILD | WS_VISIBLE | ES_WANTRETURN |
                    ES_MULTILINE | ES_AUTOVSCROLL,
                0, 0, 0, 0,
                hWnd,
                (HMENU) ID_EDITCHILD,
                (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE),
                NULL);

            // Update the MLE.
            SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM) "Choose Performance Review Wizard from the Options menu.");
            return 0;

        case WM_SIZE:
            // Make the edit control the size of the window's client area.
            MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            return 0;

        case WM_COMMAND:
            switch( LOWORD( wParam ))
{
                case IDM_WIZARD:
                    CreateWizard(hWnd, rvInfo.hInst);

// Update the MLE.
    if (SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM) lpReview) != TRUE)
MessageBox( hWnd, TEXT("Text not set!"), NULL, MB_OK);

break;

case IDM_EXIT:
PostQuitMessage(0);
break;

case IDM_ABOUT:
DialogBox(rvInfo.hInst, TEXT("AboutBox"), hWnd, (DLGPROC)About);
break;

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

}
break;

case WM_DESTROY:                  /* message: window being destroyed */
PostQuitMessage(0);
break;

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

//
//  FUNCTION: About(HWND, UINT, UINT, LONG)
//
//  PURPOSE:  Processes messages for the "About" dialog box
//
//  MESSAGES:
//
//WM_INITDIALOG - initalizes the dialog box
//WM_COMMAND - processes the input
//
BOOL APIENTRY About(
   HWND hDlg,
UINT message,
UINT wParam,
LONG lParam)
{
switch (message)
{
   case WM_INITDIALOG:
  return TRUE;

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

default:
return FALSE;
}

}

//
//  FUNCTION: YourInfo(HWND, UINT, UINT, LONG)
//
//  PURPOSE:  Processes messages for "Your Information" page
//
//  MESSAGES:
//
//WM_INITDIALOG - intializes the page
//WM_NOTIFY - processes the notifications sent to the page
//
BOOL APIENTRY YourInfo(
HWND hDlg,
UINT message,
UINT wParam,
LONG lParam)
{

switch (message)
{
case WM_INITDIALOG:
lstrcpy(rvInfo.pszName, TEXT(""));
lstrcpy(rvInfo.pszTitle, TEXT(""));
lstrcpy(rvInfo.pszProject, TEXT(""));
lstrcpy(rvInfo.pszDepartment, TEXT(""));

break;

case WM_NOTIFY:
    switch (((NMHDR FAR *) lParam)->code)
    {

  case PSN_KILLACTIVE:
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
return 1;
break;

case PSN_RESET:
// reset to the original values
lstrcpy(rvInfo.pszName, TEXT(""));
lstrcpy(rvInfo.pszTitle,TEXT(""));
lstrcpy(rvInfo.pszProject, TEXT(""));
lstrcpy(rvInfo.pszDepartment, TEXT(""));
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
break;

 case PSN_SETACTIVE:
    PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT);
SendMessage(GetDlgItem(hDlg,0x3024 ), BM_SETSTYLE, (WPARAM)BS_PUSHBUTTON, MAKELONG(FALSE, 0));
//SendMessage(GetParent(hDlg), DM_SETDEFID, (WPARAM)IDC_BUTTON1, 0);
SendMessage(GetDlgItem(hDlg, IDE_NAME), WM_SETTEXT, 0, (LPARAM)rvInfo.pszName);
SendMessage(GetDlgItem(hDlg, IDE_TITLE), WM_SETTEXT, 0, (LPARAM)rvInfo.pszTitle);
SendMessage(GetDlgItem(hDlg, IDE_PROJECT), WM_SETTEXT, 0, (LPARAM)rvInfo.pszProject);
SendMessage(GetDlgItem(hDlg, IDE_DEPARTMENT), WM_SETTEXT, 0, (LPARAM)rvInfo.pszDepartment);
break;

                case PSN_WIZNEXT:
// the Next button was pressed
 SendDlgItemMessage(hDlg, IDE_NAME, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM) rvInfo.pszName);
SendDlgItemMessage(hDlg, IDE_TITLE, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)rvInfo.pszTitle);
SendDlgItemMessage(hDlg, IDE_PROJECT, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)rvInfo.pszProject);
SendDlgItemMessage(hDlg, IDE_DEPARTMENT, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)rvInfo.pszDepartment);
     break;

default:
return FALSE;

    }
break;

default:
return FALSE;
}
return TRUE;
}

//
//  FUNCTION: WorkHabits(HWND, UINT, UINT, LONG)
//
//  PURPOSE:  Processes messages for "Work Habits" page
//
//  MESSAGES:
//
//WM_INITDIALOG - intializes the page
//WM_NOTIFY - processes the notifications sent to the page
//WM_COMMAND - saves the id of the choice selected
//
BOOL APIENTRY WorkHabits(
HWND hDlg,
UINT message,
UINT wParam,
LONG lParam)
{

switch (message)
{
case WM_INITDIALOG:
rvInfo.iWorkHabits = 0;
break;

case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
rvInfo.iWorkHabits = LOWORD(wParam);
CheckRadioButton( hDlg, IDC_WORKHAB1,IDC_WORKHAB4, LOWORD(wParam));
}
break;

case WM_NOTIFY:
    switch (((NMHDR FAR *) lParam)->code)
    {

  case PSN_KILLACTIVE:
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
return 1;
break;

case PSN_RESET:
// rest to the original values
rvInfo.iWorkHabits = 0;
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
break;

 case PSN_SETACTIVE:
    PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT | PSWIZB_BACK);
if (rvInfo.iWorkHabits)
SendMessage(GetDlgItem(hDlg, rvInfo.iWorkHabits), BM_SETCHECK, 1, 0L);
break;

                case PSN_WIZNEXT:
// get the selected radio button
     break;

default:
return FALSE;

    }
break;

default:
return FALSE;
}
return TRUE;
}

//
//  FUNCTION: TeamWork(HWND, UINT, UINT, LONG)
//
//  PURPOSE:  Processes messages for "Team Work" page
//
//  MESSAGES:
//
//WM_INITDIALOG - intializes the page
//WM_NOTIFY - processes the notifications sent to the page
//WM_COMMAND - saves the id of the choice selected
//
//
BOOL APIENTRY TeamWork(
HWND hDlg,
UINT message,
UINT wParam,
LONG lParam)
{

switch (message)
{
case WM_INITDIALOG:
rvInfo.iTeamWork = 0;
break;

case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
rvInfo.iTeamWork = LOWORD(wParam);
CheckRadioButton( hDlg, IDC_TEAMWORK1,IDC_TEAMWORK4, LOWORD(wParam));
}
break;
case WM_NOTIFY:
    switch (((NMHDR FAR *) lParam)->code)
    {

  case PSN_KILLACTIVE:
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
return 1;
break;

case PSN_RESET:
// rest to the original values
rvInfo.iTeamWork = 0;
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
break;

 case PSN_SETACTIVE:
if (rvInfo.iTeamWork)
SendMessage(GetDlgItem(hDlg, rvInfo.iTeamWork), BM_SETCHECK, 1, 0L);
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK | PSWIZB_NEXT);
break;

                case PSN_WIZBACK:
                    break;

                case PSN_WIZNEXT:
                    break;

default:
return FALSE;

    }
break;

default:
return FALSE;

}
return TRUE;
}
//
//  FUNCTION: Reliability(HWND, UINT, UINT, LONG)
//
//  PURPOSE:  Processes messages for "Reliability" page
//
//  MESSAGES:
//
//WM_INITDIALOG - intializes the page
//WM_NOTIFY - processes the notifications sent to the page
//WM_COMMAND - saves the id of the choice selected
//
BOOL APIENTRY Reliability(
HWND hDlg,
UINT message,
UINT wParam,
LONG lParam)
{

switch (message)
{
case WM_INITDIALOG:
rvInfo.iReliability = 0;
break;

case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
rvInfo.iReliability = LOWORD(wParam);
CheckRadioButton( hDlg, IDC_RELIABILITY1,IDC_RELIABILITY4, LOWORD(wParam));
}
break;

case WM_NOTIFY:
    switch (((NMHDR FAR *) lParam)->code)
    {

  case PSN_KILLACTIVE:
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
return 1;
break;

case PSN_RESET:
// rest to the original values
rvInfo.iReliability = 0;
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
break;

 case PSN_SETACTIVE:
if (rvInfo.iReliability)
SendMessage(GetDlgItem(hDlg, rvInfo.iReliability), BM_SETCHECK, 1, 0L);
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK | PSWIZB_NEXT);
break;

                case PSN_WIZBACK:
                    break;

                case PSN_WIZNEXT:
                    break;

default:
return FALSE;
    }
break;

default:
return FALSE;
}
return TRUE;
}
//
//  FUNCTION: Goals(HWND, UINT, UINT, LONG)
//
//  PURPOSE:  Processes messages for "Goal Attainment" page
//
//  MESSAGES:
//
//WM_INITDIALOG - intializes the page
//WM_NOTIFY - processes the notifications sent to the page
//WM_COMMAND - saves the id of the choice selected
//
BOOL APIENTRY Goals(
HWND hDlg,
UINT message,
UINT wParam,
LONG lParam)
{

switch (message)
{
case WM_INITDIALOG:
rvInfo.iGoals = 0;
break;

case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
rvInfo.iGoals = LOWORD(wParam);
CheckRadioButton( hDlg, IDC_GOALS1, IDC_GOALS4, LOWORD(wParam));
}
break;

case WM_NOTIFY:
    switch (((NMHDR FAR *) lParam)->code)
    {

  case PSN_KILLACTIVE:
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
return 1;
break;

case PSN_RESET:
// rest to the original values
rvInfo.iGoals = 0;
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
break;

 case PSN_SETACTIVE:
if (rvInfo.iGoals)
SendMessage(GetDlgItem(hDlg, rvInfo.iGoals), BM_SETCHECK, 1, 0L);
 PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK | PSWIZB_NEXT);
break;

                case PSN_WIZBACK:
                    break;

                case PSN_WIZNEXT:
                    break;

default:
return FALSE;

    }
break;

default:
return FALSE;
}
return TRUE;
}
//
//  FUNCTION: Adaptation(HWND, UINT, UINT, LONG)
//
//  PURPOSE:  Processes messages for "Addaptability to Change" page
//
//  MESSAGES:
//
//WM_INITDIALOG - intializes the page
//WM_NOTIFY - processes the notifications sent to the page
//WM_COMMAND - saves the id of the choice selected
//
//
BOOL APIENTRY Adaptation(
HWND hDlg,
UINT message,
UINT wParam,
LONG lParam)
{

switch (message)
{
case WM_INITDIALOG:
rvInfo.iAdaptation = 0;
break;

case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
            {
rvInfo.iAdaptation = LOWORD(wParam);
CheckRadioButton( hDlg, IDC_ADAPTATION1, IDC_ADAPTATION4, LOWORD(wParam));
}
break;

case WM_NOTIFY:
    switch (((NMHDR FAR *) lParam)->code)
    {
  case PSN_KILLACTIVE:
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
return 1;
break;

case PSN_RESET:
// rest to the original values
rvInfo.iAdaptation = 0;
           SetWindowLong(hDlg,DWL_MSGRESULT, FALSE);
break;

 case PSN_SETACTIVE:
if (rvInfo.iAdaptation)
SendMessage(GetDlgItem(hDlg, rvInfo.iAdaptation), BM_SETCHECK, 1, 0L);
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK | PSWIZB_FINISH);
break;

                case PSN_WIZBACK:
                    break;

                case PSN_WIZFINISH:
GenerateReview(hDlg);
                    break;

default:
return FALSE;
    }
break;

default:
return FALSE;
}
return TRUE;
}

//
//
//  FUNCTION: FillInPropertyPage(PROPSHEETPAGE *, int, LPSTR, LPFN)
//
//  PURPOSE: Fills in the given PROPSHEETPAGE structure
//
//  COMMENTS:
//
//      This function fills in a PROPSHEETPAGE structure with the
//      information the system needs to create the page.
//
void FillInPropertyPage( PROPSHEETPAGE* psp, int idDlg, LPSTR pszProc, DLGPROC pfnDlgProc)
{
    psp->dwSize = sizeof(PROPSHEETPAGE);
    psp->dwFlags = 0;
    psp->hInstance = rvInfo.hInst;
    psp->pszTemplate = MAKEINTRESOURCE(idDlg);
    psp->pszIcon = NULL;
    psp->pfnDlgProc = pfnDlgProc;
    psp->pszTitle = pszProc;
    psp->lParam = 0;

}

//
//
//    FUNCTION: CreateWizard(HWND)
//
//    PURPOSE: Create the Wizard control.
//
//   COMMENTS:
//
//      This function creates the wizard property sheet.
//
int CreateWizard(HWND hwndOwner, HINSTANCE hInst)
{
    PROPSHEETPAGE psp[NUM_PAGES];
    PROPSHEETHEADER psh;

FillInPropertyPage( &psp[0], IDD_INFO, TEXT("Your Information"), YourInfo);
FillInPropertyPage( &psp[1], IDD_WORKHABITS, TEXT("Work Habits"), WorkHabits);
FillInPropertyPage( &psp[2], IDD_TEAMWORK, TEXT("Team Work"), TeamWork);
FillInPropertyPage( &psp[3], IDD_RELIABILITY, TEXT("Reliability"), Reliability);
FillInPropertyPage( &psp[4], IDD_GOALS, TEXT("Attainment of Goals"), Goals);
FillInPropertyPage( &psp[5], IDD_ADAPTATION, TEXT("Adaptability to Change"), Adaptation);

    psh.dwSize = sizeof(PROPSHEETHEADER);
    psh.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD | PSH_NOAPPLYNOW;
    psh.hwndParent = hwndOwner;
    psh.pszCaption = (LPSTR) TEXT("Review Wizard");
    psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
    psh.nStartPage = 0;
    psh.ppsp = (LPCPROPSHEETPAGE) &psp;

    return (PropertySheet(&psh));
}

//
//
//    FUNCTION: GenerateReview(void)
//
//    PURPOSE: Generate the review.
//
//   COMMENTS:
//
//      This function generates the review based upon the answers
//      given by the Wizard. The function translates lame reality into
//      impressive-sounding manager-speak via a string table.
//
void GenerateReview( HWND hDlg )
{
    TCHAR lpBuf1[MAX_LINE];  // Buffers for the lines in the review.
    TCHAR lpBuf2[MAX_LINE];
    TCHAR lpBuf3[MAX_LINE];
    TCHAR lpBuf4[MAX_LINE];
    TCHAR lpBuf5[MAX_LINE];

wsprintf(lpReview, TEXT("Name: %s%C%C%C%CTitle: %s%C%C%C%CDepartment: %s%C%C%C%CMain Project: %s%C%C%C%C"),
rvInfo.pszName, 0x0d, 0x0a, 0x0d, 0x0a,
rvInfo.pszTitle, 0x0d, 0x0a, 0x0d, 0x0a,
rvInfo.pszDepartment, 0x0d, 0x0a, 0x0d, 0x0a,
rvInfo.pszProject,0x0d, 0x0a, 0x0d, 0x0a );

// Add a line describing work habits
if (LoadString(rvInfo.hInst, rvInfo.iWorkHabits, lpBuf1, sizeof(lpBuf1)) == 0)
MessageBox(hDlg, TEXT("Error loading string!"), NULL, MB_OK);
else
lstrcat(lpReview, lpBuf1);

// Add a line describing team work
if (LoadString(rvInfo.hInst, rvInfo.iTeamWork, lpBuf2, sizeof(lpBuf2)) == 0)
MessageBox(hDlg, TEXT("Error loading string!"), NULL, MB_OK);
else
lstrcat(lpReview, lpBuf2);

// Add a line describing reliability
if (LoadString(rvInfo.hInst, rvInfo.iReliability, lpBuf3, sizeof(lpBuf3)) == 0)
MessageBox(hDlg, TEXT("Error loading string!"), NULL, MB_OK);
else
lstrcat(lpReview, lpBuf3);

// Add a line describing goals
if (LoadString(rvInfo.hInst, rvInfo.iGoals, lpBuf4, sizeof(lpBuf4)) == 0)
MessageBox(hDlg, TEXT("Error loading string!"), NULL, MB_OK);
else
lstrcat(lpReview, lpBuf4);

// Add a line describing adaptability
if (LoadString(rvInfo.hInst, rvInfo.iAdaptation, lpBuf5, sizeof(lpBuf5)) == 0)
MessageBox(hDlg, TEXT("Error loading string!"), NULL, MB_OK);
else
lstrcat(lpReview, lpBuf5);

}


WIZARD.H

// menu commands

// Options menu
#define IDM_WIZARD      100
#define IDM_EXIT        101

// Help menu
#define IDM_ABOUT       200

// icons
#define EXE_ICON        300

// ids
#define ID_EDITCHILD1000

// constants
#define NUM_PAGES6
#define MAX_BUF5000
#define MAX_LINE512

// typedefs
typedef struct tagREVIEWINFO
{
    HINSTANCE hInst;        // current instance
int iWorkHabits;
int iTeamWork;
int iReliability;
int iGoals;
int iAdaptation;
char pszName[MAX_PATH];
char pszTitle[MAX_PATH];
char pszProject[MAX_PATH];
char pszDepartment[MAX_PATH];
} REVIEWINFO;

// Function prototypes

// procs
long APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
BOOL APIENTRY About(HWND, UINT, UINT, LONG);

// Pages for Wizard
BOOL APIENTRY WorkHabits(HWND, UINT, UINT, LONG);
BOOL APIENTRY TeamWork(HWND, UINT, UINT, LONG);
BOOL APIENTRY Reliability(HWND, UINT, UINT, LONG);
BOOL APIENTRY Goals(HWND, UINT, UINT, LONG);
BOOL APIENTRY Adaptation(HWND, UINT, UINT, LONG);

//functions
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);
int CreateWizard(HWND, HINSTANCE);
void FillInPropertyPage( PROPSHEETPAGE* , int, LPSTR, DLGPROC);
void GenerateReview(HWND);


README.TXT

Dynamic Dialog Box Creation

SUMMARY
=======

The DYNDLG sample shows how to create the dialog box template at run time.
Usually, an application defines a dialog box template in an RC or DLG file,
then creates a dialog box at run time from the template. However, it is
possible to create the dialog box template at run time.

MORE INFORMATION
================

The Win32 dialog box template is conceptually similar to the Windows 3.1
version, but is different enough to require some porting work. For example,
strings are now stored in Unicode. The dialog box template format is
documented in the help file included with the dialog box editor.

Run-time dependency:

One of the dialog boxes uses a custom control which is expected to be
defined in the spincube.dll file. The DYNDLG sample tries to load this DLL
at run time from the following location:

    "..\spincube\spincube.dll"

This path can be easily changed, and the sample recompiled. SPINCUBE is
another sample distributed in the Win32 SDK.


DYNDLG.H

LRESULT APIENTRY MainWndProc(HWND, UINT, UINT, LONG);

#define IDM_DIALOG1     101
#define IDM_DIALOG2     102
#define IDM_HELP        201
#define IDM_ABOUT       202


DYNDLG.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include "dyndlg.h"

LRESULT APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
LRESULT APIENTRY About(HWND, UINT, WPARAM, LPARAM );

int Create1(HWND);
int Create2(HWND);

LPWORD lpwAlign (LPWORD);
int nCopyAnsiToWideChar (LPWORD, LPSTR);

HINSTANCE ghInst;

/**************************************************************************\
*
*  function:  WinMain()
*
*  input parameters:  c.f. generic sample
*
\**************************************************************************/
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine, int nCmdShow)
{
    HWND   hwnd;
    MSG    msg;
    HANDLE hLibrary;

    UNREFERENCED_PARAMETER( lpCmdLine );

    ghInst = hInstance;

    /* Check for previous instance.  If none, then register class. */
    if (!hPrevInstance) {
        WNDCLASS  wc;

        wc.style = 0;
        wc.lpfnWndProc = (WNDPROC)MainWndProc;

        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon(hInstance, TEXT("dyndlgIcon"));
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = GetStockObject(LTGRAY_BRUSH);
        wc.lpszMenuName = TEXT("dyndlgMenu");
        wc.lpszClassName = TEXT("dyndlg");

        if (!RegisterClass(&wc)) return (FALSE);
    }  /* class registered o.k. */

    /* Create the main window.  Return false if CreateWindow() fails */
    hwnd = CreateWindow(
        TEXT("dyndlg"),
        TEXT("DynDlg"),
        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL);

    if (!hwnd) return (FALSE);

    /***** CUSTOM CONTROL
    * Load the DLL containing the custom control.
    *****/
    hLibrary = LoadLibrary (TEXT("..\\spincube\\SPINCUBE.DLL"));
    if (hLibrary == NULL)
      if (PRIMARYLANGID(GetUserDefaultLangID ()) == LANG_JAPANESE)
        MessageBox (hwnd, TEXT("LoadLibrary (..\\spincube\\SPINCUBE.DLL)      s         B"),
                  TEXT(" G   [,      A v   P [ V       spincube    K v    "), MB_OK | MB_ICONEXCLAMATION);
      else
        MessageBox (hwnd, TEXT("LoadLibrary (..\\spincube\\SPINCUBE.DLL) failed"),
                  TEXT("Error, this app requires spincube."), MB_OK | MB_ICONEXCLAMATION);

    /***** CUSTOM CONTROL *****/

    /* Demo: Just for fun, start out with one of the dialogs created. */
    PostMessage (hwnd, WM_COMMAND, IDM_DIALOG2, 0);

    /* Loop getting messages and dispatching them. */
    while (GetMessage(&msg,NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    if (hLibrary != NULL) FreeLibrary (hLibrary);

    return (msg.wParam);
}

/***************************************************************************\
*    FUNCTION: MainWndProc
\***************************************************************************/
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message) {

    /**********************************************************************\
    *  Menu item support.
    *
    \**********************************************************************/
    case WM_COMMAND:
      switch (LOWORD(wParam)) {

        case IDM_DIALOG1:
          Create1 (hwnd);
        break;

        case IDM_DIALOG2:
          Create2 (hwnd);
        break;

        case IDM_HELP:
          WinHelp( hwnd, TEXT("dyndlg.hlp"), HELP_INDEX, (DWORD) NULL );
        break;

        case IDM_ABOUT:
          DialogBox (GetModuleHandle(NULL), TEXT("aboutBox"), hwnd, (DLGPROC)About);
        return 0;

      }  /* end switch */
    break;  /* end wm_command */

    case WM_DESTROY:
      WinHelp( hwnd,  TEXT("dyndlg.hlp"), (UINT) HELP_QUIT, (DWORD) NULL );
      PostQuitMessage(0);
    break;

    } /* end switch */
    return (DefWindowProc(hwnd, message, wParam, lParam));
}

/****************************************************************************
    FUNCTION: About
****************************************************************************/
LRESULT CALLBACK About(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  if (message == WM_INITDIALOG)
    return TRUE;
  if ((message == WM_COMMAND) && (LOWORD(wParam) == IDOK)) {
    EndDialog (hwnd, TRUE);
    return TRUE;
  }
  if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE)) {
    EndDialog (hwnd, TRUE);
    return TRUE;
  }
  return FALSE;
}

/*+++

    Create the first dialog dynamically.  Notice that we are NOT using
    structures here because too many of the fields are of variable length.
    Instead, just allocate some memory to play with, and start filling in
    the data at that pointer.

    p - pointer which is moved down through the DLGTEMPLATE information.
    pdlgtemplate - pointer to the TOP of the DLGTEMPLATE information.

    Here we create a simple dialog with one item.  The dialog has a title,
    the item has text, and the item class is specified by ordinal.  There
    is no font information.

---*/
Create1(HWND hwnd)
{
  WORD  *p, *pdlgtemplate;
  int   nchar;
  DWORD lStyle;

  /* allocate some memory to play with  */
  pdlgtemplate = p = (PWORD) LocalAlloc (LPTR, 1000);

  /* start to fill in the dlgtemplate information.  addressing by WORDs */
  lStyle = DS_MODALFRAME | WS_CAPTION | WS_SYSMENU | WS_VISIBLE;

  *p++ = LOWORD (lStyle);
  *p++ = HIWORD (lStyle);
  *p++ = 0;          // LOWORD (lExtendedStyle)
  *p++ = 0;          // HIWORD (lExtendedStyle)
  *p++ = 1;          // NumberOfItems
  *p++ = 10;         // x
  *p++ = 10;         // y
  *p++ = 100;        // cx
  *p++ = 100;        // cy
  *p++ = 0;          // Menu
  *p++ = 0;          // Class

  /* copy the title of the dialog */
  if (PRIMARYLANGID(GetUserDefaultLangID ()) == LANG_JAPANESE)
    nchar = nCopyAnsiToWideChar (p, TEXT(" ^ C g   1"));
  else
    nchar = nCopyAnsiToWideChar (p, TEXT("Title 1"));
  p += nchar;

  /* add in the wPointSize and szFontName here iff the DS_SETFONT bit on */

  /* make sure the first item starts on a DWORD boundary */
  p = lpwAlign (p);

  /* now start with the first item */
  lStyle = BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD;

  *p++ = LOWORD (lStyle);
  *p++ = HIWORD (lStyle);
  *p++ = 0;          // LOWORD (lExtendedStyle)
  *p++ = 0;          // HIWORD (lExtendedStyle)
  *p++ = 10;         // x
  *p++ = 70;         // y
  *p++ = 80;         // cx
  *p++ = 20;         // cy
  *p++ = IDOK;       // ID

  /* fill in class i.d. Button in this case */
  *p++ = (WORD)0xffff;
  *p++ = (WORD)0x0080;

  /* copy the text of the first item */
  nchar = nCopyAnsiToWideChar (p, TEXT("OK"));
  p += nchar;

  *p++ = 0;  // advance pointer over nExtraStuff WORD

  CreateDialogIndirect (ghInst, (LPDLGTEMPLATE) pdlgtemplate, hwnd, (DLGPROC) About);

  LocalFree (LocalHandle (pdlgtemplate));

  return 0;
}

/*+++

    Create the second dialog dynamically.

    Here we create a dialog which has font information (DS_SETFONT),
    and which has two items with the item class specified by name.

---*/
Create2(HWND hwnd)
{
  WORD  *p, *pdlgtemplate;
  int   nchar;
  DWORD lStyle;

  /* allocate some memory to play with  */
  pdlgtemplate = p = (PWORD) LocalAlloc (LPTR, 1000);

  /* start to fill in the dlgtemplate information.  addressing by WORDs */
  lStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE | DS_SETFONT;
  *p++ = LOWORD (lStyle);
  *p++ = HIWORD (lStyle);
  *p++ = 0;          // LOWORD (lExtendedStyle)
  *p++ = 0;          // HIWORD (lExtendedStyle)
  *p++ = 2;          // NumberOfItems
  *p++ = 210;        // x
  *p++ = 10;         // y
  *p++ = 100;        // cx
  *p++ = 100;        // cy
  *p++ = 0;          // Menu
  *p++ = 0;          // Class

  /* copy the title of the dialog */
  if (PRIMARYLANGID(GetUserDefaultLangID ()) == LANG_JAPANESE)
    nchar = nCopyAnsiToWideChar (p, TEXT(" ^ C g   2"));
  else
    nchar = nCopyAnsiToWideChar (p, TEXT("Title 2"));
  p += nchar;

  /* Font information because of DS_SETFONT */
  *p++ = 18;     // point size
  nchar = nCopyAnsiToWideChar (p, TEXT("Times New Roman"));  // Face name
  p += nchar;

  /* make sure the first item starts on a DWORD boundary */
  p = lpwAlign (p);

  /* now start with the first item */
  lStyle = BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | WS_TABSTOP;
  *p++ = LOWORD (lStyle);
  *p++ = HIWORD (lStyle);
  *p++ = 0;          // LOWORD (lExtendedStyle)
  *p++ = 0;          // HIWORD (lExtendedStyle)
  *p++ = 10;         // x
  *p++ = 60;         // y
  *p++ = 80;         // cx
  *p++ = 20;         // cy
  *p++ = IDOK;       // ID

  /* fill in class i.d., this time by name */
  nchar = nCopyAnsiToWideChar (p, TEXT("BUTTON"));
  p += nchar;

  /* copy the text of the first item */
  nchar = nCopyAnsiToWideChar (p, TEXT("OK"));
  p += nchar;

  *p++ = 0;  // advance pointer over nExtraStuff WORD

  /* make sure the second item starts on a DWORD boundary */
  p = lpwAlign (p);

#define SS_INMOTION 0x0002  /* from spincube.h */
  lStyle = WS_VISIBLE | WS_CHILD | SS_INMOTION;
  *p++ = LOWORD (lStyle);
  *p++ = HIWORD (lStyle);
  *p++ = 0;          // LOWORD (lExtendedStyle)
  *p++ = 0;          // HIWORD (lExtendedStyle)
  *p++ = 20;         // x
  *p++ = 5;          // y
  *p++ = 65;         // cx
  *p++ = 45;         // cy
  *p++ = 57;         // ID

  /* fill in class i.d., this time by name */

  /***** CUSTOM CONTROL
  * Fill in the class name that is specified in the DLL
  *  See the \q_a\samples\spincube sample for the source to this.
  *****/
  nchar = nCopyAnsiToWideChar (p, TEXT("Spincube"));
  p += nchar;

  /* copy the text of the second item, null terminate the string. */
  nchar = nCopyAnsiToWideChar (p, TEXT(""));
  p += nchar;

  *p++ = 0;  // advance pointer over nExtraStuff WORD

  CreateDialogIndirect (ghInst, (LPDLGTEMPLATE) pdlgtemplate, hwnd, (DLGPROC) About);

  LocalFree (LocalHandle (pdlgtemplate));

  return 0;
}

/*+++

    Helper routine.  Take an input pointer, return closest
     pointer that is aligned on a DWORD (4 byte) boundary.

---*/
LPWORD lpwAlign ( LPWORD lpIn)
{
  ULONG ul;

  ul = (ULONG) lpIn;
  ul +=3;
  ul >>=2;
  ul <<=2;
  return (LPWORD) ul;
}

/*+++

    Helper routine.  Takes second parameter as Ansi string, copies
     it to first parameter as wide character (16-bits / char) string,
     and returns integer number of wide characters (words) in string
     (including the trailing wide char NULL).

---*/
int nCopyAnsiToWideChar (LPWORD lpWCStr, LPSTR lpAnsiIn)
{
  int cchAnsi = lstrlen(lpAnsiIn);

  return MultiByteToWideChar(GetACP(), MB_PRECOMPOSED, lpAnsiIn, cchAnsi, lpWCStr, cchAnsi) + 1;
}


README.TXT

IconPro Icon Manager

SUMMARY
=======

The IconPro sample is designed to manipulate icons having images with bit
depths from 1bpp to 32bpp, as well as sizes from 16x16 to 128x128.

IconPro provides no editing capabilities. To create an icon, the user must
first edit the image in DIB format, using a program like PBRUSH. Then the
image can be imported into IconPro without loss of bit depth.

Note that IconPro restricts the size of its child windows, so MDI tiling
can look different than expected.

When extracting icons from DLL and EXE files, IconPro will operate only on
32bit files.

MORE INFORMATION
================

Usage
-----

IconPro displays icons in MDI child windows, showing you one image at a
time. Since an icon may contain more than one image, a list box is provided
below the display area to choose which image to look at.

IconPro is capable of reading and writing ICO files, as well as extracting
icons from Win32 PE format DLLs and EXEs. It can also import from/export
to BMP files and copy/paste with the clipboard.

Holding the Control key and left-clicking on the XOR mask will make the
program calculate new masks, using the pixel color at the mouse location as
the transparent color.

File Menu
---------

New Icon: Creates a new icon resource and a window in which to display it.
    The newly created icon resource has no images in it. New images would
    need to be created using the Add Format command in the Edit menu.
Open Icon File: Opens an ICO file and creates a new window in which to
    display its images. The images stored in the ICO file are enumerated in
    the listbox at the bottom of the display window.
Extract From DLL/EXE: Extracts an icon resource from a 32bit DLL or EXE
    file. It will fail on 16bit files. If the file contains more than one
    icon resource, a dialog box is shown allowing the choice of which icon
    resource to extract.
Close: Closes the current icon resource child window. If the image has been
    modified, a dialog box will be shown asking whether to save changes.
Save ICO File: Saves the current icon resource to its ICO file.
Save As ICO File: Saves the current icon resource to a newly named ICO file.
Exit: Terminates IconPro.

Edit Menu
---------

Copy: Causes the XOR mask of the current icon resource's current image to be
    placed on the clipboard in CF_DIB format.
Paste: Takes the CF_DIB format image from the clipboard and uses it to
    replace the XOR mask of the current icon resource's current image. If
    the clipboard image is larger than the current icon image, only the
    corner of the image is used. If the clipboard image is smaller than the
    current icon image, some of the resultant icon image may be black or
   undefined.
Stretch Paste: Takes the CF_DIB format image from the clipboard and uses it
    to replace the XOR mask of the current icon resource's current image,
    stretching it to fit the dimensions of the icon image.
Import BMP: Reads a BMP file and uses it to replace the XOR mask of the
    current icon resource's current image. If the BMP bitmap is larger than
    the current icon image, only the corner of the image is used. If the BMP
    bitmap is smaller than the current icon image, some of the resultant
    icon image may be black or undefined.
Stretch Import BMP: Reads a BMP file and uses it to replace the XOR mask of
    the current icon resource's current image, stretching it to fit the
    dimensions of the icon image.
Export BMP: Causes the XOR mask of the current icon resource's current
    image to be written to a newly named BMP file.
Add Format: Launches a dialog box that allows the creation of a new icon
    image in the current icon resource. Slider controls are provided to
    allow the Width, Height, and Color Depth to be chosen. Valid ranges for
    these values are:
        Width: Min 16, Max 128
        Height: Min 16, Max 128
        Color Depth: Min 1bpp, Max 32bpp
    Two images with identical Width, Height and Color Depth are not allowed
    in the same icon resource.
Remove Format: Removes the current icon image from the current icon
    resource.

Window Menu
-----------

Arrange Icons: Arranges the MDI child icons.
Cascade: Arranges the MDI child windows in a cascade effect.
Tile Horizontal: Horizontally tiles the MDI child windows.
Tile Vertical: Vertically tiles the MDI child windows.
Contents: Launches help for IconPro.
About: Displays the About box.

Icons
-----

Icon images are stored in ICO files and as resources in EXEs and DLLs in
near DIB format - a BITMAPINFO followed by XOR bits followed by AND bits. A
block of memory like this can be passed to CreateIconFromResourceEx to
create a HICON from the resource. This API expects the bmiHeader.biHeight
member of the BITMAPINFO to be the sum of the heights of the XOR and AND
masks. Further, this API is not implemented on NT at this time, so
CreateIconFromResource must be used instead.

When creating the AND mask for an icon (or any transparent BitBlt), a simple
method is to BitBlt from the source image to a mono bitmap and let GDI do
the conversions based on SetBkColor. This will not work with DIBSections.
Any BitBlt'ing with DIBSections results in color mapping without regard to
the background color.

DIBSections can not be used in the ICONINFO structure passed to
CreateIconIndirect. DDBs must be used there.

Currently, during the icon selection process in Windows 95, all bit
depths >= 8 are treated as equal. Therefore, it is pointless to have both
a 24bpp and an 8bpp image in the same ICO file. When running in any video
mode of at least 8bpp, Windows will simply choose the first one. Further,
in 8bpp mode, icons are always displayed using the system palette.

ICO File Format
---------------

Icons are normally stored in ICO files. The ICO file format is documented in
the Windows 3.1 SDK Programmer's Reference, Volume 4: Resources, Chapter 1:
Graphics File Formats.

The ICO file starts with an ICONDIR structure. The ICONDIR structure is
defined as:

typedef struct
{
    WORD           idReserved;   // Reserved
    WORD           idType;       // resource type (1 for icons)
    WORD           idCount;      // how many images?
    ICONDIRENTRY   idEntries[1]; // entries for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;

The ICONDIRENTRY structure is defined as:

typedef struct
{
    BYTE        bWidth;          // Width of the image
    BYTE        bHeight;         // Height of the image (times 2)
    BYTE        bColorCount;     // Number of colors in image (0 if >=8bpp)
    BYTE        bReserved;       // Reserved
    WORD        wPlanes;         // Color Planes
    WORD        wBitCount;       // Bits per pixel
    DWORD       dwBytesInRes;    // how many bytes in this resource?
    DWORD       dwImageOffset;   // where in the file is this image
} ICONDIRENTRY, *LPICONDIRENTRY;

So, the file consists of the header followed by the bits for each image. The
bits for each image can be located by seeking to dwImageOffset in the file.
The format of the bits follows:

  The bits for each image are stored in something similar to CF_DIB format.
  First is a BITMAPINFO structure, followed by the DIB bits of the XOR mask,
  followed by the DIB bits of the AND mask.

  Note the following two caveats:
  1) The bmiHeader.biHeight member of the BITMAPINFO structure indicates
     the height of the XOR mask plus the height of the AND mask.
  2) The AND mask is monochrome, and has no BITMAPINFO.

Notes
-----

IconPro is an MDI application. Each child window stores a pointer to a
structure in its GWL_USERDATA location. This structure contains information
such as the handle to the listbox, the locations of various graphical
elements in the client area, and a pointer to the icon resource data.

The icon resource data is stored in a structure that contains normal
information like the number of images and info for each of those images. The
info for each image includes its width/height/bpp as well as the DIB format
memory blocks for the XOR and AND masks.

In the MDI child window, the icon is displayed using DrawIconEx() once over
a black background and once over a white background. The XOR and AND masks
are also displayed using standard DIB functions.

Holding the 'Control' key and left-clicking on the XOR mask will make the
program calculate new masks, using the pixel color at the mouse location as
the transparent color. This is implemented using the following slow, but
effective method:

    Loop through all the pixels in the source
    If a given pixel matches the transparent color (use GetPixel)
then clear the bit in the XOR mask and set the bit in the AND mask
    else
        clear the bit in the AND mask


DIB.H

/****************************************************************************\
*
*     FILE:     DIB.H
*
*     PURPOSE:  IconPro Project DIB handling header file
*
*     COMMENTS: Icons are stored in something almost identical to DIB
*               format, which makes it real easy to treat them as DIBs
*               when manipulating them.
*
*     Copyright 1995 - 1998 Microsoft Corp.
*
*
* History:
*                July '95 - Created
*
\****************************************************************************/

/****************************************************************************/
// local #defines

// How wide, in bytes, would this many bits be, DWORD aligned?
#define WIDTHBYTES(bits)      ((((bits) + 31)>>5)<<2)
/****************************************************************************/

/****************************************************************************/
// Exported function prototypes
LPSTR FindDIBBits (LPSTR lpbi);
WORD DIBNumColors (LPSTR lpbi);
WORD PaletteSize (LPSTR lpbi);
DWORD BytesPerLine( LPBITMAPINFOHEADER lpBMIH );
LPBYTE ConvertDIBFormat( LPBITMAPINFO lpSrcDIB, UINT nWidth, UINT nHeight, UINT nColors, BOOL bStretch );
void SetMonoDIBPixel( LPBYTE pANDBits, DWORD dwWidth, DWORD dwHeight, DWORD x, DWORD y, BOOL bWhite );
LPBYTE ReadBMPFile( LPCTSTR szFileName );
BOOL WriteBMPFile( LPCTSTR szFileName, LPBYTE lpDIB );
/****************************************************************************/


ICONPRO.C

/****************************************************************************\
*
*     FILE:     ICONPRO.C
*
*     PURPOSE:  IconPro Project main C file
*
*     COMMENTS: This file contains the main window and instance handing
*               for this project.
*
*     FUNCTIONS:
*      EXPORTS:
*               GetSaveIconFileName     - Get the name of the file to write
*               GetOpenIconFileName     - Get the name of a file to open
*      LOCALS:
*               WinMain                 - Program entry point
*               InitApplication         - Register classes
*               InitInstance            - Create windows
*               WndProc                 - Main Window's 'Window Procedure'
*               OnCommand               - Handle command messages (menu items)
*               AboutDlgProc            - About Dialog Box's 'Window Procedure'
*               CreateNewMDIChildWindow - Creates a new child MDI window
*               UpdateMenuState         - Grays/enables appropriate menu items
*               QueryCloseAllChildren   - Close all children if possible
*
*     Copyright 1995 - 1998 Microsoft Corp.
*
*
* History:
*                July '95 - Created
*
\****************************************************************************/
#include <Windows.H>
#include "Resource.h"
#include "Icons.H"
#include "IconPro.h"
#include "MDIChild.h"

/****************************************************************************/
/* Local Function Prototypes */
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow);
BOOL InitApplication( HANDLE hInstance );
BOOL InitInstance( HANDLE hInstance, int nCmdShow );
LRESULT CALLBACK WndProc( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam );
LRESULT OnCommand( HWND hWnd, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK AboutDlgProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
HWND CreateNewMDIChildWindow( LPSTR szTitle, LPVOID lpData );
BOOL UpdateMenuState( HWND hWnd, HMENU hMenu );
BOOL QueryCloseAllChildren( void );
/****************************************************************************/

/****************************************************************************/
/* Global Variables */
HINSTANCEhInst;
HWND        hWndMain, hMDIClientWnd;
char        szAppName[] = TEXT("IconPro");
char        szTitle[] = TEXT("IconPro Icon Handler");
char        szChildClassName[] = TEXT("IconChildClass");
char        szHelpFileName[] = "IconPro.HLP";
/****************************************************************************/

/****************************************************************************
*
*     FUNCTION: WinMain
*
*     PURPOSE:  Main entry point for this app
*
*     PARAMS:   HANDLE hInstance     - This instance
*               HANDLE hPrevInstance - Previous instance
*               LPSTR  lpszCmdLine   - Command Line
*               int    nCmdShow      - Desired window status
*
*     RETURNS:  int - return code
*
* History:
*                July '95 - Created
*
\****************************************************************************/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
    MSG msg;

    // standard init stuff
    if( ! hPrevInstance )
    {
        if( ! InitApplication( hInstance ) )
        {
            return FALSE;
        }
    }
    if( ! InitInstance( hInstance, nCmdShow ) )
    {
        return FALSE;
    }

    // Standard message loop
    while (GetMessage(&msg, (HWND) NULL, 0, 0))
    {
        if( !TranslateMDISysAccel(hMDIClientWnd, &msg) )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return msg.wParam;
}
/* End WinMain() ************************************************************/

/****************************************************************************
*
*     FUNCTION: InitApplication
*
*     PURPOSE:  Register classes
*
*     PARAMS:   HANDLE hInstance     - This instance
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL InitApplication( HANDLE hInstance )
{
    WNDCLASS    wc;
    BOOL        bResult;

    wc.style         = 0;
    wc.lpfnWndProc   = (WNDPROC)WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon( hInstance, MAKEINTRESOURCE(ICONPRO_ICON) );
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = MAKEINTRESOURCE(ICONPRO_MENU);
    wc.lpszClassName = szAppName;

    bResult = RegisterClass( &wc );

    wc.lpfnWndProc   = (WNDPROC)IconChildWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon( hInstance, MAKEINTRESOURCE(ICONPRO_ICON) );
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = CreateSolidBrush(COLOR_3DFACE);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = szChildClassName;

    return( bResult && RegisterClass( &wc ) );
}
/* End InitApplication() ***************************************************/

/****************************************************************************
*
*     FUNCTION: InitInstance
*
*     PURPOSE:  Create and show the main window
*
*     PARAMS:   HANDLE hInstance  - This instance
*               int    nCmdShow   - desired show state
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL InitInstance( HANDLE hInstance, int nCmdShow )
{
    hInst = hInstance;
    hWndMain = CreateWindow(
        szAppName,
        szTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0,
        CW_USEDEFAULT, 0,
        NULL,
        NULL,
        hInstance,
        NULL );

    if( hWndMain == NULL )
    {
        return FALSE;
    }

    ShowWindow( hWndMain, nCmdShow );
    UpdateWindow( hWndMain );

    return TRUE;
}
/* End InitInstance() *****************************************************/

/****************************************************************************
*
*     FUNCTION: WndProc
*
*     PURPOSE:  Window Procedure for the main window.
*
*     PARAMS:   HWND   hWnd    - This window
*               UINT   Message - Which message?
*               WPARAM wParam  - message parameter
*               LPARAM lParam  - message parameter
*
*     RETURNS:  LRESULT - depends on message
*
* History:
*                July '95 - Created
*
\****************************************************************************/
LRESULT CALLBACK WndProc( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam )
{
    // which message are we handling?
    switch( Message )
    {
        // Menu is coming up, initialize it
        case WM_INITMENU:
            UpdateMenuState( hWnd, (HMENU)wParam );
            return 1;
        break; // end WM_INITMENU

        // Window is being created, create the MDI client also
        case WM_CREATE:
        {
            CLIENTCREATESTRUCT ccs;

            // Retrieve the handle of the Window menu and assign the
            // first child window identifier.
            ccs.hWindowMenu = GetSubMenu(GetMenu(hWnd), 2 );
            ccs.idFirstChild = IDM_WINDOWCHILD;

            // Create the MDI client window
            hMDIClientWnd = CreateWindow(TEXT("MDICLIENT"), (LPCTSTR) NULL,
                                            WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
                                            0, 0, 0, 0, hWnd, (HMENU) 0xCAC, hInst, (LPSTR) &ccs );

            ShowWindow(hMDIClientWnd, SW_SHOW);
        }
        break; // End WM_CREATE

        // Command Messages (menu items, etc)
        case WM_COMMAND:
            OnCommand( hWnd, wParam, lParam );
            return DefFrameProc( hWnd, hMDIClientWnd, Message, wParam, lParam );
        break; // End WM_COMMAND

        // Time to close down now :(
        case WM_CLOSE:
        {
            // Will the children allow it? (Give 'em a chance to cancel)
            if( QueryCloseAllChildren() )
            {
                WinHelp( hWnd, szHelpFileName, HELP_QUIT, 0 );
                DestroyWindow( hWnd );
                PostQuitMessage( 0 );
                return DefFrameProc( hWnd, hMDIClientWnd, Message, wParam, lParam );
            }
            return 0;
        }
        break; // End WM_CLOSE

        // Pass it on to the default window procedure
        default:
            return DefFrameProc( hWnd, hMDIClientWnd, Message, wParam, lParam );
        break; // end default
    }
    return DefFrameProc( hWnd, hMDIClientWnd, Message, wParam, lParam );
}
/* End WndProc() ***********************************************************/

/****************************************************************************
*
*     FUNCTION: OnCommand
*
*     PURPOSE:  Handles command messages for main window
*
*     PARAMS:   HWND   hWnd    - This window
*               WPARAM wParam  - message parameter
*               LPARAM lParam  - message parameter
*
*     RETURNS:  LRESULT - depends on message
*
* History:
*                July '95 - Created
*
\****************************************************************************/
LRESULT OnCommand( HWND hWnd, WPARAM wParam, LPARAM lParam )
{
    // Which command is it?
    switch( LOWORD( wParam ) )
    {
        // File->New menu option - spawn a new child
        case ID_FILE_NEW:
        {
            HWND    hWndNew;

            hWndNew = CreateNewMDIChildWindow( TEXT("Untitled"), NULL );
            SendMessage( hMDIClientWnd, WM_MDIACTIVATE, (WPARAM)hWndNew, 0 );
        }
        break; // End ID_FILE_NEW

        // File->Open menu option - open an ICO file
        case ID_FILE_OPEN:
        {
            HWND            hWndNew;
            LPICONRESOURCElpIR;
            TCHAR        szFileName[MAX_PATH];
            TCHAR        szFileTitle[MAX_PATH];

            // Get the name of the file to open
            if( GetOpenIconFileName( szFileName, IDS_FILTERSTRING, "Open Icon File" ) )
            {
                // Read in the icon data
                if( (lpIR = ReadIconFromICOFile( szFileName )) == NULL )
                    break;
                // Get the name of the file for the window title
                if( GetFileTitle( szFileName, szFileTitle, MAX_PATH ) )
                    break;
                // Make a new child to handle this icon
                hWndNew = CreateNewMDIChildWindow( szFileTitle, lpIR );
                SendMessage( hMDIClientWnd, WM_MDIACTIVATE, (WPARAM)hWndNew, 0 );
            }
        }
        break; // End ID_FILE_OPEN

        // File->Extract menu option - extract icon data from a DLL or EXE
        case ID_FILE_EXTRACT:
        {
            HWND            hWndNew;
            LPICONRESOURCElpIR;
            TCHAR        szFileName[MAX_PATH];

            // Get the name of the file from which to extract the icon
            if( GetOpenIconFileName( szFileName, IDS_EXEFILTERSTRING, "Extract Icon from File" ) )
            {
                // Extract the icon data
                if( (lpIR = ReadIconFromEXEFile( szFileName )) == NULL )
                    break;
                // Make a new child to handle this icon
                hWndNew = CreateNewMDIChildWindow( ("Untitled"), lpIR );
                SendMessage( hMDIClientWnd, WM_MDIACTIVATE, (WPARAM)hWndNew, 0 );
            }
        }
        break; // End ID_FILE_EXTRACT

        // File->Save and File->SaveAs menu options - save current ICO file to disk
        case ID_FILE_SAVEAS:
        case ID_FILE_SAVE:
        {
            HWND    hWndActive;

            // Get the active MDI child window
            if( (hWndActive = (HWND)SendMessage( hMDIClientWnd, WM_MDIGETACTIVE, 0, 0 )) != NULL )
            {
                // Tell it to write its icon to disk
                SendMessage( hWndActive, WM_COMMAND, wParam, 0 );
            }
            else
                MessageBox( hWnd, TEXT("Error Getting Active Window"), TEXT("Error"), MB_OK );
        }
        break; // End ID_FILE_SAVE/ID_FILE_SAVEAS

        // File->Close menu option - close the current MDI child window
        case ID_FILE_CLOSE:
        {
            HWND    hWndActive;

            // Get the active MDI child window and tell it to close itself
            if( (hWndActive = (HWND)SendMessage( hMDIClientWnd, WM_MDIGETACTIVE, 0, 0)) != NULL )
                SendMessage( hWndActive, WM_CLOSE, 0, lParam );
        }
        break; // End ID_FILE_CLOSE

        // File->Exit menu option - shut 'er down
        case ID_F_EXIT:
            // Just send a WM_CLOSE - code there will handle it
            PostMessage( hWnd, WM_CLOSE, (WPARAM)0, (LPARAM)0 );
        break; // End ID_F_EXIT

        // Edit->Import BMP and Edit->Stretch-Import BMP - convert BMP file to icon
        // Edit->Copy, Edit->Paste, Edit->StretchPaste, Edit->AddFormat, Edit->RemoveFormat
        // menu options. Just pass 'em on to the currently active MDI child window
        case ID_EDIT_EXPORTBMP:
        case ID_EDIT_IMPORTBMP:
        case ID_EDIT_STRETCHIMPORTBMP:
        case ID_EDIT_COPY:
        case ID_EDIT_PASTE:
        case ID_EDIT_STRETCHPASTE:
        case ID_EDIT_ADDFORMAT:
        case ID_EDIT_REMOVEFORMAT:
        {
            HWND    hWndActive;

            // Get active MDI child
            if( (hWndActive = (HWND)SendMessage( hMDIClientWnd, WM_MDIGETACTIVE, 0, 0 )) != NULL )
            {
                // Pass the message along
                SendMessage( hWndActive, WM_COMMAND, wParam, 0 );
            }
        }
        break; // End ID_EDIT_XXXX

        // Window->ArrangeIcons menu option
        case ID_WINDOW_ARRANGEICONS:
            // Ask the MDIClient to do it
            SendMessage( hMDIClientWnd, WM_MDIICONARRANGE, 0, 0);
        break; // End ID_WINDOW_ARRANGEICONS

        // Window->Cascade menu option
        case ID_WINDOW_CASCADE:
            // Ask the MDIClient to do it
            SendMessage( hMDIClientWnd, WM_MDICASCADE, 0, 0);
        break; // End ID_WINDOW_CASCADE

        // Window->Tile menu option
        case ID_WINDOW_TILE:
            // Ask the MDIClient to do it
            SendMessage( hMDIClientWnd, WM_MDITILE, (WPARAM)MDITILE_HORIZONTAL, 0);
        break; // End ID_WINDOW_TILE

        // Window->TileVertical menu option
        case ID_WINDOW_TILEVERTICAL:
            // Ask the MDIClient to do it
            SendMessage( hMDIClientWnd, WM_MDITILE, (WPARAM)MDITILE_VERTICAL, 0);
        break; // End ID_WINDOW_TILEVERTICAL

        // Help->About menu option
        case ID_HELP_ABOUT:
            // Launch the About Dialog
            DialogBox( hInst, MAKEINTRESOURCE(ICONPRO_ABOUT_DLG), hWnd, AboutDlgProc );
        break;

        case ID_HELP_CONTENTS:
                WinHelp( hWnd, szHelpFileName, HELP_CONTENTS, 0 );
        break;
    }
    return 0l;
}
/* End OnCommand() *********************************************************/

/****************************************************************************
*
*     FUNCTION: AboutDlgProc
*
*     PURPOSE:  Handles messages for About Dialog
*
*     PARAMS:   HWND   hWnd    - This window
*               UINT   Msg     - Which message?
*               WPARAM wParam  - message parameter
*               LPARAM lParam  - message parameter
*
*     RETURNS:  BOOL - depends on message
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL CALLBACK AboutDlgProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
    switch( Msg )
    {
        case WM_INITDIALOG:
        break;

        case WM_CLOSE:
            PostMessage( hWnd, WM_COMMAND, IDCANCEL, 0l );
        break;

        case WM_COMMAND:
            switch( LOWORD(wParam) )
            {
                case IDOK:
                    EndDialog( hWnd, TRUE );
                break;

                case IDCANCEL:
                    EndDialog( hWnd, FALSE );
                break;

            }
        break;
        default:
            return FALSE;
        break;
    }
    return TRUE;
}
/* End AboutDlgProc() ******************************************************/

/****************************************************************************
*
*     FUNCTION: CreateNewMDIChildWindow
*
*     PURPOSE:  Creates and shows a new MDI child window
*
*     PARAMS:   LPSTR  szTitle - The title for the new window
*               LPVOID lpData  - Data to attach to the window (icon info)
*
*     RETURNS:  HWND - Handle to the new MDI child window
*
* History:
*                July '95 - Created
*
\****************************************************************************/
HWND CreateNewMDIChildWindow( LPTSTR szTitle, LPVOID lpData )
{
    HWND    hReturnWnd;

    // Create the MDI child window
    hReturnWnd = CreateMDIWindow( szChildClassName, szTitle, 0,
                    CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT,
                    hMDIClientWnd, hInst, (LPARAM)lpData );
    // Show it
    ShowWindow( hReturnWnd, SW_SHOW );
    return hReturnWnd;
}
/* End CreateNewMDIChildWindow() ********************************************/

/****************************************************************************
*
*     FUNCTION: GetOpenIconFileName
*
*     PURPOSE:  Prompts user for the filename for opening
*
*     PARAMS:   LPSTR   szFileName     - Buffer to receive name of file
*               UINT    FilterStringID - ID of filter string
*               LPCTSTR szTitle        - Title for the Dialog
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL GetOpenIconFileName( LPTSTR szFileName, UINT FilterStringID, LPCTSTR szTitle )
{
    OPENFILENAME    ofn;
    UINT            i, cbString;
    TCHAR        chReplace;
    TCHAR        szFilter[256];

    // Get the filter string
    if ((cbString = LoadString( hInst, FilterStringID, szFilter, sizeof(szFilter))) == 0 )
        return FALSE;
    // Replace the wildcard with NULL
    chReplace = szFilter[cbString - 1]; /* retrieve wildcard */
    for (i = 0; szFilter[i] != '\0'; i++)
    {
        if (szFilter[i] == chReplace)
            szFilter[i] = '\0';
    }

    // Fill out the OPENFILENAME struct
    ZeroMemory( &ofn, sizeof( OPENFILENAME ) );
    ofn.lStructSize = sizeof( OPENFILENAME );
    ofn.hwndOwner = hWndMain;
    ofn.hInstance = hInst;
    lstrcpy( szFileName, TEXT("") );
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_FILEMUSTEXIST;
    ofn.lpstrFilter = szFilter;
    ofn.lpstrTitle = szTitle;

    // Use the common dialog
    return GetOpenFileName( &ofn );
}
/* End GetOpenIconFileName() ***********************************************/

/****************************************************************************
*
*     FUNCTION: GetSaveIconFileName
*
*     PURPOSE:  Prompts user for the filename for saving
*
*     PARAMS:   LPSTR   szFileName     - Buffer to receive name of file
*               UINT    FilterStringID - ID of filter string
*               LPCTSTR szTitle        - Title for the Dialog
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL GetSaveIconFileName( LPTSTR szFileName, UINT FilterStringID, LPCTSTR szTitle )
{
    OPENFILENAME    ofn;
    UINT            i, cbString;
    TCHAR        chReplace;
    TCHAR        szFilter[256];

    // Get the filter string
    if ((cbString = LoadString( hInst, FilterStringID, szFilter, sizeof(szFilter))) == 0 )
        return FALSE;
    // Replace the wildcard with NULL
    chReplace = szFilter[cbString - 1]; /* retrieve wildcard */
    for (i = 0; szFilter[i] != '\0'; i++)
    {
        if (szFilter[i] == chReplace)
            szFilter[i] = '\0';
    }

    // Fill out the OPENFILENAME struct
    ZeroMemory( &ofn, sizeof( OPENFILENAME ) );
    ofn.lStructSize = sizeof( OPENFILENAME );
    ofn.hwndOwner = hWndMain;
    ofn.hInstance = hInst;
    lstrcpy( szFileName, TEXT("") );
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_OVERWRITEPROMPT;
    ofn.lpstrFilter = szFilter;
    ofn.lpstrDefExt = TEXT("ICO");
    ofn.lpstrTitle = szTitle;

    // Use the common dialog
    return GetSaveFileName( &ofn );
}
/* End GetSaveIconFileName() ***********************************************/

/****************************************************************************
*
*     FUNCTION: UpdateMenuState
*
*     PURPOSE:  Updates states of menu items (called on menu activation)
*
*     PARAMS:   HWND  hWnd  - Handle to the window
*               HMENU hMenu - Handle to the menu
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL UpdateMenuState( HWND hWnd, HMENU hMenu )
{
    UINT    nEnableFlag, nNumFormats;
    HWND    hWndActive;

    // Get a handle to the active MDI child window
    hWndActive=(HWND)SendMessage( hMDIClientWnd, WM_MDIGETACTIVE, 0, 0 );
    // Find out how many icon image formats it holds
    nNumFormats = SendMessage( hWndActive, WM_COMMAND, ID_GETNUMFORMATS, 0 );
    // Do we actually have an active window?
    if( hWndActive == NULL )
    {
        // No active MDI child...
        EnableMenuItem( hMenu, ID_FILE_NEW, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_FILE_OPEN, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_FILE_EXTRACT, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_FILE_CLOSE, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_FILE_SAVE, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_FILE_SAVEAS, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_F_EXIT, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_EDIT_COPY, MF_BYCOMMAND| MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_PASTE, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_STRETCHPASTE, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_ADDFORMAT, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_IMPORTBMP, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_STRETCHIMPORTBMP, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_EXPORTBMP, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_REMOVEFORMAT, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_WINDOW_ARRANGEICONS, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_WINDOW_CASCADE, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_WINDOW_TILE, MF_BYCOMMAND | MF_GRAYED );
        EnableMenuItem( hMenu, ID_WINDOW_TILEVERTICAL, MF_BYCOMMAND | MF_GRAYED );
    }
    else
    {
        // We have an active MDI child...
        EnableMenuItem( hMenu, ID_FILE_NEW, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_FILE_OPEN, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_FILE_EXTRACT, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_FILE_CLOSE, MF_BYCOMMAND | MF_ENABLED );
        nEnableFlag = (BOOL)SendMessage( hWndActive, WM_COMMAND, ID_HASFILECHANGED, 0 ) ? MF_ENABLED : MF_GRAYED;
        EnableMenuItem( hMenu, ID_FILE_SAVE, MF_BYCOMMAND | nEnableFlag );
        EnableMenuItem( hMenu, ID_FILE_SAVEAS, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_F_EXIT, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_EDIT_COPY, MF_BYCOMMAND | (nNumFormats>0)?MF_ENABLED:MF_GRAYED );
        if( IsClipboardFormatAvailable( CF_DIB ) )
        {
            EnableMenuItem( hMenu, ID_EDIT_PASTE, MF_BYCOMMAND | (nNumFormats>0)?MF_ENABLED:MF_GRAYED );
            EnableMenuItem( hMenu, ID_EDIT_STRETCHPASTE, MF_BYCOMMAND | (nNumFormats>0)?MF_ENABLED:MF_GRAYED );
        }
        else
        {
            EnableMenuItem( hMenu, ID_EDIT_PASTE, MF_BYCOMMAND | MF_GRAYED );
            EnableMenuItem( hMenu, ID_EDIT_STRETCHPASTE, MF_BYCOMMAND | MF_GRAYED );
        }
        EnableMenuItem( hMenu, ID_EDIT_IMPORTBMP, MF_BYCOMMAND | (nNumFormats>0)?MF_ENABLED:MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_STRETCHIMPORTBMP, MF_BYCOMMAND | (nNumFormats>0)?MF_ENABLED:MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_EXPORTBMP, MF_BYCOMMAND | (nNumFormats>0)?MF_ENABLED:MF_GRAYED );
        EnableMenuItem( hMenu, ID_EDIT_ADDFORMAT, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_EDIT_REMOVEFORMAT, MF_BYCOMMAND | (nNumFormats>0)?MF_ENABLED:MF_GRAYED );
        EnableMenuItem( hMenu, ID_WINDOW_ARRANGEICONS, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_WINDOW_CASCADE, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_WINDOW_TILE, MF_BYCOMMAND | MF_ENABLED );
        EnableMenuItem( hMenu, ID_WINDOW_TILEVERTICAL, MF_BYCOMMAND | MF_ENABLED );
    }
    return TRUE;
}
/* End UpdateMenuState() ***************************************************/

/****************************************************************************
*
*  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.
*
*  PARAMS:     : none
*
*
*  RETURNS    : TRUE - If all children agree to the query.
*               FALSE- If any one of them disagrees.
*
\***************************************************************************/
BOOL QueryCloseAllChildren( void )
{
    register HWND hwndT;

    for( hwndT = GetWindow(hMDIClientWnd, GW_CHILD); hwndT; hwndT = GetWindow (hwndT, GW_HWNDNEXT) )
    {
        if( GetWindow( hwndT, GW_OWNER ) )
            continue;

        if( SendMessage( hwndT, WM_CLOSE, 0, 0L ) )
            return FALSE;
    }
    return TRUE;
}
/* End QueryCloseAllChildren() **********************************************/


ICONPRO.H

/****************************************************************************\
*
*     FILE:     IconPro.H
*
*     PURPOSE:  IconPro Project main header file
*
*     COMMENTS: The child window size is determined here, based on the
*               hard coded maximum sizes of icons supported. Only one
*               function is exported from IconPro.C, and it is exported
*               to allow MDI child windows to query for a save file name
*               if they are asked to close and their data has changed.
*
*     Copyright 1995 - 1998 Microsoft Corp.
*
*
* History:
*                July '95 - Created
*
\****************************************************************************/

/****************************************************************************/
// local #defines

// How large of icons will we support? This is really only important
// bacuase of the design of the UI. It needs to draw the entire icon
// 4 times, so we limit how big one can be. The ICO spec puts no limit
// on icon sizes.
#define MAX_ICON_WIDTH128              // Max width
#define MIN_ICON_WIDTH16               // Min width
#define MAX_ICON_HEIGHTMAX_ICON_WIDTH   // Max height
#define MIN_ICON_HEIGHTMIN_ICON_WIDTH   // Min height

// How big do the MDI child windows need to be to display the icon and
// the listbox?
#define WINDOW_WIDTH( ( MAX_ICON_WIDTH * 2 ) + 30 )
#define WINDOW_HEIGHT( ( MAX_ICON_HEIGHT * 2 ) + 150 )

// Utility macro to calculate a rectangle's width/height
#define RectWidth(r)((r).right - (r).left + 1)
#define RectHeight(r)((r).bottom - (r).top + 1)
/****************************************************************************/

/****************************************************************************/
// Exported function prototypes
BOOL GetSaveIconFileName( LPTSTR szFileName, UINT FilterStringID, LPCTSTR szTitle );
BOOL GetOpenIconFileName( LPTSTR szFileName, UINT FilterStringID, LPCTSTR szTitle );
/****************************************************************************/


ICONS.C

/****************************************************************************\
*
*     FILE:     ICONS.C
*
*     PURPOSE:  IconPro Project Icon handing Code C file
*
*     COMMENTS: This file contains the icon handling code
*
*     FUNCTIONS:
*      EXPORTS:
*               ReadIconFromICOFile        - Reads Icon from ICO file
*               WriteIconToICOFile         - Writes Icon to ICO file
*               MakeIconFromResource       - Makes HICON from a resource
*               ReadIconFromEXEFile        - Reads Icon from a EXE or DLL file
*               IconImageToClipBoard       - Puts icon image on clipboard
*               IconImageFromClipBoard     - Gets icon image from clipboard
*               CreateBlankNewFormatIcon   - Makes a new, blank icon image
*               DrawXORMask                - Draws XOR mask using DIBs
*               DrawANDMask                - Draws AND mask using DIBs
*               GetXORImageRect            - Calculates XOR image position
*               MakeNewANDMaskBasedOnPoint - Calculates new AND mask
*               ConvertBMPFileToIcon       - Converts BMP to Icon
*               IconImageToBMPFile         - Writes an icon image to BMP file
*      LOCALS:
*               ReadICOHeader              - Reads ICO file header
*               AdjustIconImagePointers    - Adjusts internal pointers
*               ExtractDlgProc             - Dlg Proc for extract dialog
*               MyEnumProcedure            - For EnumResourceNames()
*               GetIconFromInstance        - Extracts Icon from Instance
*               ChooseIconFromEXEFile      - Gets a user's choice icon from file
*               WriteICOHeader             - Writes ICO file header
*               CalculateImageOffset       - Calcs offset in file of image
*               DIBToIconImage             - Converts DIB to icon image
*
*     Copyright 1995 - 1998 Microsoft Corp.
*
*
* History:
*                July '95 - Created
*
\****************************************************************************/
#include <Windows.h>
#include "Resource.h"
#include "IconPro.h"
#include "Icons.H"
#include "Dib.H"

/****************************************************************************/
// Structs used locally (file scope)
// Resource Position info - size and offset of a resource in a file
typedef struct
{
    DWORDdwBytes;
    DWORDdwOffset;
} RESOURCEPOSINFO, *LPRESOURCEPOSINFO;
// EXE/DLL icon information - filename, instance handle and ID
typedef struct
{
    LPCTSTR    szFileName;
    HINSTANCEhInstance;
    LPTSTR    lpID;
} EXEDLLICONINFO, *LPEXEDLLICONINFO;
/****************************************************************************/

/****************************************************************************/
// External Globals
extern HINSTANCE    hInst;
extern HWND        hWndMain;
/****************************************************************************/

/****************************************************************************/
// Prototypes for local functions
UINT ReadICOHeader( HANDLE hFile );
BOOL AdjustIconImagePointers( LPICONIMAGE lpImage );
BOOL CALLBACK ExtractDlgProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK MyEnumProcedure( HANDLE  hModule, LPCTSTR  lpszType, LPTSTR  lpszName, LONG  lParam );
HICON GetIconFromInstance( HINSTANCE hInstance, LPTSTR nIndex );
LPTSTR ChooseIconFromEXEFile( LPEXEDLLICONINFO lpEDII );
BOOL WriteICOHeader( HANDLE hFile, UINT nNumEntries );
DWORD CalculateImageOffset( LPICONRESOURCE lpIR, UINT nIndex );
BOOL DIBToIconImage( LPICONIMAGE lpii, LPBYTE lpDIB, BOOL bStretch );
/****************************************************************************/

/****************************************************************************
*
*     FUNCTION: MakeIconFromResource
*
*     PURPOSE:  Makes an HICON from an icon resource
*
*     PARAMS:   LPICONIMAGElpIcon - pointer to the icon resource
*
*     RETURNS:  HICON - handle to the new icon, NULL for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
HICON MakeIconFromResource( LPICONIMAGE lpIcon )
{
    HICON        hIcon = NULL;

    // Sanity Check
    if( lpIcon == NULL )
        return NULL;
    if( lpIcon->lpBits == NULL )
        return NULL;
    // Let the OS do the real work :)
    hIcon = CreateIconFromResourceEx( lpIcon->lpBits, lpIcon->dwNumBytes, TRUE, 0x00030000,
            (*(LPBITMAPINFOHEADER)(lpIcon->lpBits)).biWidth, (*(LPBITMAPINFOHEADER)(lpIcon->lpBits)).biHeight/2, 0 );

    // It failed, odds are good we're on NT so try the non-Ex way
    if( hIcon == NULL )
    {
        // We would break on NT if we try with a 16bpp image
        if(lpIcon->lpbi->bmiHeader.biBitCount != 16)
        {
            hIcon = CreateIconFromResource( lpIcon->lpBits, lpIcon->dwNumBytes, TRUE, 0x00030000 );
        }
    }
    return hIcon;
}
/* End MakeIconFromResource() **********************************************/

/****************************************************************************
*
*     FUNCTION: ReadIconFromICOFile
*
*     PURPOSE:  Reads an Icon Resource from an ICO file
*
*     PARAMS:   LPCTSTR szFileName - Name of the ICO file
*
*     RETURNS:  LPICONRESOURCE - pointer to the resource, NULL for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
LPICONRESOURCE ReadIconFromICOFile( LPCTSTR szFileName )
{
    LPICONRESOURCE    lpIR = NULL, lpNew = NULL;
    HANDLE            hFile = NULL;
    LPRESOURCEPOSINFOlpRPI = NULL;
    UINT                i;
    DWORD            dwBytesRead;
    LPICONDIRENTRY    lpIDE = NULL;

    // Open the file
    if( (hFile = CreateFile( szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE )
    {
        MessageBox( hWndMain, "Error Opening File for Reading", szFileName, MB_OK );
        return NULL;
    }
    // Allocate memory for the resource structure
    if( (lpIR = malloc( sizeof(ICONRESOURCE) )) == NULL )
    {
        MessageBox( hWndMain, "Error Allocating Memory", szFileName, MB_OK );
        CloseHandle( hFile );
        return NULL;
    }
    // Read in the header
    if( (lpIR->nNumImages = ReadICOHeader( hFile )) == (UINT)-1 )
    {
        MessageBox( hWndMain, "Error Reading File Header", szFileName, MB_OK );
        CloseHandle( hFile );
        free( lpIR );
        return NULL;
    }
    // Adjust the size of the struct to account for the images
    if( (lpNew = realloc( lpIR, sizeof(ICONRESOURCE) + ((lpIR->nNumImages-1) * sizeof(ICONIMAGE)) )) == NULL )
    {
        MessageBox( hWndMain, "Error Allocating Memory", szFileName, MB_OK );
        CloseHandle( hFile );
        free( lpIR );
        return NULL;
    }
    lpIR = lpNew;
    // Store the original name
    lstrcpy( lpIR->szOriginalICOFileName, szFileName );
    lstrcpy( lpIR->szOriginalDLLFileName, "" );
    // Allocate enough memory for the icon directory entries
    if( (lpIDE = malloc( lpIR->nNumImages * sizeof( ICONDIRENTRY ) ) ) == NULL )
    {
        MessageBox( hWndMain, "Error Allocating Memory", szFileName, MB_OK );
        CloseHandle( hFile );
        free( lpIR );
        return NULL;
    }
    // Read in the icon directory entries
    if( ! ReadFile( hFile, lpIDE, lpIR->nNumImages * sizeof( ICONDIRENTRY ), &dwBytesRead, NULL ) )
    {
        MessageBox( hWndMain, "Error Reading File", szFileName, MB_OK );
        CloseHandle( hFile );
        free( lpIR );
        return NULL;
    }
    if( dwBytesRead != lpIR->nNumImages * sizeof( ICONDIRENTRY ) )
    {
        MessageBox( hWndMain, "Error Reading File", szFileName, MB_OK );
        CloseHandle( hFile );
        free( lpIR );
        return NULL;
    }
    // Loop through and read in each image
    for( i = 0; i < lpIR->nNumImages; i++ )
    {
        // Allocate memory for the resource
        if( (lpIR->IconImages[i].lpBits = malloc(lpIDE[i].dwBytesInRes)) == NULL )
        {
            MessageBox( hWndMain, "Error Allocating Memory", szFileName, MB_OK );
            CloseHandle( hFile );
            free( lpIR );
            free( lpIDE );
            return NULL;
        }
        lpIR->IconImages[i].dwNumBytes = lpIDE[i].dwBytesInRes;
        // Seek to beginning of this image
        if( SetFilePointer( hFile, lpIDE[i].dwImageOffset, NULL, FILE_BEGIN ) == 0xFFFFFFFF )
        {
            MessageBox( hWndMain, "Error Seeking in File", szFileName, MB_OK );
            CloseHandle( hFile );
            free( lpIR );
            free( lpIDE );
            return NULL;
        }
        // Read it in
        if( ! ReadFile( hFile, lpIR->IconImages[i].lpBits, lpIDE[i].dwBytesInRes, &dwBytesRead, NULL ) )
        {
            MessageBox( hWndMain, "Error Reading File", szFileName, MB_OK );
            CloseHandle( hFile );
            free( lpIR );
            free( lpIDE );
            return NULL;
        }
        if( dwBytesRead != lpIDE[i].dwBytesInRes )
        {
            MessageBox( hWndMain, "Error Reading File", szFileName, MB_OK );
            CloseHandle( hFile );
            free( lpIDE );
            free( lpIR );
            return NULL;
        }
        // Set the internal pointers appropriately
        if( ! AdjustIconImagePointers( &(lpIR->IconImages[i]) ) )
        {
            MessageBox( hWndMain, "Error Converting to Internal Format", szFileName, MB_OK );
            CloseHandle( hFile );
            free( lpIDE );
            free( lpIR );
            return NULL;
        }
    }
    // Clean up
    free( lpIDE );
    free( lpRPI );
    CloseHandle( hFile );
    return lpIR;
}
/* End ReadIconFromICOFile() **********************************************/

/****************************************************************************
*
*     FUNCTION: AdjustIconImagePointers
*
*     PURPOSE:  Adjusts internal pointers in icon resource struct
*
*     PARAMS:   LPICONIMAGE lpImage - the resource to handle
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL AdjustIconImagePointers( LPICONIMAGE lpImage )
{
    // Sanity check
    if( lpImage==NULL )
        return FALSE;
    // BITMAPINFO is at beginning of bits
    lpImage->lpbi = (LPBITMAPINFO)lpImage->lpBits;
    // Width - simple enough
    lpImage->Width = lpImage->lpbi->bmiHeader.biWidth;
    // Icons are stored in funky format where height is doubled - account for it
    lpImage->Height = (lpImage->lpbi->bmiHeader.biHeight)/2;
    // How many colors?
    lpImage->Colors = lpImage->lpbi->bmiHeader.biPlanes * lpImage->lpbi->bmiHeader.biBitCount;
    // XOR bits follow the header and color table
    lpImage->lpXOR = FindDIBBits((LPSTR)lpImage->lpbi);
    // AND bits follow the XOR bits
    lpImage->lpAND = lpImage->lpXOR + (lpImage->Height*BytesPerLine((LPBITMAPINFOHEADER)(lpImage->lpbi)));
    return TRUE;
}
/* End AdjustIconImagePointers() *******************************************/

/****************************************************************************
*
*     FUNCTION: ReadICOHeader
*
*     PURPOSE:  Reads the header from an ICO file
*
*     PARAMS:   HANDLE hFile - handle to the file
*
*     RETURNS:  UINT - Number of images in file, -1 for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
UINT ReadICOHeader( HANDLE hFile )
{
    WORD    Input;
    DWORDdwBytesRead;

    // Read the 'reserved' WORD
    if( ! ReadFile( hFile, &Input, sizeof( WORD ), &dwBytesRead, NULL ) )
        return (UINT)-1;
    // Did we get a WORD?
    if( dwBytesRead != sizeof( WORD ) )
        return (UINT)-1;
    // Was it 'reserved' ?   (ie 0)
    if( Input != 0 )
        return (UINT)-1;
    // Read the type WORD
    if( ! ReadFile( hFile, &Input, sizeof( WORD ), &dwBytesRead, NULL ) )
        return (UINT)-1;
    // Did we get a WORD?
    if( dwBytesRead != sizeof( WORD ) )
        return (UINT)-1;
    // Was it type 1?
    if( Input != 1 )
        return (UINT)-1;
    // Get the count of images
    if( ! ReadFile( hFile, &Input, sizeof( WORD ), &dwBytesRead, NULL ) )
        return (UINT)-1;
    // Did we get a WORD?
    if( dwBytesRead != sizeof( WORD ) )
        return (UINT)-1;
    // Return the count
    return Input;
}
/* End ReadICOHeader() ****************************************************/

/****************************************************************************
*
*     FUNCTION: MyEnumProcedure
*
*     PURPOSE:  Callback for enumerating resources in a DLL/EXE
*
*     PARAMS:   HANDLE  hModule  - Handle of the module
*               LPCTSTR lpszType - Resource Type
*               LPTSTR  lpszName - Resource Name
*               LONG    lParam   - Handle of ListBox to add name to
*
*     RETURNS:  BOOL - TRUE to continue, FALSE to stop
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL CALLBACK MyEnumProcedure( HANDLE  hModule, LPCTSTR  lpszType, LPTSTR  lpszName, LONG  lParam )
{
    TCHARszBuffer[256];
    LONG    nIndex = LB_ERR;
    LPTSTRlpID = NULL;

    // Name is from MAKEINTRESOURCE()
    if( HIWORD(lpszName) == 0 )
    {
        wsprintf( szBuffer, "Icon [%d]", (DWORD)lpszName );
        lpID = lpszName;
    }
    else
    {
        // Name is string
        lpID = strdup( lpszName );
        wsprintf( szBuffer, "Icon [%s]", lpID );
    }
    // Add it to the listbox
    nIndex = SendDlgItemMessage( (HWND)lParam, IDC_LIST1, LB_ADDSTRING, 0, (LPARAM)(szBuffer) );
    // Set the item data to be the name of the resource so we can get it later
    SendDlgItemMessage( (HWND)lParam, IDC_LIST1, LB_SETITEMDATA, (WPARAM)nIndex, (LPARAM)lpID );
    return TRUE;
}
/* End MyEnumProcedure() ***************************************************/

/****************************************************************************
*
*     FUNCTION: GetIconFromInstance
*
*     PURPOSE:  Callback for enumerating resources in a DLL/EXE
*
*     PARAMS:   HINSTANCE hInstance - Instance handle for this module
*               LPTSTR    nIndex    - Resource index
*
*     RETURNS:  HICON - Handle to the icon, NULL for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
HICON GetIconFromInstance( HINSTANCE hInstance, LPTSTR nIndex )
{
    HICONhIcon = NULL;
    HRSRChRsrc = NULL;
    HGLOBALhGlobal = NULL;
    LPVOIDlpRes = NULL;
    int    nID;

    // Find the group icon
    if( (hRsrc = FindResource( hInstance, nIndex, RT_GROUP_ICON )) == NULL )
        return NULL;
    if( (hGlobal = LoadResource( hInstance, hRsrc )) == NULL )
        return NULL;
    if( (lpRes = LockResource(hGlobal)) == NULL )
        return NULL;

    // Find this particular image
    nID = LookupIconIdFromDirectory( lpRes, TRUE );
    if( (hRsrc = FindResource( hInstance, MAKEINTRESOURCE(nID), RT_ICON )) == NULL )
        return NULL;
    if( (hGlobal = LoadResource( hInstance, hRsrc )) == NULL )
        return NULL;
    if( (lpRes = LockResource(hGlobal)) == NULL )
        return NULL;
    // Let the OS make us an icon
    hIcon = CreateIconFromResource( lpRes, SizeofResource(hInstance,hRsrc), TRUE, 0x00030000 );
    return hIcon;
}
/* End GetIconFromInstance() ***********************************************/

/****************************************************************************
*
*     FUNCTION: ExtractDlgProc
*
*     PURPOSE:  Window Procedure for the Extract Dialog
*
*     PARAMS:   HWND hWnd     - This window handle
*               UINT Msg      - Which Message?
*               WPARAM wParam - message parameter
*               LPARAM lParam - message parameter
*
*     RETURNS:  BOOL - FALSE for cancel, TRUE for ok
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL CALLBACK ExtractDlgProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
    // Variable that holds info on this EXE/DLL
    static LPEXEDLLICONINFO lpEDII;

    switch( Msg )
    {
        // During Paint, we will draw the currently selected icon
        case WM_PAINT:
        {
            HDC                hDC;
            PAINTSTRUCT        ps;
            DWORD            nIndex;
            LPTSTR            lpIconID;

            hDC = BeginPaint( hWnd, &ps );
            // Get the current selection
            if( (nIndex = SendDlgItemMessage( hWnd, IDC_LIST1, LB_GETCURSEL, 0, 0 )) != CB_ERR )
            {
                // Get the data associated with the current selection - its the icon name
                if( (lpIconID = (LPTSTR)SendDlgItemMessage( hWnd, IDC_LIST1, LB_GETITEMDATA, nIndex, 0 )) != (LPTSTR)CB_ERR )
                {
                    RECT        Rect, ButtonRect, DlgRect;
                    HWND        hWndButton;
                    HICON    hIcon;
                    ICONINFO    IconInfo;
                    BITMAP    bm;
                    POINT    UpperLeft, LowerRight;

                    // Make an Icon
                    hIcon = GetIconFromInstance( lpEDII->hInstance, lpIconID );
                    // Locate the icon
                    GetIconInfo( hIcon, &IconInfo );
                    GetObject( IconInfo.hbmColor, sizeof(BITMAP), &bm );
                    hWndButton = GetDlgItem( hWnd, IDCANCEL );
                    GetWindowRect( hWndButton, &ButtonRect );
                    GetWindowRect( hWnd, &DlgRect );
                    UpperLeft.x = ButtonRect.left;
                    UpperLeft.y = ButtonRect.bottom;
                    LowerRight.x = ButtonRect.right;
                    LowerRight.y = DlgRect.bottom;
                    ScreenToClient( hWnd, &UpperLeft );
                    ScreenToClient( hWnd, &LowerRight );
                    SetRect( &Rect, UpperLeft.x, UpperLeft.y, LowerRight.x, LowerRight.y );
                    // Draw it
                    DrawIcon( hDC, Rect.left + ((Rect.right - Rect.left - bm.bmWidth)/2),
                            Rect.top + ((Rect.bottom - Rect.top - bm.bmHeight)/2), hIcon );
                    // Kill it
                    DestroyIcon( hIcon );
                }
            }
            EndPaint( hWnd, &ps );
        }
        break; // End WM_PAINT

        // Dialog is being initialized
        case WM_INITDIALOG:
        {
            UINT    nCount;
            TCHARszBuffer[MAX_PATH], szFileTitle[MAX_PATH];

            // Are we being sent data about an EXE/DLL?
            if( (lpEDII = (LPEXEDLLICONINFO)lParam) != NULL )
            {
                // Set the title of the dialog to reflect the EXE/DLL filename
                GetFileTitle( lpEDII->szFileName, szFileTitle, MAX_PATH );
                wsprintf( szBuffer, "Extract Icon [%s]", szFileTitle );
                SetWindowText( hWnd, szBuffer );
                // Fill in the listbox with the icons available
                if( ! EnumResourceNames( lpEDII->hInstance, RT_GROUP_ICON, MyEnumProcedure, (LPARAM)hWnd ) )
                {
                    MessageBox( hWnd, "Error Enumerating Icons", "Error", MB_OK );
                    PostMessage( hWnd, WM_CLOSE, 0, 0 );
                }
                SendDlgItemMessage( hWnd, IDC_LIST1, LB_SETCURSEL, 0, 0 );
                // If we have <= 1, post an OK message
                if( (nCount = SendDlgItemMessage(hWnd, IDC_LIST1, LB_GETCOUNT, 0, 0)) == 1 )
                {
                    PostMessage( hWnd, WM_COMMAND, IDOK, 0 );
                }
                // If there were no icons, let the user know
                if( nCount == 0 )
                {
                    MessageBox( hWnd, "No Icons in this File", "Error", MB_OK );
                    PostMessage( hWnd, WM_CLOSE, 0, 0 );
                }
            }
            return FALSE;
        }
        break; // End WM_INITDIALOG

        // Shut 'er down
        case WM_CLOSE:
            PostMessage( hWnd, WM_COMMAND, IDCANCEL, 0l );
        break; // End WM_CLOSE

        // Children are sending messages
        case WM_COMMAND:
            switch( LOWORD(wParam) )
            {
                // Its the listbox, just redraw the icon
                case IDC_LIST1:
                    switch( HIWORD(wParam) )
                    {
                        case CBN_SELCHANGE:
                        case CBN_SELENDOK:
                            InvalidateRect( hWnd, NULL, TRUE );
                        break;
                    }
                break; // End IDC_LIST1

                // User has chosen an icon, shut it down
                case IDOK:
                {
                    LONG nIndex;

                    lpEDII->lpID = NULL;
                    if( (nIndex = SendDlgItemMessage( hWnd, IDC_LIST1, LB_GETCURSEL, 0, 0 )) != LB_ERR )
                        lpEDII->lpID = (LPTSTR)SendDlgItemMessage( hWnd, IDC_LIST1, LB_GETITEMDATA, nIndex, 0 );
                    EndDialog( hWnd, TRUE );
                }
                break; // End IDOK

                // BAIL!
                case IDCANCEL:
                    EndDialog( hWnd, FALSE );
                break; // End IDCANCEL

            }
        break;
        default:
            return FALSE;
        break;
    }
    return TRUE;
}
/* End ExtractDlgProc() ****************************************************/

/****************************************************************************
*
*     FUNCTION: ChooseIconFromEXEFile
*
*     PURPOSE:  Ask the user which icon he/she wants from the DLL/EXE
*
*     PARAMS:   LPEXEDLLICONINFO lpEDII - info on this DLL/EXE
*
*     RETURNS:  LPTSTR - pointer to the resource name
*
* History:
*                July '95 - Created
*
\****************************************************************************/
LPTSTR ChooseIconFromEXEFile( LPEXEDLLICONINFO lpEDII )
{
    // Just launch the dialog box and let it handle it
    if( DialogBoxParam( hInst, MAKEINTRESOURCE(IDD_EXTRACTDLG), hWndMain, ExtractDlgProc, (LPARAM)(lpEDII) ) )
    {
        // User chose 'Ok'
        return lpEDII->lpID;
    }
    // User chose 'Cancel', or an error occurred, fail the call
    return NULL;
}
/* End ChooseIconFromEXEFile() **********************************************/

/****************************************************************************
*
*     FUNCTION: ReadIconFromEXEFile
*
*     PURPOSE:  Load an Icon Resource from a DLL/EXE file
*
*     PARAMS:   LPCTSTR szFileName - name of DLL/EXE file
*
*     RETURNS:  LPICONRESOURCE - pointer to icon resource
*
* History:
*                July '95 - Created
*
\****************************************************************************/
LPICONRESOURCE ReadIconFromEXEFile( LPCTSTR szFileName )
{
    LPICONRESOURCE    lpIR = NULL, lpNew = NULL;
    HINSTANCE        hLibrary;
    LPTSTR            lpID;
    EXEDLLICONINFO    EDII;

    // Load the DLL/EXE - NOTE: must be a 32bit EXE/DLL for this to work
    if( (hLibrary = LoadLibraryEx( szFileName, NULL, LOAD_LIBRARY_AS_DATAFILE )) == NULL )
    {
        // Failed to load - abort
        MessageBox( hWndMain, "Error Loading File - Choose a 32bit DLL or EXE", szFileName, MB_OK );
        return NULL;
    }
    // Store the info
    EDII.szFileName = szFileName;
    EDII.hInstance = hLibrary;
    // Ask the user, "Which Icon?"
    if( (lpID = ChooseIconFromEXEFile( &EDII )) != NULL )
    {
        HRSRC        hRsrc = NULL;
        HGLOBAL        hGlobal = NULL;
        LPMEMICONDIR    lpIcon = NULL;
        UINT            i;

        // Find the group icon resource
        if( (hRsrc = FindResource( hLibrary, lpID, RT_GROUP_ICON )) == NULL )
        {
            FreeLibrary( hLibrary );
            return NULL;
        }
        if( (hGlobal = LoadResource( hLibrary, hRsrc )) == NULL )
        {
            FreeLibrary( hLibrary );
            return NULL;
        }
        if( (lpIcon = LockResource(hGlobal)) == NULL )
        {
            FreeLibrary( hLibrary );
            return NULL;
        }
        // Allocate enough memory for the images
        if( (lpIR = malloc( sizeof(ICONRESOURCE) + ((lpIcon->idCount-1) * sizeof(ICONIMAGE)) )) == NULL )
        {
            MessageBox( hWndMain, "Error Allocating Memory", szFileName, MB_OK );
            FreeLibrary( hLibrary );
            return NULL;
        }
        // Fill in local struct members
        lpIR->nNumImages = lpIcon->idCount;
        lstrcpy( lpIR->szOriginalDLLFileName, szFileName );
        lstrcpy( lpIR->szOriginalICOFileName, "" );
        // Loop through the images
        for( i = 0; i < lpIR->nNumImages; i++ )
        {
            // Get the individual image
            if( (hRsrc = FindResource( hLibrary, MAKEINTRESOURCE(lpIcon->idEntries[i].nID), RT_ICON )) == NULL )
            {
                free( lpIR );
                FreeLibrary( hLibrary );
                return NULL;
            }
            if( (hGlobal = LoadResource( hLibrary, hRsrc )) == NULL )
            {
                free( lpIR );
                FreeLibrary( hLibrary );
                return NULL;
            }
            // Store a copy of the resource locally
            lpIR->IconImages[i].dwNumBytes = SizeofResource( hLibrary, hRsrc );
            lpIR->IconImages[i].lpBits = malloc( lpIR->IconImages[i].dwNumBytes );
            memcpy( lpIR->IconImages[i].lpBits, LockResource( hGlobal ), lpIR->IconImages[i].dwNumBytes );
            // Adjust internal pointers
            if( ! AdjustIconImagePointers( &(lpIR->IconImages[i]) ) )
            {
                MessageBox( hWndMain, "Error Converting to Internal Format", szFileName, MB_OK );
                free( lpIR );
                FreeLibrary( hLibrary );
                return NULL;
            }
        }
    }
    FreeLibrary( hLibrary );
    return lpIR;
}
/* End ReadIconFromEXEFile() ************************************************/

/****************************************************************************
*
*     FUNCTION: WriteICOHeader
*
*     PURPOSE:  Writes the header to an ICO file
*
*     PARAMS:   HANDLE hFile       - handle to the file
*               UINT   nNumEntries - Number of images in file
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL WriteICOHeader( HANDLE hFile, UINT nNumEntries )
{
    WORD    Output;
    DWORDdwBytesWritten;

    // Write 'reserved' WORD
    Output = 0;
    if( ! WriteFile( hFile, &Output, sizeof( WORD ), &dwBytesWritten, NULL ) )
        return FALSE;
    // Did we write a WORD?
    if( dwBytesWritten != sizeof( WORD ) )
        return FALSE;
    // Write 'type' WORD (1)
    Output = 1;
    if( ! WriteFile( hFile, &Output, sizeof( WORD ), &dwBytesWritten, NULL ) )
        return FALSE;
    // Did we write a WORD?
    if( dwBytesWritten != sizeof( WORD ) )
        return FALSE;
    // Write Number of Entries
    Output = (WORD)nNumEntries;
    if( ! WriteFile( hFile, &Output, sizeof( WORD ), &dwBytesWritten, NULL ) )
        return FALSE;
    // Did we write a WORD?
    if( dwBytesWritten != sizeof( WORD ) )
        return FALSE;
    return TRUE;
}
/* End WriteICOHeader() ****************************************************/

/****************************************************************************
*
*     FUNCTION: CalculateImageOffset
*
*     PURPOSE:  Calculates the file offset for an icon image
*
*     PARAMS:   LPICONRESOURCE lpIR   - pointer to icon resource
*               UINT           nIndex - which image?
*
*     RETURNS:  DWORD - the file offset for that image
*
* History:
*                July '95 - Created
*
\****************************************************************************/
DWORD CalculateImageOffset( LPICONRESOURCE lpIR, UINT nIndex )
{
    DWORDdwSize;
    UINT    i;

    // Calculate the ICO header size
    dwSize = 3 * sizeof(WORD);
    // Add the ICONDIRENTRY's
    dwSize += lpIR->nNumImages * sizeof(ICONDIRENTRY);
    // Add the sizes of the previous images
    for(i=0;i<nIndex;i++)
        dwSize += lpIR->IconImages[i].dwNumBytes;
    // we're there - return the number
    return dwSize;
}
/* End CalculateImageOffset() ***********************************************/

/****************************************************************************
*
*     FUNCTION: WriteIconToICOFile
*
*     PURPOSE:  Writes the icon resource data to an ICO file
*
*     PARAMS:   LPICONRESOURCE lpIR       - pointer to icon resource
*               LPCTSTR        szFileName - name for the ICO file
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL WriteIconToICOFile( LPICONRESOURCE lpIR, LPCTSTR szFileName )
{
    HANDLE    hFile;
    UINT        i;
    DWORD    dwBytesWritten;

    // open the file
    if( (hFile = CreateFile( szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE )
    {
        MessageBox( hWndMain, "Error Opening File for Writing", szFileName, MB_OK );
        return FALSE;
    }
    // Write the header
    if( ! WriteICOHeader( hFile, lpIR->nNumImages ) )
    {
        MessageBox( hWndMain, "Error Writing ICO File", szFileName, MB_OK );
        CloseHandle( hFile );
        return FALSE;
    }
    // Write the ICONDIRENTRY's
    for( i=0; i<lpIR->nNumImages; i++ )
    {
        ICONDIRENTRY    ide;

        // Convert internal format to ICONDIRENTRY
        ide.bWidth = lpIR->IconImages[i].Width;
        ide.bHeight = lpIR->IconImages[i].Height;
        ide.bReserved = 0;
        ide.wPlanes = lpIR->IconImages[i].lpbi->bmiHeader.biPlanes;
        ide.wBitCount = lpIR->IconImages[i].lpbi->bmiHeader.biBitCount;
        if( (ide.wPlanes * ide.wBitCount) >= 8 )
            ide.bColorCount = 0;
        else
            ide.bColorCount = 1 << (ide.wPlanes * ide.wBitCount);
        ide.dwBytesInRes = lpIR->IconImages[i].dwNumBytes;
        ide.dwImageOffset = CalculateImageOffset( lpIR, i );
        // Write the ICONDIRENTRY out to disk
        if( ! WriteFile( hFile, &ide, sizeof( ICONDIRENTRY ), &dwBytesWritten, NULL ) )
            return FALSE;
        // Did we write a full ICONDIRENTRY ?
        if( dwBytesWritten != sizeof( ICONDIRENTRY ) )
            return FALSE;
    }
    // Write the image bits for each image
    for( i=0; i<lpIR->nNumImages; i++ )
    {
        DWORD dwTemp = lpIR->IconImages[i].lpbi->bmiHeader.biSizeImage;

        // Set the sizeimage member to zero
        lpIR->IconImages[i].lpbi->bmiHeader.biSizeImage = 0;
        // Write the image bits to file
        if( ! WriteFile( hFile, lpIR->IconImages[i].lpBits, lpIR->IconImages[i].dwNumBytes, &dwBytesWritten, NULL ) )
            return FALSE;
        if( dwBytesWritten != lpIR->IconImages[i].dwNumBytes )
            return FALSE;
        // set it back
        lpIR->IconImages[i].lpbi->bmiHeader.biSizeImage = dwTemp;
    }
    CloseHandle( hFile );
    return FALSE;
}
/* End WriteIconToICOFile() **************************************************/

/****************************************************************************
*
*     FUNCTION: IconImageToClipBoard
*
*     PURPOSE:  Copies an icon image to the clipboard in CF_DIB format
*
*     PARAMS:   LPICONIMAGE lpii - pointer to icon image data
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL IconImageToClipBoard( LPICONIMAGE lpii )
{
    HANDLEhGlobal;
    LPSTRlpBits;

    // Open the clipboard
    if( OpenClipboard( hWndMain ) )
    {
        // empty it
        if( EmptyClipboard() )
        {
            // Make a buffer to send to clipboard
            hGlobal = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, lpii->dwNumBytes );
            lpBits = GlobalLock( hGlobal );
            // Copy the bits to the buffer
            memcpy( lpBits, lpii->lpBits, lpii->dwNumBytes );
            // Adjust for funky height*2 thing
            ((LPBITMAPINFOHEADER)lpBits)->biHeight /= 2;
            GlobalUnlock( hGlobal );
            // Send it to the clipboard
            SetClipboardData( CF_DIB, hGlobal );
            CloseClipboard();
            return TRUE;
        }
    }
    return FALSE;
}
/* End IconImageToClipBoard() ***********************************************/

/****************************************************************************
*
*     FUNCTION: IconImageFromClipBoard
*
*     PURPOSE:  Creates an icon image from the CF_DIB clipboard entry
*
*     PARAMS:   LPICONIMAGE lpii          - pointer to icon image data
*               BOOL        bStretchToFit - TRUE to stretch, FALSE to take
*                                           the upper left corner of the DIB
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL IconImageFromClipBoard( LPICONIMAGE lpii, BOOL bStretchToFit )
{
    LPBITMAPINFO    lpbi;
    HANDLE        hClipGlobal;
    BOOL            bRet = FALSE;

    // Open the clipboard
    if( OpenClipboard( hWndMain ) )
    {
        // Get the CF_DIB data from it
        if( (hClipGlobal = GetClipboardData( CF_DIB )) != NULL )
        {
            // Lock it down
            if( (lpbi=GlobalLock(hClipGlobal)) != NULL )
            {
                // Convert it to an icon image
                bRet = DIBToIconImage( lpii, (LPBYTE)lpbi, bStretchToFit );
                GlobalUnlock( hClipGlobal );
            }
        }
        CloseClipboard();
    }
    return bRet;
}
/* End IconImageFromClipBoard() ********************************************/

/****************************************************************************
*
*     FUNCTION: DIBToIconImage
*
*     PURPOSE:  Converts a CF_DIB memory block to an icon image
*
*     PARAMS:   LPICONIMAGE lpii          - pointer to icon image data
*               LPBYTE      lpDIB         - a pointer to the CF_DIB block
*               BOOL        bStretchToFit - TRUE to stretch, FALSE to take
*                                           the upper left corner of the DIB
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL DIBToIconImage( LPICONIMAGE lpii, LPBYTE lpDIB, BOOL bStretch )
{
    LPBYTE    lpNewDIB;

    // Sanity check
    if( lpDIB == NULL )
        return FALSE;

    // Let the DIB engine convert color depths if need be
    lpNewDIB = ConvertDIBFormat( (LPBITMAPINFO)lpDIB, lpii->Width, lpii->Height, lpii->Colors, bStretch );

    // Now we have a cool new DIB of the proper size/color depth
    // Lets poke it into our data structures and be done with it

    // How big is it?
    lpii->dwNumBytes = sizeof( BITMAPINFOHEADER )                    // Header
                    + PaletteSize( (LPSTR)lpNewDIB )                    // Palette
                    + lpii->Height * BytesPerLine( (LPBITMAPINFOHEADER)lpNewDIB )// XOR mask
                    + lpii->Height * WIDTHBYTES( lpii->Width );        // AND mask

    // If there was already an image here, free it
    if( lpii->lpBits != NULL )
        free( lpii->lpBits );
    // Allocate enough room for the new image
    if( (lpii->lpBits = malloc( lpii->dwNumBytes )) == NULL )
    {
        free( lpii );
        return FALSE;
    }
    // Copy the bits
    memcpy( lpii->lpBits, lpNewDIB, sizeof( BITMAPINFOHEADER ) + PaletteSize( (LPSTR)lpNewDIB ) );
    // Adjust internal pointers/variables for new image
    lpii->lpbi = (LPBITMAPINFO)(lpii->lpBits);
    lpii->lpbi->bmiHeader.biHeight *= 2;
    lpii->lpXOR = FindDIBBits( (LPSTR)(lpii->lpBits) );
    memcpy( lpii->lpXOR, FindDIBBits((LPSTR)lpNewDIB), lpii->Height * BytesPerLine( (LPBITMAPINFOHEADER)lpNewDIB ) );
    lpii->lpAND = lpii->lpXOR + lpii->Height * BytesPerLine( (LPBITMAPINFOHEADER)lpNewDIB );
    memset( lpii->lpAND, 0, lpii->Height * WIDTHBYTES( lpii->Width ) );
    // Free the source
    free( lpNewDIB );
    return TRUE;
}
/* End DIBToIconImage() ***************************************************/

/****************************************************************************
*
*     FUNCTION: CreateBlankNewFormatIcon
*
*     PURPOSE:  Creates a blank icon image for a new format
*
*     PARAMS:   LPICONIMAGE lpii          - pointer to icon image data
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL CreateBlankNewFormatIcon( LPICONIMAGE lpii )
{
    DWORD            dwFinalSize;
    BITMAPINFOHEADER    bmih;

    // Fill in the bitmap header
    ZeroMemory( &bmih, sizeof( BITMAPINFOHEADER ) );
    bmih.biSize = sizeof( BITMAPINFOHEADER );
    bmih.biBitCount = lpii->Colors;
    bmih.biClrUsed = 0;

    // How big will the final thing be?
    // Well, it'll have a header
    dwFinalSize = sizeof( BITMAPINFOHEADER );
    // and a color table (even if it's zero length)
    dwFinalSize += PaletteSize( (LPSTR)&bmih );
    // and XOR bits
    dwFinalSize += lpii->Height * WIDTHBYTES( lpii->Width * lpii->Colors );
    // and AND bits. That's about it :)
    dwFinalSize += lpii->Height * WIDTHBYTES( lpii->Width );

    // Allocate some memory for it
    lpii->lpBits = malloc( dwFinalSize );
    ZeroMemory( lpii->lpBits, dwFinalSize );
    lpii->dwNumBytes = dwFinalSize;
    lpii->lpbi = (LPBITMAPINFO)(lpii->lpBits);
    lpii->lpXOR = (LPSTR)(lpii->lpbi) + sizeof(BITMAPINFOHEADER) + PaletteSize( (LPSTR)&bmih );
    lpii->lpAND = lpii->lpXOR + (lpii->Height * WIDTHBYTES( lpii->Width * lpii->Colors ));

    // The bitmap header is zeros, fill it out
    lpii->lpbi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
    lpii->lpbi->bmiHeader.biWidth = lpii->Width;
    // Don't forget the funky height*2 icon resource thing
    lpii->lpbi->bmiHeader.biHeight = lpii->Height * 2;
    lpii->lpbi->bmiHeader.biPlanes = 1;
    lpii->lpbi->bmiHeader.biBitCount = lpii->Colors;
    lpii->lpbi->bmiHeader.biCompression = BI_RGB;

    return TRUE;
}
/* End CreateBlankNewFormatIcon() ******************************************/

/****************************************************************************
*
*     FUNCTION: GetXORImageRect
*
*     PURPOSE:  Given a bounding Rect, calculates the XOR mask display Rect
*
*     PARAMS:   RECT        Rect   - Bounding rect for drawing area
*               LPICONIMAGE lpIcon - pointer to icon image data
*
*     RETURNS:  RECT - the rect where the XOR image will be drawn
*
* History:
*                July '95 - Created
*
\****************************************************************************/
RECT GetXORImageRect( RECT Rect, LPICONIMAGE lpIcon )
{
    RECT    NewRect;

    // Just center the thing in the bounding display rect
    NewRect.left = Rect.left + ((RectWidth(Rect)-lpIcon->lpbi->bmiHeader.biWidth)/2);
    NewRect.top = Rect.top + ((RectHeight(Rect)-(lpIcon->lpbi->bmiHeader.biHeight/2))/2);
    NewRect.bottom = NewRect.top + (lpIcon->lpbi->bmiHeader.biHeight/2);
    NewRect.right = NewRect.left + lpIcon->lpbi->bmiHeader.biWidth;
    return NewRect;
}
/* End GetXORImageRect() ***************************************************/

/****************************************************************************
*
*     FUNCTION: DrawXORMask
*
*     PURPOSE:  Using DIB functions, draw XOR mask on hDC in Rect
*
*     PARAMS:   HDC         hDC    - The DC on which to draw
*               RECT        Rect   - Bounding rect for drawing area
*               LPICONIMAGE lpIcon - pointer to icon image data
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
*     COMMENTS: Does not use any palette information since the
*               OS won't when it draws the icon anyway.
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL DrawXORMask( HDC hDC, RECT Rect, LPICONIMAGE lpIcon )
{
    int            x, y;

    // Sanity checks
    if( lpIcon == NULL )
        return FALSE;
    if( lpIcon->lpBits == NULL )
        return FALSE;

    // Account for height*2 thing
    lpIcon->lpbi->bmiHeader.biHeight /= 2;

    // Locate it
    x = Rect.left + ((RectWidth(Rect)-lpIcon->lpbi->bmiHeader.biWidth)/2);
    y = Rect.top + ((RectHeight(Rect)-lpIcon->lpbi->bmiHeader.biHeight)/2);

    // Blast it to the screen
    SetDIBitsToDevice( hDC, x, y, lpIcon->lpbi->bmiHeader.biWidth, lpIcon->lpbi->bmiHeader.biHeight, 0, 0, 0, lpIcon->lpbi->bmiHeader.biHeight, lpIcon->lpXOR, lpIcon->lpbi, DIB_RGB_COLORS );

    // UnAccount for height*2 thing
    lpIcon->lpbi->bmiHeader.biHeight *= 2;

    return TRUE;
}
/* End DrawXORMask() *******************************************************/

/****************************************************************************
*
*     FUNCTION: DrawANDMask
*
*     PURPOSE:  Using DIB functions, draw AND mask on hDC in Rect
*
*     PARAMS:   HDC         hDC    - The DC on which to draw
*               RECT        Rect   - Bounding rect for drawing area
*               LPICONIMAGE lpIcon - pointer to icon image data
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL DrawANDMask( HDC hDC, RECT Rect, LPICONIMAGE lpIcon )
{
    LPBITMAPINFO    lpbi;
    int            x, y;

    // Sanity checks
    if( lpIcon == NULL )
        return FALSE;
    if( lpIcon->lpBits == NULL )
        return FALSE;

    // Need a bitmap header for the mono mask
    lpbi = malloc( sizeof(BITMAPINFO) + (2 * sizeof( RGBQUAD )) );
    lpbi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
    lpbi->bmiHeader.biWidth = lpIcon->lpbi->bmiHeader.biWidth;
    lpbi->bmiHeader.biHeight = lpIcon->lpbi->bmiHeader.biHeight/2;
    lpbi->bmiHeader.biPlanes = 1;
    lpbi->bmiHeader.biBitCount = 1;
    lpbi->bmiHeader.biCompression = BI_RGB;
    lpbi->bmiHeader.biSizeImage = 0;
    lpbi->bmiHeader.biXPelsPerMeter = 0;
    lpbi->bmiHeader.biYPelsPerMeter = 0;
    lpbi->bmiHeader.biClrUsed = 0;
    lpbi->bmiHeader.biClrImportant = 0;
    lpbi->bmiColors[0].rgbRed = 0;
    lpbi->bmiColors[0].rgbGreen = 0;
    lpbi->bmiColors[0].rgbBlue = 0;
    lpbi->bmiColors[0].rgbReserved = 0;
    lpbi->bmiColors[1].rgbRed = 255;
    lpbi->bmiColors[1].rgbGreen = 255;
    lpbi->bmiColors[1].rgbBlue = 255;
    lpbi->bmiColors[1].rgbReserved = 0;

    // Locate it
    x = Rect.left + ((RectWidth(Rect)-lpbi->bmiHeader.biWidth)/2);
    y = Rect.top + ((RectHeight(Rect)-lpbi->bmiHeader.biHeight)/2);

    // Blast it to the screen
    SetDIBitsToDevice( hDC, x, y, lpbi->bmiHeader.biWidth, lpbi->bmiHeader.biHeight, 0, 0, 0, lpbi->bmiHeader.biHeight, lpIcon->lpAND, lpbi, DIB_RGB_COLORS );

    // clean up
    free( lpbi );

    return TRUE;
}
/* End DrawANDMask() *******************************************************/

/****************************************************************************
*
*     FUNCTION: MakeNewANDMaskBasedOnPoint
*
*     PURPOSE:  Creates a new AND mask for the icon image
*
*     PARAMS:   LPICONIMAGE lpIcon - pointer to icon image data
*               POINT       pt     - coords of transparent pixel
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
*     COMMENTS: Creates the AND mask using the color of the pixel at pt
*               as a transparent color. The XOR mask is changed as well.
*               This is because the OS expects the XOR mask to have the
*               AND mask already applied (ie black in transparent areas)
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL MakeNewANDMaskBasedOnPoint( LPICONIMAGE lpIcon, POINT pt )
{
    HBITMAP        hXORBitmap, hOldXORBitmap;
    HDC            hDC, hMemDC1;
    LPBYTE        pXORBits;
    COLORREF        crTransparentColor;
    LONG            i,j;

    // Account for height*2 thing
    lpIcon->lpbi->bmiHeader.biHeight /= 2;

    // Need a DC
    hDC = GetDC( NULL );

    // Use DIBSection for source
    hXORBitmap = CreateDIBSection( hDC, lpIcon->lpbi, DIB_RGB_COLORS, &pXORBits, NULL, 0  );
    memcpy( pXORBits, lpIcon->lpXOR, (lpIcon->lpbi->bmiHeader.biHeight) * BytesPerLine((LPBITMAPINFOHEADER)(lpIcon->lpbi)) );
    hMemDC1 = CreateCompatibleDC( hDC );
    hOldXORBitmap = SelectObject( hMemDC1, hXORBitmap );

    // Set the color table if need be
    if( lpIcon->lpbi->bmiHeader.biBitCount <= 8 )
        SetDIBColorTable( hMemDC1, 0, DIBNumColors((LPSTR)(lpIcon->lpbi)), lpIcon->lpbi->bmiColors);

    // What's the transparent color?
    crTransparentColor = GetPixel( hMemDC1, pt.x, pt.y );

    // Loop through the pixels
    for(i=0;i<lpIcon->lpbi->bmiHeader.biWidth;i++)
    {
        for(j=0;j<lpIcon->lpbi->bmiHeader.biHeight;j++)
        {
            // Is the source transparent at this point?
            if( GetPixel( hMemDC1, i, j ) == crTransparentColor )
            {
                // Yes, so set the pixel in AND mask, and clear it in XOR mask
                SetMonoDIBPixel( lpIcon->lpAND, lpIcon->lpbi->bmiHeader.biWidth, lpIcon->lpbi->bmiHeader.biHeight, i, j, TRUE );
                if( lpIcon->lpbi->bmiHeader.biBitCount == 1 )
                    SetMonoDIBPixel( pXORBits, lpIcon->lpbi->bmiHeader.biWidth, lpIcon->lpbi->bmiHeader.biHeight, i, j, FALSE );
                else
                    SetPixelV( hMemDC1, i, j, RGB(0,0,0) );
            }
            else
            {
                // No, so clear pixel in AND mask
                SetMonoDIBPixel( lpIcon->lpAND, lpIcon->lpbi->bmiHeader.biWidth, lpIcon->lpbi->bmiHeader.biHeight, i, j, FALSE );
            }
        }
    }
    // Flush the SetPixelV() calls
    GdiFlush();

    SelectObject( hMemDC1, hOldXORBitmap );

    // Copy the new XOR bits back to our storage
    memcpy( lpIcon->lpXOR, pXORBits, (lpIcon->lpbi->bmiHeader.biHeight) * BytesPerLine((LPBITMAPINFOHEADER)(lpIcon->lpbi)) );

    // Clean up
    DeleteObject( hXORBitmap );
    DeleteDC( hMemDC1 );
    ReleaseDC( NULL, hDC );

    // UnAccount for height*2 thing
    lpIcon->lpbi->bmiHeader.biHeight *= 2;
    return TRUE;
}
/* End MakeNewANDMaskBasedOnPoint() *****************************************/

/****************************************************************************
*
*     FUNCTION: IconImageFromBMPFile
*
*     PURPOSE:  Creates an icon image from a BMP file
*
*     PARAMS:   LPCTSTR     szFileName    - Filename for BMP file
*               LPICONIMAGE lpii          - pointer to icon image data
*               BOOL        bStretchToFit - TRUE to stretch, FALSE to take
*                                           the upper left corner of the DIB
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL IconImageFromBMPFile( LPCTSTR szFileName, LPICONIMAGE lpii, BOOL bStretchToFit )
{
    LPBYTE        lpDIB = NULL;
    BOOL            bRet = FALSE;

    if( (lpDIB=ReadBMPFile(szFileName)) == NULL )
        return FALSE;
    // Convert it to an icon image
    bRet = DIBToIconImage( lpii, lpDIB, bStretchToFit );
    free( lpDIB );
    return bRet;
}
/* End IconImageFromBMPFile() ********************************************/

/****************************************************************************
*
*     FUNCTION: IconImageToBMPFile
*
*     PURPOSE:  Creates BMP file from an icon image
*
*     PARAMS:   LPCTSTR     szFileName    - Filename for BMP file
*               LPICONIMAGE lpii          - pointer to icon image data
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL IconImageToBMPFile( LPCTSTR szFileName, LPICONIMAGE lpii )
{
    return WriteBMPFile( szFileName, (LPBYTE)lpii->lpbi );
}


ICONS.H

/****************************************************************************\
*
*     FILE:     Icons.H
*
*     PURPOSE:  IconPro Project Icon handling header file
*
*     COMMENTS:
*
*
*     Copyright 1995 - 1998 Microsoft Corp.
*
*
* History:
*                July '95 - Created
*
\****************************************************************************/

/****************************************************************************/
// Structs

// These first two structs represent how the icon information is stored
// when it is bound into a EXE or DLL file. Structure members are WORD
// aligned and the last member of the structure is the ID instead of
// the imageoffset.
#pragma pack( push )
#pragma pack( 2 )
typedef struct
{
BYTEbWidth;               // Width of the image
BYTEbHeight;              // Height of the image (times 2)
BYTEbColorCount;          // Number of colors in image (0 if >=8bpp)
BYTEbReserved;            // Reserved
WORDwPlanes;              // Color Planes
WORDwBitCount;            // Bits per pixel
DWORDdwBytesInRes;         // how many bytes in this resource?
WORDnID;                  // the ID
} MEMICONDIRENTRY, *LPMEMICONDIRENTRY;
typedef struct
{
WORDidReserved;   // Reserved
WORDidType;       // resource type (1 for icons)
WORDidCount;      // how many images?
MEMICONDIRENTRYidEntries[1]; // the entries for each image
} MEMICONDIR, *LPMEMICONDIR;
#pragma pack( pop )

// These next two structs represent how the icon information is stored
// in an ICO file.
typedef struct
{
BYTEbWidth;               // Width of the image
BYTEbHeight;              // Height of the image (times 2)
BYTEbColorCount;          // Number of colors in image (0 if >=8bpp)
BYTEbReserved;            // Reserved
WORDwPlanes;              // Color Planes
WORDwBitCount;            // Bits per pixel
DWORDdwBytesInRes;         // how many bytes in this resource?
DWORDdwImageOffset;        // where in the file is this image
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
WORDidReserved;   // Reserved
WORDidType;       // resource type (1 for icons)
WORDidCount;      // how many images?
ICONDIRENTRYidEntries[1]; // the entries for each image
} ICONDIR, *LPICONDIR;

// The following two structs are for the use of this program in
// manipulating icons. They are more closely tied to the operation
// of this program than the structures listed above. One of the
// main differences is that they provide a pointer to the DIB
// information of the masks.
typedef struct
{
UINTWidth, Height, Colors; // Width, Height and bpp
LPBYTElpBits;                // ptr to DIB bits
DWORDdwNumBytes;            // how many bytes?
LPBITMAPINFOlpbi;                  // ptr to header
LPBYTElpXOR;                 // ptr to XOR image bits
LPBYTElpAND;                 // ptr to AND image bits
} ICONIMAGE, *LPICONIMAGE;
typedef struct
{
BOOLbHasChanged;                     // Has image changed?
TCHARszOriginalICOFileName[MAX_PATH]; // Original name
TCHARszOriginalDLLFileName[MAX_PATH]; // Original name
UINTnNumImages;                      // How many images?
ICONIMAGEIconImages[1];                   // Image entries
} ICONRESOURCE, *LPICONRESOURCE;
/****************************************************************************/

/****************************************************************************/
// Exported function prototypes
LPICONRESOURCE ReadIconFromICOFile( LPCTSTR szFileName );
BOOL WriteIconToICOFile( LPICONRESOURCE lpIR, LPCTSTR szFileName );
HICON MakeIconFromResource( LPICONIMAGE lpIcon );
LPICONRESOURCE ReadIconFromEXEFile( LPCTSTR szFileName );
BOOL IconImageToClipBoard( LPICONIMAGE lpii );
BOOL IconImageFromClipBoard( LPICONIMAGE lpii, BOOL bStretchToFit );
BOOL CreateBlankNewFormatIcon( LPICONIMAGE lpii );
BOOL DrawXORMask( HDC hDC, RECT Rect, LPICONIMAGE lpIcon );
BOOL DrawANDMask( HDC hDC, RECT Rect, LPICONIMAGE lpIcon );
RECT GetXORImageRect( RECT Rect, LPICONIMAGE lpIcon );
BOOL MakeNewANDMaskBasedOnPoint( LPICONIMAGE lpIcon, POINT pt );
BOOL IconImageFromBMPFile( LPCTSTR szFileName, LPICONIMAGE lpii, BOOL bStretchToFit );
BOOL IconImageToBMPFile( LPCTSTR szFileName, LPICONIMAGE lpii );
/****************************************************************************/


MDICHILD.C

/****************************************************************************\
*
*     FILE:     MDIChild.C
*
*     PURPOSE:  IconPro Project MDI Child Window handling c file
*
*     COMMENTS: This file contains the MDI Child Window handling code
*
*     FUNCTIONS:
*      EXPORTS:
*               IconChildWndProc   - Window Procedure for the MDI children
*      LOCALS:
*               Draw3DRect         - Draws a rectangle using 3D colors
*               EraseBackground    - Draws the MDI child's background
*               CreateChildren     - Creates MDI child's child windows
*               CreateChildListBox - Creates and shows a list box
*               AddFormatDlgProc   - Dialog Proc for AddFormat dialog
*
*     Copyright 1995 - 1998 Microsoft Corp.
*
*
* History:
*                July '95 - Created
*
\****************************************************************************/
#include <windows.h>
#include <commctrl.h>
#include "Icons.h"
#include "IconPro.h"
#include "Resource.h"
#include "MDIChild.H"

/****************************************************************************/
// External Globals
extern HINSTANCE    hInst;
extern HWND        hWndMain, hMDIClientWnd;
/****************************************************************************/

/****************************************************************************/
// Prototypes for local functions
BOOL Draw3DRect( HDC hDC, RECT Rect, BOOL bSunken );
void EraseBackground( HWND hWnd, HDC hDC );
BOOL CreateChildren( HWND hWnd );
HWND CreateChildListBox( HWND hWndParent, UINT ID, LPRECT lpRect );
BOOL CALLBACK AddFormatDlgProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
/****************************************************************************/

/****************************************************************************
*
*     FUNCTION: IconChildWndProc
*
*     PURPOSE:  Window Procedure for MDI child window
*
*     PARAMS:   HWND hWnd     - This window handle
*               UINT Msg      - Which Message?
*               WPARAM wParam - message parameter
*               LPARAM lParam - message parameter
*
*     RETURNS:  LRESULT - depends on message
*
* History:
*                July '95 - Created
*
\****************************************************************************/
LRESULT CALLBACK IconChildWndProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
    // Which message is it?
    switch( Msg )
    {
        // User pressed left mouse button
        // Should be re-generate the AND mask?
        case WM_LBUTTONDOWN:
        {
            LPCHILDWINDOWDATAlpcwd;

            // Get the icon resource data aassociated with this window
            if( (lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA )) == NULL )
                break;

            // Do something only if the CNTRL key is pressed too
            if( MK_CONTROL & wParam )
            {
                POINTSpts = MAKEPOINTS(lParam);
                POINTpt;
                RECT    ImageRect;
                DWORDnIndex;

                pt.x = pts.x; pt.y = pts.y;
                // Do we have some icon resource data?
                if( lpcwd->lpIR != NULL )
                {
                    // Which image is selected right now?
                    if( (nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_GETCURSEL, 0, 0 )) != CB_ERR )
                    {
                        // Where is it drawn
                        ImageRect = GetXORImageRect( lpcwd->XORRect, &(lpcwd->lpIR->IconImages[nIndex]) );
                        // Is the mouse click in the image?
                        if( PtInRect( &ImageRect, pt ) )
                        {
                            HCURSORhOldCursor;

                            // This might take a while :(
                            hOldCursor = SetCursor( LoadCursor(NULL,IDC_WAIT) );
                            pt.x -= ImageRect.left;
                            pt.y -= ImageRect.top;
                            // generate the new AND mask
                            MakeNewANDMaskBasedOnPoint( &(lpcwd->lpIR->IconImages[nIndex]), pt );
                            // force a redraw
                            InvalidateRect( hWnd, NULL, TRUE );
                            //  Set changed flag
                            lpcwd->lpIR->bHasChanged = TRUE;
                            // finally, its over, put the cursor back
                            SetCursor( hOldCursor );
                        }
                    }
                }
            }
        }
        break; // End WM_LBUTTONDOWN

        // Time to say "Goodbye"
        case WM_CLOSE:
        {
            LPCHILDWINDOWDATAlpcwd;
            TCHARszWindowTitle[MAX_PATH];

            // Get the data associated with this window
            lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
            // Is there data associated with this window?
            if( lpcwd != NULL )
            {
                // Does the data include an icon resource
                if( lpcwd->lpIR != NULL )
                {
                    // Has the resource changed?
                    if( lpcwd->lpIR->bHasChanged )
                    {
                        // Get the title for the message box
                        GetWindowText( hWnd, szWindowTitle, MAX_PATH );
                        if( lstrlen( szWindowTitle ) < 1 )
                            lstrcpy( szWindowTitle, "UnKnown" );
                        // User want to save changes?
                        switch( MessageBox( hWnd, "Icon has Changed, Save Changes?", szWindowTitle, MB_ICONSTOP | MB_YESNOCANCEL ) )
                        {
                            case IDYES:
                                SendMessage( hWnd, WM_COMMAND, ID_FILE_SAVE, 0 );
                                // Fall through to IDNO and kill window
                            case IDNO:
                                DefMDIChildProc( hWnd, Msg, wParam, lParam );
                                return 0;
                            break;
                            case IDCANCEL:
                                return 1;
                            break;
                        }
                    }
                }
            }
            DefMDIChildProc( hWnd, Msg, wParam, lParam );
            return 0;
        }
        break; // End WM_CLOSE

        // We are being created
        case WM_CREATE:
        {
            LPCHILDWINDOWDATAlpcwd;

            // Need new data for this new window
            lpcwd = malloc( sizeof( CHILDWINDOWDATA ) );
            SetWindowLong( hWnd, GWL_USERDATA, (LONG)lpcwd );
            lpcwd->lpIR = NULL;
            // If a resource was passed in, use it
            if( (LPVOID)lParam != NULL )
                lpcwd->lpIR = (LPICONRESOURCE)(((MDICREATESTRUCT *)(((CREATESTRUCT *)lParam)->lpCreateParams))->lParam);
            // If no resource was passed in, do minimal initialization
            if( lpcwd->lpIR == NULL )
            {
                lpcwd->lpIR = malloc(sizeof(ICONRESOURCE));
                lstrcpy( lpcwd->lpIR->szOriginalICOFileName, "Untitled" );
                lstrcpy( lpcwd->lpIR->szOriginalDLLFileName, "" );
                lpcwd->lpIR->nNumImages = 0;
            }
            // Nothing has changed
            lpcwd->lpIR->bHasChanged = FALSE;
            // Create the list box, etc
            CreateChildren( hWnd );
            return DefMDIChildProc( hWnd, Msg, wParam, lParam );
        }
        break; // End WM_CREATE

        // Won't let window get too small to show our main area
        case WM_GETMINMAXINFO:
        {
            LPMINMAXINFO    lpmmi = (LPMINMAXINFO)lParam;

            lpmmi->ptMinTrackSize.x = WINDOW_WIDTH;
            lpmmi->ptMinTrackSize.y = WINDOW_HEIGHT;
            return 0;
        }
        break; // End WM_GETMINMAXINFO

        // Yikes! We're being destroyed!
        case WM_DESTROY:
        {
            LPCHILDWINDOWDATAlpcwd;

            // Get the data associated with this window
            lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
            // Is there some?
            if( lpcwd != NULL )
            {
                // Is there a resource?
                if( lpcwd->lpIR != NULL )
                {
                    UINT i;

                    // Free all the bits
                    for( i=0; i< lpcwd->lpIR->nNumImages; i++ )
                    {
                        if( lpcwd->lpIR->IconImages[i].lpBits != NULL )
                            free( lpcwd->lpIR->IconImages[i].lpBits );
                    }
                    free( lpcwd->lpIR );
                }
                free( lpcwd );
            }
            SetWindowLong( hWnd, GWL_USERDATA, 0 );
        }
        break; // End WM_DESTROY

        // Draw our background (white and black squares, etc)
        case WM_ERASEBKGND:
            EraseBackground( hWnd, (HDC)wParam );
            return 1;
        break; // End WM_ERASEBKGND

        // Ok, time to paint
        case WM_PAINT:
        {
            LPCHILDWINDOWDATAlpcwd;
            HDC                hDC;
            PAINTSTRUCT        ps;
            DWORD            nIndex;
            HICON            hIcon = NULL;

            // Obligatory BeginPaint()
            hDC = BeginPaint( hWnd, &ps );
            // Get the data associated with this window
            lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
            // Is there some?
            if( lpcwd != NULL )
            {
                // Is there a resource?
                if( lpcwd->lpIR != NULL )
                {
                    // Which image is selected?
                    if( (nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_GETCURSEL, 0, 0 )) != CB_ERR )
                    {
                        int        Width, Height;

                        // Get an HICON for the currently selected image
                        hIcon = MakeIconFromResource( &(lpcwd->lpIR->IconImages[nIndex]) );
                        // How big is the icon?
                        Width = lpcwd->lpIR->IconImages[nIndex].Width;
                        Height = lpcwd->lpIR->IconImages[nIndex].Height;
                        // Check to see if the icon is NULL
                        // If it is, consider it "unsupported"
                        // In the future, maybe we should look into MakeIconFromResource() and
                        // see why it is null - it may be for another reason than "unsupported"
                        if( hIcon == NULL )
                        {
                            SIZE    Size, Position;

                            // Draw some text in the black rect
                            SetTextColor( hDC, RGB(255,255,255) );
                            GetTextExtentPoint32( hDC, "Unsupported", 11, &Size );
                            Position.cx = lpcwd->BlackRect.left + ((RectWidth(lpcwd->BlackRect)-Size.cx)/2);
                            Position.cy = lpcwd->BlackRect.top + (RectHeight(lpcwd->BlackRect)/2) - Size.cy;
                            TextOut( hDC, Position.cx, Position.cy, "Unsupported", 11 );
                            GetTextExtentPoint32( hDC, "Format", 6, &Size );
                            Position.cx = lpcwd->BlackRect.left + ((RectWidth(lpcwd->BlackRect)-Size.cx)/2);
                            Position.cy = lpcwd->BlackRect.top + (RectHeight(lpcwd->BlackRect)/2) + 1;
                            TextOut( hDC, Position.cx, Position.cy, "Format", 6 );

                            // Draw some text in the white rect
                            SetTextColor( hDC, RGB(0,0,0) );
                            GetTextExtentPoint32( hDC, "Unsupported", 11, &Size );
                            Position.cx = lpcwd->WhiteRect.left + ((RectWidth(lpcwd->WhiteRect)-Size.cx)/2);
                            Position.cy = lpcwd->WhiteRect.top + (RectHeight(lpcwd->WhiteRect)/2) - Size.cy;
                            TextOut( hDC, Position.cx, Position.cy, "Unsupported", 11 );
                            GetTextExtentPoint32( hDC, "Format", 6, &Size );
                            Position.cx = lpcwd->WhiteRect.left + ((RectWidth(lpcwd->WhiteRect)-Size.cx)/2);
                            Position.cy = lpcwd->WhiteRect.top + (RectHeight(lpcwd->WhiteRect)/2) + 1;
                            TextOut( hDC, Position.cx, Position.cy, "Format", 6 );
                        }
                        else
                        {
                            // Draw it on the black background
                            DrawIconEx( hDC, lpcwd->BlackRect.left + ((RectWidth(lpcwd->BlackRect)-Width)/2),
                                            lpcwd->BlackRect.top + ((RectHeight(lpcwd->BlackRect)-Height)/2),
                                            hIcon, Width, Height, 0, NULL, DI_NORMAL );
                            // Draw it on the white background
                            DrawIconEx( hDC, lpcwd->WhiteRect.left + ((RectWidth(lpcwd->WhiteRect)-Width)/2),
                                            lpcwd->WhiteRect.top + ((RectHeight(lpcwd->WhiteRect)-Height)/2),
                                            hIcon, Width, Height, 0, NULL, DI_NORMAL );
                        }
                        // Draw just the XOR mask
                        DrawXORMask( hDC, lpcwd->XORRect, &(lpcwd->lpIR->IconImages[nIndex]) );
                        // Draw just the AND mask
                        DrawANDMask( hDC, lpcwd->ANDRect, &(lpcwd->lpIR->IconImages[nIndex]) );
                        // Kill the icon, we're one with it
                        if( hIcon != NULL )
                            DestroyIcon( hIcon );
                    }
                }
            }
            // Obligtory EndPaint()
            EndPaint( hWnd, &ps );
        }
        break; // End WM_PAINT

        // WM_COMMAND - menu options, etc
        case WM_COMMAND:
            // which one is it?
            switch( LOWORD(wParam) )
            {
                // Edit->Export BMP - write icon image as BMP file
                case ID_EDIT_EXPORTBMP:
                {
                    LPCHILDWINDOWDATAlpcwd;
                    DWORD            nIndex;
                    TCHAR            szFileName[MAX_PATH];

                    // Get the data associated this window
                    lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                    // If we have some data, including an icon resource
                    if( (lpcwd != NULL ) && (lpcwd->lpIR != NULL) && (lpcwd->hWndFormatListBox!= NULL) )
                    {
                        // Which image is currently selected?
                        if( (nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_GETCURSEL, 0, 0 )) != CB_ERR )
                        {
                            // Get the name of the file from which to import the image
                            if( GetSaveIconFileName( szFileName, IDS_BMPFILTERSTRING, "Export to BMP File" ) )
                            {
                                HCURSORhOldCursor;

                                // This might take a while :(
                                hOldCursor = SetCursor( LoadCursor(NULL,IDC_WAIT) );
                                IconImageToBMPFile( szFileName, &(lpcwd->lpIR->IconImages[nIndex]) );
                                SetCursor( hOldCursor );
                            }
                        }
                    }
                }
                break; // End ID_EDIT_EXPORTBMP

                // Edit->Import BMP and Edit->Stretch-Import BMP - convert BMP file to icon
                case ID_EDIT_IMPORTBMP:
                case ID_EDIT_STRETCHIMPORTBMP:
                {
                    LPCHILDWINDOWDATAlpcwd;
                    DWORD            nIndex;
                    TCHAR            szFileName[MAX_PATH];

                    // Get the data associated this window
                    lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                    // If we have some data, including an icon resource
                    if( (lpcwd != NULL ) && (lpcwd->lpIR != NULL) && (lpcwd->hWndFormatListBox!= NULL) )
                    {
                        // Which image is currently selected?
                        if( (nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_GETCURSEL, 0, 0 )) != CB_ERR )
                        {
                            // Get the name of the file from which to import the image
                            if( GetOpenIconFileName( szFileName, IDS_BMPFILTERSTRING, "Import from BMP File" ) )
                            {
                                HCURSORhOldCursor;

                                // This might take a while :(
                                hOldCursor = SetCursor( LoadCursor(NULL,IDC_WAIT) );
                                // Import the BMP image data
                                if( IconImageFromBMPFile( szFileName, &(lpcwd->lpIR->IconImages[nIndex]), (LOWORD(wParam)==ID_EDIT_STRETCHIMPORTBMP)?TRUE:FALSE ) )
                                {
                                    // which, of course, changes things
                                    lpcwd->lpIR->bHasChanged = TRUE;
                                    // Force a repaint
                                    InvalidateRect( hWnd, NULL, TRUE );
                                }
                                SetCursor( hOldCursor );
                            }
                        }
                    }
                }
                break; // End ID_EDIT_IMPORTBMP/ID_EDIT_STRETCHIMPORTBMP

                // User wants to add an image format
                case ID_EDIT_ADDFORMAT:
                {
                    LPICONIMAGElpii;
                    TCHARszBuffer[256];

                    // Launch the dialog to ask which size and color depth
                    if( DialogBoxParam( hInst, MAKEINTRESOURCE(IDD_ADDFORMATDLG), hWndMain, AddFormatDlgProc, (LPARAM)(&lpii) ) )
                    {
                        LPCHILDWINDOWDATAlpcwd;
                        LPICONRESOURCE    lpNewIR;
                        DWORD            nIndex, i;

                        // Get the data associated this window
                        lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                        // If we have some data, including an icon resource
                        if( (lpcwd != NULL ) && (lpcwd->lpIR != NULL) && (lpcwd->hWndFormatListBox!= NULL) )
                        {
                            // Need to see if the new format already exists in the resource
                            // We don't want dupes, so check each image for dupe
                            for(i=0;i<lpcwd->lpIR->nNumImages;i++)
                            {
                                // Is it the same as the new one?
                                if( (lpcwd->lpIR->IconImages[i].Width==lpii->Width) &&
                                    (lpcwd->lpIR->IconImages[i].Height==lpii->Height) &&
                                    (lpcwd->lpIR->IconImages[i].Colors==lpii->Colors) )
                                {
                                    // Yikes! It is - bail and select the old one
                                    MessageBox( hWnd, "That format already exists - format not added", "Error", MB_OK );
                                    SendMessage( lpcwd->hWndFormatListBox, CB_SETCURSEL, (WPARAM)i, (LPARAM)0 );
                                    break;
                                }
                            }
                            // Need bigger block of memory to hold an extra image format
                            lpNewIR = malloc( sizeof( ICONRESOURCE ) + ( ( lpcwd->lpIR->nNumImages + 1) * sizeof(ICONIMAGE) ) );
                            // Of course this changes things
                            lpNewIR->bHasChanged = TRUE;
                            // Copy old to new
                            lstrcpy( lpNewIR->szOriginalICOFileName, lpcwd->lpIR->szOriginalICOFileName );
                            lstrcpy( lpNewIR->szOriginalDLLFileName, lpcwd->lpIR->szOriginalDLLFileName );
                            lpNewIR->nNumImages = lpcwd->lpIR->nNumImages + 1;
                            for(i=0;i<lpcwd->lpIR->nNumImages;i++)
                            {
                                memcpy( &(lpNewIR->IconImages[i]), &(lpcwd->lpIR->IconImages[i]), sizeof( ICONIMAGE ) );
                            }
                            // Add in the new one
                            memcpy( &(lpNewIR->IconImages[i]), lpii, sizeof( ICONIMAGE ) );
                            // Add this new one to the list box
                            wsprintf( szBuffer, "%dx%d, %d Bit Color", lpii->Width, lpii->Height, lpii->Colors );
                            nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_ADDSTRING, 0, (LPARAM)szBuffer );
                            // Select the new one
                            SendMessage( lpcwd->hWndFormatListBox, CB_SETCURSEL, (WPARAM)i, (LPARAM)0 );
                            // clean up
                            free( lpii );
                            free( lpcwd->lpIR );
                            lpcwd->lpIR = lpNewIR;
                            // Create a nice new blank image for this format
                            CreateBlankNewFormatIcon( &(lpcwd->lpIR->IconImages[i]) );
                            // force a repaint
                            InvalidateRect( hWnd, NULL, TRUE );
                        }
                    }
                }
                break; // End WM_COMMAND -> ID_EDIT_ADDFORMAT

                // User wants to remove an image format
                case ID_EDIT_REMOVEFORMAT:
                {
                    LPCHILDWINDOWDATAlpcwd;
                    DWORD            nIndex, i;
                    LPICONRESOURCE    lpNewIR;

                    // Get the data associated this window
                    lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                    // If we have some data, including an icon resource
                    if( (lpcwd != NULL ) && (lpcwd->lpIR != NULL) && (lpcwd->hWndFormatListBox!= NULL) )
                    {
                        // Which image is currently selected?
                        if( (nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_GETCURSEL, 0, 0 )) != CB_ERR )
                        {
                            // Remove the entry from the list box
                            SendMessage( lpcwd->hWndFormatListBox, CB_DELETESTRING, nIndex, 0 );
                            // Need less memory now
                            lpNewIR = malloc( sizeof( ICONRESOURCE ) + ( ( lpcwd->lpIR->nNumImages - 1) * sizeof(ICONIMAGE) ) );
                            // Of course this changes things
                            lpNewIR->bHasChanged = TRUE;
                            // Copy old to new
                            lstrcpy( lpNewIR->szOriginalICOFileName, lpcwd->lpIR->szOriginalICOFileName );
                            lstrcpy( lpNewIR->szOriginalDLLFileName, lpcwd->lpIR->szOriginalDLLFileName );
                            lpNewIR->nNumImages = lpcwd->lpIR->nNumImages - 1;
                            // Copy the rest of the images from old to new
                            for(i=0;i<nIndex;i++)
                            {
                                memcpy( &(lpNewIR->IconImages[i]), &(lpcwd->lpIR->IconImages[i]), sizeof( ICONIMAGE ) );
                            }
                            for(;i<lpcwd->lpIR->nNumImages-1;i++)
                            {
                                memcpy( &(lpNewIR->IconImages[i]), &(lpcwd->lpIR->IconImages[i+1]), sizeof( ICONIMAGE ) );
                            }
                            // Clean up
                            free( lpcwd->lpIR );
                            lpcwd->lpIR = lpNewIR;
                            // Select a different image
                            if( --nIndex < 0 ) nIndex = 0;
                            SendMessage( lpcwd->hWndFormatListBox, CB_SETCURSEL, (WPARAM)nIndex, 0 );
                            // Force a repaint
                            InvalidateRect( hWnd, NULL, TRUE );
                        }
                    }
                }
                break; // End WM_COMMAND -> ID_EDIT_REMOVEFORMAT

                // User wants to paste CF_DIB from clipboard into current image
                case ID_EDIT_STRETCHPASTE:
                case ID_EDIT_PASTE:
                {
                    LPCHILDWINDOWDATAlpcwd;
                    DWORD            nIndex;

                    // Get the data associated this window
                    lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                    // If we have some data, including an icon resource
                    if( (lpcwd != NULL ) && (lpcwd->lpIR != NULL) && (lpcwd->hWndFormatListBox!= NULL) )
                    {
                        // Which image is currently selected?
                        if( (nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_GETCURSEL, 0, 0 )) != CB_ERR )
                        {
                            HCURSORhOldCursor;

                            // This might take a while :(
                            hOldCursor = SetCursor( LoadCursor(NULL,IDC_WAIT) );

                            // Paste over it from the clipboard
                            if( IconImageFromClipBoard( &(lpcwd->lpIR->IconImages[nIndex]), LOWORD(wParam)!=ID_EDIT_PASTE ) )
                                // which, of course, changes things
                                lpcwd->lpIR->bHasChanged = TRUE;
                            // Force a repaint
                            InvalidateRect( hWnd, NULL, TRUE );
                            SetCursor( hOldCursor );
                        }
                    }
                }
                break; // End WM_COMMAND -> ID_EDIT_STRETCHPASTE/ID_EDIT_PASTE

                // Put current image on the clipboard in CF_DIB format
                case ID_EDIT_COPY:
                {
                    LPCHILDWINDOWDATAlpcwd;
                    DWORD            nIndex;

                    // Get the data associated this window
                    lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                    // If we have some data, including an icon resource
                    if( (lpcwd != NULL ) && (lpcwd->lpIR != NULL) && (lpcwd->hWndFormatListBox!= NULL) )
                    {
                        // Which image is currently selected?
                        if( (nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_GETCURSEL, 0, 0 )) != CB_ERR )
                        {
                            // Send this image to the clipboard
                            IconImageToClipBoard( &(lpcwd->lpIR->IconImages[nIndex]) );
                        }
                    }
                }
                break; // End WM_COMMAND -> ID_EDIT_COPY

                // The filename has changed, update the window title
                case ID_UPDATETITLE:
                {
                    TCHARszFileTitle[MAX_PATH];
                    LPCHILDWINDOWDATAlpcwd;

                    // Get the data associated this window
                    lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                    // If we have some data
                    if( lpcwd != NULL )
                    {
                        // including an icon resource
                        if( lpcwd->lpIR != NULL )
                        {
                            // The calculate and set the new title
                            if( GetFileTitle( lpcwd->lpIR->szOriginalICOFileName, szFileTitle, MAX_PATH ) == 0 )
                            {
                                SetWindowText( hWnd, szFileTitle );
                            }
                        }
                    }
                }
                break; // End WM_COMMAND -> ID_UPDATETITLE

                // How many image formats in the icon resource? (return that number)
                case ID_GETNUMFORMATS:
                {
                    LPCHILDWINDOWDATAlpcwd;
                    UINT nNum = 0;

                    // Get the data associated this window
                    lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                    // If we have some data
                    if( lpcwd != NULL )
                    {
                        // If we have a listbox, get its count, else 0
                        if( lpcwd->hWndFormatListBox != NULL )
                            nNum = (UINT)SendMessage( lpcwd->hWndFormatListBox, CB_GETCOUNT, 0, 0 );
                        else
                            nNum = 0;
                        // If an error occurred, default to 0
                        if( nNum == (UINT)CB_ERR )
                            nNum = 0;
                    }
                    // Send it back
                    return nNum;
                }
                break; // End WM_COMMAND -> ID_GETNUMFORMATS

                // Has this icon resource changed? return TRUE=yes, FALSE=no
                case ID_HASFILECHANGED:
                {
                    LPCHILDWINDOWDATAlpcwd;
                    UINT nRet = 0;

                    // Get the data associated this window
                    lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA );
                    // If we have some data
                    if( lpcwd != NULL )
                        // And it includes an icon resource
                        if( lpcwd->lpIR != NULL )
                            // then check whether it has changed
                            return lpcwd->lpIR->bHasChanged == TRUE;
                    // Otherwise, return FALSE
                    return nRet;
                }
                break; // End WM_COMMAND -> ID_HASFILECHANGED

                // Handle selection changes, etc from listbox
                case ID_FORMAT_BOX:
                    switch( HIWORD(wParam) )
                    {
                        // If a selection is made, or changes, repaint
                        case CBN_SELCHANGE:
                        case CBN_SELENDOK:
                            InvalidateRect( hWnd, NULL, TRUE );
                        break;
                    }
                break; // End WM_COMMAND -> ID_FORMAT_BOX

                // User wants to save the ICO file
                case ID_FILE_SAVEAS:
                case ID_FILE_SAVE:
                {
                    TCHARszFileName[MAX_PATH];
                    LPCHILDWINDOWDATAlpcwd;

                    // Get the data associated this window
                    if( ( lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA ) ) != NULL )
                    {
                        // See if it includes an icon resource
                        if( lpcwd->lpIR != NULL )
                        {
                            HCURSORhOldCursor;

                            // This might take a while :(
                            hOldCursor = SetCursor( LoadCursor(NULL,IDC_WAIT) );

                            // If we have an original filename, and user did *NOT* 'Save As'
                            if( ( lstrlen(lpcwd->lpIR->szOriginalICOFileName) > 0 ) && (LOWORD(wParam)!=ID_FILE_SAVEAS) )
                            {
                                // The just write it out
                                WriteIconToICOFile( lpcwd->lpIR, lpcwd->lpIR->szOriginalICOFileName );
                                // which, of course, brings it up to date
                                lpcwd->lpIR->bHasChanged = FALSE;
                            }
                            else
                            {
                                // Either we have no name, or user hit 'Save As'
                                if( GetSaveIconFileName( szFileName, IDS_FILTERSTRING, LOWORD(wParam)==ID_FILE_SAVE?"Save Icon File":"Save Icon File As" ) )
                                {
                                    // So, write it out
                                    WriteIconToICOFile( lpcwd->lpIR, szFileName );
                                    // Update the name associated with the resource
                                    lstrcpy( lpcwd->lpIR->szOriginalICOFileName, szFileName );
                                    // Inform window to update title
                                    SendMessage( hWnd, WM_COMMAND, ID_UPDATETITLE, 0 );
                                    // and, of course, it is now up to date
                                    lpcwd->lpIR->bHasChanged = FALSE;
                                }
                            }
                            SetCursor( hOldCursor );
                        }
                        else
                            MessageBox( hWnd, "Error Getting Icon Info - File Not Saved", "Error", MB_OK );
                    }
                }
                break; // End WM_COMMAND -> ID_FILE_SAVE/ID_FILE_SAVEAS
            }
        break; // End WM_COMMAND
    }
    return DefMDIChildProc( hWnd, Msg, wParam, lParam );
}
/* End IconChildWndProc() **************************************************/

/****************************************************************************
*
*     FUNCTION: Draw3DRect
*
*     PURPOSE:  draws a rectangle in 3d colors
*
*     PARAMS:   HDC hDC      - The DC on which to draw
*               RECT Rect    - The rectangle itself
*               BOOL bSunken - TRUE  = rect should look sunken
*                              FALSE = rect should look raised
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL Draw3DRect( HDC hDC, RECT Rect, BOOL bSunken )
{
    HBRUSHhBrush;
    HPEN    hPen, hOldPen;

    // Get the color for the main foreground
    hBrush = CreateSolidBrush( GetSysColor(COLOR_3DFACE) );
    // paint it
    FillRect( hDC, &Rect, hBrush );
    DeleteObject( hBrush );
    // Get the pen for the top and left sides
    if( bSunken )
        hPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW) );
    else
        hPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT) );
    hOldPen = SelectObject( hDC, hPen);
    // Draw the top and left sides
    MoveToEx( hDC, Rect.right, Rect.top, NULL );
    LineTo( hDC, Rect.left, Rect.top );
    LineTo( hDC, Rect.left, Rect.bottom );
    SelectObject( hDC, hOldPen);
    DeleteObject( hPen );
    // Get the pen for the bottom and right sides
    if( bSunken )
        hPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT) );
    else
        hPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW) );
    hOldPen = SelectObject( hDC, hPen);
    // Draw the bottom and right sides
    LineTo( hDC, Rect.right, Rect.bottom );
    LineTo( hDC, Rect.right, Rect.top );
    SelectObject( hDC, hOldPen);
    DeleteObject( hPen );
    return TRUE;
}
/* End Draw3DRect() *******************************************************/

/****************************************************************************
*
*     FUNCTION: EraseBackground
*
*     PURPOSE:  Draws the background for the MDI child
*
*     PARAMS:   HWND hWnd - The MDI window of interest
*               HDC  hDC  - The DC on which to draw
*
*     RETURNS:  void
*
* History:
*                July '95 - Created
*
\****************************************************************************/
void EraseBackground( HWND hWnd, HDC hDC )
{
    RECT                Rect;
    LPCHILDWINDOWDATAlpcwd;
    HBRUSH            hBrush;
    SIZE                TextSize;

    // Just how big is this window?
    GetClientRect( hWnd, &Rect );
    // Paint the background
    Draw3DRect( hDC, Rect, FALSE );

    // If there is no icon resource yet, bail out
    if( ( lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA ) ) == NULL )
        return;
    // Draw 3d rectangles around areas of interest
    Draw3DRect( hDC, lpcwd->WhiteRect, TRUE );
    Draw3DRect( hDC, lpcwd->WhiteTextRect, TRUE );
    Draw3DRect( hDC, lpcwd->BlackRect, TRUE );
    Draw3DRect( hDC, lpcwd->BlackTextRect, TRUE );
    Draw3DRect( hDC, lpcwd->XORRect, TRUE );
    Draw3DRect( hDC, lpcwd->XORTextRect, TRUE );
    Draw3DRect( hDC, lpcwd->ANDRect, TRUE );
    Draw3DRect( hDC, lpcwd->ANDTextRect, TRUE );
    // Fill in the white area
    hBrush = GetStockObject( WHITE_BRUSH );
    SelectObject( hDC, hBrush );
    Rectangle( hDC, lpcwd->WhiteRect.left, lpcwd->WhiteRect.top, lpcwd->WhiteRect.right, lpcwd->WhiteRect.bottom );
    // Fill in the black area
    hBrush = GetStockObject( BLACK_BRUSH );
    SelectObject( hDC, hBrush );
    Rectangle( hDC, lpcwd->BlackRect.left, lpcwd->BlackRect.top, lpcwd->BlackRect.right, lpcwd->BlackRect.bottom );

    // Set texts for the various sections
    SetBkMode( hDC, TRANSPARENT );
    GetTextExtentPoint32( hDC, "Icon On Black", 13, &TextSize );
    TextOut( hDC, lpcwd->BlackTextRect.left + ((RectWidth(lpcwd->BlackTextRect)-TextSize.cx)/2),
                    lpcwd->BlackTextRect.top + ((RectHeight(lpcwd->BlackTextRect)-TextSize.cy)/2), "Icon On Black", 13 );
    GetTextExtentPoint32( hDC, "Icon On White", 13, &TextSize );
    TextOut( hDC, lpcwd->WhiteTextRect.left + ((RectWidth(lpcwd->WhiteTextRect)-TextSize.cx)/2),
                    lpcwd->WhiteTextRect.top + ((RectHeight(lpcwd->WhiteTextRect)-TextSize.cy)/2), "Icon On White", 13 );
    GetTextExtentPoint32( hDC, "XOR Mask", 8, &TextSize );
    TextOut( hDC, lpcwd->XORTextRect.left + ((RectWidth(lpcwd->XORTextRect)-TextSize.cx)/2),
                    lpcwd->XORTextRect.top + ((RectHeight(lpcwd->XORTextRect)-TextSize.cy)/2), "XOR Mask", 8 );
    GetTextExtentPoint32( hDC, "AND Mask", 8, &TextSize );
    TextOut( hDC, lpcwd->ANDTextRect.left + ((RectWidth(lpcwd->ANDTextRect)-TextSize.cx)/2),
                    lpcwd->ANDTextRect.top + ((RectHeight(lpcwd->ANDTextRect)-TextSize.cy)/2), "AND Mask", 8 );
}
/* End EraseBackground() ***************************************************/

/****************************************************************************
*
*     FUNCTION: CreateChildren
*
*     PURPOSE:  Create the listbox, fills it with entries
*
*     PARAMS:   HWND hWnd - The MDI window of interest
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL CreateChildren( HWND hWnd )
{
    RECT                ClientRect, TempRect;
    LPCHILDWINDOWDATAlpcwd;
    HDC                hDC;
    SIZE                size;

    // Get the data associated with this window
    if( (lpcwd = (LPCHILDWINDOWDATA)GetWindowLong( hWnd, GWL_USERDATA )) == NULL )
        return FALSE;
    // Just how big is this window?
    GetClientRect( hWnd, &ClientRect );
    // Calculate listbox size and position
    SetRect( &(lpcwd->BoxRect), 10, ClientRect.bottom-30, (MAX_ICON_WIDTH*2)+14, ClientRect.bottom+50 );
    // Create the listbox
    if((lpcwd->hWndFormatListBox=CreateChildListBox( hWnd, ID_FORMAT_BOX, &(lpcwd->BoxRect) )) == NULL )
        return FALSE;
    // If we have an icon resource
    if( lpcwd->lpIR != NULL )
    {
        UINT    i, nIndex;
        TCHARszBuffer[256];

        // For each image in the resoure
        for(i=0;i<lpcwd->lpIR->nNumImages;i++)
        {
            // Add the type of the image to the listbox
            wsprintf( szBuffer, "%dx%d, %d Bit Color", lpcwd->lpIR->IconImages[i].Width,
                        lpcwd->lpIR->IconImages[i].Height, lpcwd->lpIR->IconImages[i].Colors );
            nIndex = SendMessage( lpcwd->hWndFormatListBox, CB_ADDSTRING, 0, (LPARAM)szBuffer );
        }
        // Select the first entry
        SendMessage( lpcwd->hWndFormatListBox, CB_SETCURSEL, (WPARAM)0, (LPARAM)0 );
    }
    // Adjust the box size based on the listbox's real size
    GetClientRect( lpcwd->hWndFormatListBox, &TempRect );
    lpcwd->BoxRect.bottom = lpcwd->BoxRect.top + TempRect.bottom;

    // How big is text these days?
    hDC = GetDC( hWnd );
    GetTextExtentPoint32( hDC, "Icon on Black", 13, &size );
    ReleaseDC( hWnd, hDC );

    // Set the rectangles for the various squares to be drawn later
#define DIVIDER 5
    SetRect( &(lpcwd->DrawingRect), 10, 10, (MAX_ICON_WIDTH*2)+14, 20 + (MAX_ICON_HEIGHT*2) + (TempRect.bottom*2) );
    SetRect( &(lpcwd->BlackRect), lpcwd->DrawingRect.left, lpcwd->DrawingRect.top, lpcwd->DrawingRect.left + MAX_ICON_WIDTH + 1, lpcwd->DrawingRect.top + MAX_ICON_HEIGHT + 1 );
    SetRect( &(lpcwd->BlackTextRect), lpcwd->BlackRect.left, lpcwd->BlackRect.bottom+1, lpcwd->BlackRect.right, lpcwd->BlackRect.bottom + TempRect.bottom + 1 );
    SetRect( &(lpcwd->WhiteRect), lpcwd->BlackRect.right+1, lpcwd->BlackRect.top, lpcwd->DrawingRect.right, lpcwd->BlackRect.bottom );
    SetRect( &(lpcwd->WhiteTextRect), lpcwd->WhiteRect.left, lpcwd->WhiteRect.bottom+1, lpcwd->WhiteRect.right, lpcwd->WhiteRect.bottom + TempRect.bottom + 1 );
    SetRect( &(lpcwd->XORRect),lpcwd->BlackTextRect.left, lpcwd->BlackTextRect.bottom + 1 + DIVIDER, lpcwd->BlackRect.right, lpcwd->BlackTextRect.bottom + 2 + DIVIDER + MAX_ICON_HEIGHT );
    SetRect( &(lpcwd->XORTextRect),lpcwd->XORRect.left, lpcwd->XORRect.bottom + 1, lpcwd->XORRect.right, lpcwd->DrawingRect.bottom );
    SetRect( &(lpcwd->ANDRect),lpcwd->WhiteTextRect.left, lpcwd->WhiteTextRect.bottom + 1 + DIVIDER, lpcwd->WhiteRect.right, lpcwd->WhiteTextRect.bottom + 2 + DIVIDER + MAX_ICON_HEIGHT );
    SetRect( &(lpcwd->ANDTextRect),lpcwd->ANDRect.left, lpcwd->ANDRect.bottom + 1, lpcwd->ANDRect.right, lpcwd->DrawingRect.bottom );
#undef DIVIDER

    return TRUE;
}
/* End CreateChildren() ****************************************************/

/****************************************************************************
*
*     FUNCTION: CreateChildListBox
*
*     PURPOSE:  Creates a listbox and shows it
*
*     PARAMS:   HWND   hWndParent - The MDI window to be a parent
*               UINT   ID         - the ID of the new listbox
*               LPRECT lpRect     - the RECT for the listbox
*
*     RETURNS:  HWND - handle to listbox window, NULL for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
HWND CreateChildListBox( HWND hWndParent, UINT ID, LPRECT lpRect )
{
    HWND hWnd;

    hWnd = CreateWindow( "COMBOBOX", "", CBS_DROPDOWNLIST | CBS_DISABLENOSCROLL | WS_CHILD | WS_VSCROLL,
                        lpRect->left, lpRect->top,
                        lpRect->right - lpRect->left + 1, lpRect->bottom - lpRect->top + 1,
                        hWndParent, (HMENU)ID, hInst, 0 );
    if( hWnd != NULL )
        ShowWindow( hWnd, SW_SHOW );
    return hWnd;
}
/* End CreateChildListBox() ************************************************/

/****************************************************************************
*
*     FUNCTION: AddFormatDlgProc
*
*     PURPOSE:  Dialog Procedure for "Add Format" dialog
*
*     PARAMS:   HWND hWnd     - This dialog's window handle
*               UINT Msg      - Which Message?
*               WPARAM wParam - message parameter
*               LPARAM lParam - message parameter
*
*     RETURNS:  BOOL - TRUE for OK, FALSE for Cancel
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL CALLBACK AddFormatDlgProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
    // Support all DIB color formats known at this time
    #define MAX_COLOR_FORMAT    5
    TCHARColorFormats[MAX_COLOR_FORMAT+1][20] = { "Monochrome (1bpp)", "16 Color (4bpp)", "256 Color (8bpp)",
                                    "16 Bit Color", "24 Bit Color", "32 Bit Color" };
    UINT    Bits[MAX_COLOR_FORMAT+1] = { 1, 4, 8, 16, 24, 32 };
    staticbSquareOnly = TRUE;
    staticLPARAM lInitParam;

    switch( Msg )
    {
        // Dialog is being initialized
        case WM_INITDIALOG:
        {
            TCHARszBuffer[100];

            // We are passed a pointer to a LPICONIMAGE in lParam, save it
            lInitParam = lParam;
            // Set the range and position of the sliders
            SendDlgItemMessage( hWnd, ID_WIDTHSLIDER, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(MIN_ICON_WIDTH,MAX_ICON_WIDTH) );
            SendDlgItemMessage( hWnd, ID_WIDTHSLIDER, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)MIN_ICON_WIDTH );
            wsprintf( szBuffer, "%d Width", MIN_ICON_WIDTH );
            SetDlgItemText( hWnd, ID_WIDTHTEXT, szBuffer );
            SendDlgItemMessage( hWnd, ID_HEIGHTSLIDER, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(MIN_ICON_HEIGHT,MAX_ICON_HEIGHT) );
            SendDlgItemMessage( hWnd, ID_HEIGHTSLIDER, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)MIN_ICON_HEIGHT );
            wsprintf( szBuffer, "%d Height", MIN_ICON_HEIGHT );
            SetDlgItemText( hWnd, ID_HEIGHTTEXT, szBuffer );
            SendDlgItemMessage( hWnd, ID_COLORSLIDER, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(0,MAX_COLOR_FORMAT) );
            SendDlgItemMessage( hWnd, ID_COLORSLIDER, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)2);
            SetDlgItemText( hWnd, ID_COLORTEXT, ColorFormats[2] );
            CheckDlgButton( hWnd, IDC_SQUAREONLY, bSquareOnly );
        }
        break; // End WM_INITDIALOG

        // Scroll message from the sliders
        case WM_HSCROLL:
        {
            intnPos;
            TCHARszBuffer[100];

            // Get the current position
            if( ( LOWORD(wParam) == TB_THUMBPOSITION ) || ( LOWORD(wParam) == TB_THUMBTRACK ) )
                nPos = HIWORD( wParam );
            else
                nPos = SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
            // Was it the width slider?
            if( (HWND)lParam == GetDlgItem( hWnd, ID_WIDTHSLIDER) )
            {
                // Update the text
                wsprintf( szBuffer, "%d Width", nPos );
                SetDlgItemText( hWnd, ID_WIDTHTEXT, szBuffer );
                // If dealing with width=height, adjust height too
                if( bSquareOnly )
                {
                    SendDlgItemMessage( hWnd, ID_HEIGHTSLIDER, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)nPos );
                    wsprintf( szBuffer, "%d Height", nPos );
                    SetDlgItemText( hWnd, ID_HEIGHTTEXT, szBuffer );
                }
            }
            else
            {
                // Was it the height slider?
                if( (HWND)lParam == GetDlgItem( hWnd, ID_HEIGHTSLIDER) )
                {
                    // Update the text
                    wsprintf( szBuffer, "%d Height", nPos );
                    SetDlgItemText( hWnd, ID_HEIGHTTEXT, szBuffer );
                    // If dealing with width=height, adjust width too
                    if( bSquareOnly )
                    {
                        SendDlgItemMessage( hWnd, ID_WIDTHSLIDER, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)nPos );
                        wsprintf( szBuffer, "%d Width", nPos );
                        SetDlgItemText( hWnd, ID_WIDTHTEXT, szBuffer );
                    }
                }
                else
                {
                    // Was it the color slider?
                    if( (HWND)lParam == GetDlgItem( hWnd, ID_COLORSLIDER) )
                    {
                        // Update the text
                        SetDlgItemText( hWnd, ID_COLORTEXT, ColorFormats[nPos] );
                    }
                }
            }
        }
        break; // End WM_HSCROLL

        // Time to say 'goodbye'
        case WM_CLOSE:
            PostMessage( hWnd, WM_COMMAND, IDCANCEL, 0l );
        break; // End WM_CLOSE

        // Messages from user items - checkboxes etc
        case WM_COMMAND:
            switch( LOWORD(wParam) )
            {
                // Checkbox for width=height restriction
                case IDC_SQUAREONLY:
                    // Is it checked now?
                    bSquareOnly = IsDlgButtonChecked( hWnd, IDC_SQUAREONLY );
                    // If it is, set height equal to width
                    if( bSquareOnly )
                    {
                        int nPos;
                        TCHARszBuffer[100];

                        nPos = SendDlgItemMessage( hWnd, ID_WIDTHSLIDER, TBM_GETPOS, 0, 0 );
                        SendDlgItemMessage( hWnd, ID_HEIGHTSLIDER, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)nPos );
                        wsprintf( szBuffer, "%d Height", nPos );
                        SetDlgItemText( hWnd, ID_HEIGHTTEXT, szBuffer );
                    }
                break; // End IDC_SQUAREONLY

                // Add Format button has been pressed
                case IDOK:
                {
                    LPICONIMAGElpii;

                    // Were we passed a valid LPICONIMAGE pointer?
                    if( ((LPICONIMAGE)lInitParam) != NULL )
                    {
                        // allocate the new ICONIMAGE
                        if( (lpii = malloc( sizeof( ICONIMAGE ) )) == FALSE )
                            EndDialog( hWnd, FALSE );
                        else
                        {
                            // initialize it
                            ZeroMemory( lpii, sizeof( ICONIMAGE ) );
                            lpii->Width = SendDlgItemMessage( hWnd, ID_WIDTHSLIDER, TBM_GETPOS, 0, 0 );
                            lpii->Height = SendDlgItemMessage( hWnd, ID_HEIGHTSLIDER, TBM_GETPOS, 0, 0 );
                            lpii->Colors = Bits[SendDlgItemMessage( hWnd, ID_COLORSLIDER, TBM_GETPOS, 0, 0 )];
                            // update the pointer that we were passed
                            *(LPICONIMAGE *)lInitParam = lpii;
                            // bail
                            EndDialog( hWnd, TRUE );
                        }
                    }
                    else
                    {
                        // bail
                        EndDialog( hWnd, FALSE );
                    }
                }
                break; // End IDOK

                // Time to cancel
                case IDCANCEL:
                    EndDialog( hWnd, FALSE );
                break; // End IDCANCEL

            }
        break;
        default:
            return FALSE;
        break;
    }
    return TRUE;
    #undef MAX_COLOR_FORMAT
}
/* End AddFormatDlgProc() ************************************************/


MDICHILD.H

/****************************************************************************\
*
*     FILE:     MDIChild.H
*
*     PURPOSE:  IconPro Project MDI Child Window handling header file
*
*     COMMENTS: One CHILDWINDOWDATA struct defined below is attached
*               to each MDI child window. It stores information about
*               the listbox, the locations of drawing objects, and the
*               icon resource associated with each window.
*
*     Copyright 1995 - 1998 Microsoft Corp.
*
*
* History:
*                July '95 - Created
*
\****************************************************************************/

/****************************************************************************/
// local #defines

// The ID for the listbox child window
#define ID_FORMAT_BOX102
/****************************************************************************/

/****************************************************************************/
// Local structs

// This struct is used to hold the data associated with an MDI window
typedef struct
{
HWNDhWndFormatListBox;        // Window Handle for listbox
LPICONRESOURCElpIR;                     // pointer to icon resource
RECTBoxRect;                  // ListBox Window Rect
RECTDrawingRect;              // Overall rect to draw in
RECTBlackRect, BlackTextRect; // for black bkgrnd & text
RECTWhiteRect, WhiteTextRect; // for white bkgrnd & text
RECTXORRect, XORTextRect;     // for XOR mask & text
RECTANDRect, ANDTextRect;     // for AND mask & text
RECTXORImageRect;             // in which to draw OR mask
} CHILDWINDOWDATA, *LPCHILDWINDOWDATA;
/****************************************************************************/

/****************************************************************************/
// Exported function prototypes
LRESULT CALLBACK IconChildWndProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
/****************************************************************************/


DIB.C

/****************************************************************************\
*
*     FILE:     DIB.C
*
*     PURPOSE:  DIB functions for IconPro Project
*
*     COMMENTS: Icons are stored in a format almost identical to DIBs. For
*               this reason, it is easiest to deal with the individual
*               icon images as DIBs (or DIBSections). This has the added
*               advantage of retaining color depth even on low-end displays.
*
*     FUNCTIONS:
*      EXPORTS:
*               FindDIBBits()      - Locate the image bits in a DIB
*               DIBNumColors()     - Calculate number of color table entries
*               PaletteSize()      - Calculate number of color table bytes
*               BytesPerLine()     - Calculate number of bytes per scan line
*               ConvertDIBFormat() - Converts DIBs between formats
*               SetMonoDIBPixel()  - Sets/Clears a pixel in a 1bpp DIB
*               ReadBMPFile()      - Reads a BMP file into CF_DIB memory
*               WriteBMPFile()     - Write a BMP file from CF_DIB memory
*      LOCALS:
*               CopyColorTable()   - Copies color table from DIB to DIB
*
*     Copyright 1995 - 1998 Microsoft Corp.
*
*
* History:
*                July '95 - Created
*
\****************************************************************************/
#include <Windows.h>
#include "Dib.H"

/****************************************************************************/
/* Local Function Prototypes */
BOOL CopyColorTable( LPBITMAPINFO lpTarget, LPBITMAPINFO lpSource );
/****************************************************************************/

/****************************************************************************
*
*     FUNCTION: FindDIBits
*
*     PURPOSE:  Locate the image bits in a CF_DIB format DIB.
*
*     PARAMS:   LPSTR lpbi - pointer to the CF_DIB memory block
*
*     RETURNS:  LPSTR - pointer to the image bits
*
* History:
*                July '95 - Copied <g>
*
\****************************************************************************/
LPSTR FindDIBBits( LPSTR lpbi )
{
   return ( lpbi + *(LPDWORD)lpbi + PaletteSize( lpbi ) );
}
/* End FindDIBits() *********************************************************/

/****************************************************************************
*
*     FUNCTION: DIBNumColors
*
*     PURPOSE:  Calculates the number of entries in the color table.
*
*     PARAMS:   LPSTR lpbi - pointer to the CF_DIB memory block
*
*     RETURNS:  WORD - Number of entries in the color table.
*
* History:
*                July '95 - Copied <g>
*
\****************************************************************************/
WORD DIBNumColors( LPSTR lpbi )
{
    WORD wBitCount;
    DWORD dwClrUsed;

    dwClrUsed = ((LPBITMAPINFOHEADER) lpbi)->biClrUsed;

    if (dwClrUsed)
        return (WORD) dwClrUsed;

    wBitCount = ((LPBITMAPINFOHEADER) lpbi)->biBitCount;

    switch (wBitCount)
    {
        case 1: return 2;
        case 4: return 16;
        case 8:return 256;
        default:return 0;
    }
    return 0;
}
/* End DIBNumColors() ******************************************************/

/****************************************************************************
*
*     FUNCTION: PaletteSize
*
*     PURPOSE:  Calculates the number of bytes in the color table.
*
*     PARAMS:   LPSTR lpbi - pointer to the CF_DIB memory block
*
*     RETURNS:  WORD - number of bytes in the color table
*
*
* History:
*                July '95 - Copied <g>
*
\****************************************************************************/
WORD PaletteSize( LPSTR lpbi )
{
    return ( DIBNumColors( lpbi ) * sizeof( RGBQUAD ) );
}
/* End PaletteSize() ********************************************************/

/****************************************************************************
*
*     FUNCTION: BytesPerLine
*
*     PURPOSE:  Calculates the number of bytes in one scan line.
*
*     PARAMS:   LPBITMAPINFOHEADER lpBMIH - pointer to the BITMAPINFOHEADER
*                                           that begins the CF_DIB block
*
*     RETURNS:  DWORD - number of bytes in one scan line (DWORD aligned)
*
* History:
*                July '95 - Created
*
\****************************************************************************/
DWORD BytesPerLine( LPBITMAPINFOHEADER lpBMIH )
{
    return WIDTHBYTES(lpBMIH->biWidth * lpBMIH->biPlanes * lpBMIH->biBitCount);
}
/* End BytesPerLine() ********************************************************/

/****************************************************************************
*
*     FUNCTION: ConvertDIBFormat
*
*     PURPOSE:  Creates a new DIB of the requested format, copies the source
*               image to the new DIB.
*
*     PARAMS:   LPBITMAPINFO lpSrcDIB - the source CF_DIB
*               UINT         nWidth   - width for new DIB
*               UINT         nHeight  - height for new DIB
*               UINT         nbpp     - bpp for new DIB
*               BOOL         bStretch - TRUE to stretch source to dest
*                                       FALSE to take upper left of image
*
*     RETURNS:  LPBYTE - pointer to new CF_DIB memory block with new image
*               NULL on failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
LPBYTE ConvertDIBFormat( LPBITMAPINFO lpSrcDIB, UINT nWidth, UINT nHeight, UINT nbpp, BOOL bStretch )
{
    LPBITMAPINFO    lpbmi = NULL;
    LPBYTE        lpSourceBits, lpTargetBits, lpResult;
    HDC            hDC = NULL, hSourceDC, hTargetDC;
    HBITMAP        hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap;
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize;

    // Allocate and fill out a BITMAPINFO struct for the new DIB
    // Allow enough room for a 256-entry color table, just in case
    dwTargetHeaderSize = sizeof( BITMAPINFO ) + ( 256 * sizeof( RGBQUAD ) );
    lpbmi = malloc( dwTargetHeaderSize );
    lpbmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
    lpbmi->bmiHeader.biWidth = nWidth;
    lpbmi->bmiHeader.biHeight = nHeight;
    lpbmi->bmiHeader.biPlanes = 1;
    lpbmi->bmiHeader.biBitCount = nbpp;
    lpbmi->bmiHeader.biCompression = BI_RGB;
    lpbmi->bmiHeader.biSizeImage = 0;
    lpbmi->bmiHeader.biXPelsPerMeter = 0;
    lpbmi->bmiHeader.biYPelsPerMeter = 0;
    lpbmi->bmiHeader.biClrUsed = 0;
    lpbmi->bmiHeader.biClrImportant = 0;
    // Fill in the color table
    if( ! CopyColorTable( lpbmi, (LPBITMAPINFO)lpSrcDIB ) )
    {
        free( lpbmi );
        return NULL;
    }

    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em
    hDC = GetDC( NULL );
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, &lpTargetBits, NULL, 0 );
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, &lpSourceBits, NULL, 0 );
    hSourceDC = CreateCompatibleDC( hDC );
    hTargetDC = CreateCompatibleDC( hDC );

    // Flip the bits on the source DIBSection to match the source DIB
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine(&(lpSrcDIB->bmiHeader));
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine(&(lpbmi->bmiHeader));
    memcpy( lpSourceBits, FindDIBBits((LPSTR)lpSrcDIB), dwSourceBitsSize );

    // Select DIBSections into DCs
    hOldSourceBitmap = SelectObject( hSourceDC, hSourceBitmap );
    hOldTargetBitmap = SelectObject( hTargetDC, hTargetBitmap );

    // Set the color tables for the DIBSections
    if( lpSrcDIB->bmiHeader.biBitCount <= 8 )
        SetDIBColorTable( hSourceDC, 0, 1 << lpSrcDIB->bmiHeader.biBitCount, lpSrcDIB->bmiColors );
    if( lpbmi->bmiHeader.biBitCount <= 8 )
        SetDIBColorTable( hTargetDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors );

    // If we are asking for a straight copy, do it
    if( (lpSrcDIB->bmiHeader.biWidth==lpbmi->bmiHeader.biWidth) && (lpSrcDIB->bmiHeader.biHeight==lpbmi->bmiHeader.biHeight) )
    {
        BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY );
    }
    else
    {
        // else, should we stretch it?
        if( bStretch )
        {
            SetStretchBltMode( hTargetDC, COLORONCOLOR );
            StretchBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, lpSrcDIB->bmiHeader.biWidth, lpSrcDIB->bmiHeader.biHeight, SRCCOPY );
        }
        else
        {
            // or just take the upper left corner of the source
            BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY );
        }
    }

    // Clean up and delete the DCs
    SelectObject( hSourceDC, hOldSourceBitmap );
    SelectObject( hSourceDC, hOldTargetBitmap );
    DeleteDC( hSourceDC );
    DeleteDC( hTargetDC );
    ReleaseDC( NULL, hDC );

    // Flush the GDI batch, so we can play with the bits
    GdiFlush();

    // Allocate enough memory for the new CF_DIB, and copy bits
    lpResult = malloc( dwTargetHeaderSize + dwTargetBitsSize );
    memcpy( lpResult, lpbmi, dwTargetHeaderSize );
    memcpy( FindDIBBits( lpResult ), lpTargetBits, dwTargetBitsSize );

    // final cleanup
    DeleteObject( hTargetBitmap );
    DeleteObject( hSourceBitmap );
    free( lpbmi );

    return lpResult;
}
/* End ConvertDIBFormat() ***************************************************/

/****************************************************************************
*
*     FUNCTION: CopyColorTable
*
*     PURPOSE:  Copies the color table from one CF_DIB to another.
*
*     PARAMS:   LPBITMAPINFO lpTarget - pointer to target DIB
*               LPBITMAPINFO lpSource - pointer to source DIB
*
*     RETURNS:  BOOL - TRUE for success, FALSE for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL CopyColorTable( LPBITMAPINFO lpTarget, LPBITMAPINFO lpSource )
{
    // What we do depends on the target's color depth
    switch( lpTarget->bmiHeader.biBitCount )
    {
        // 8bpp - need 256 entry color table
        case 8:
            if( lpSource->bmiHeader.biBitCount == 8 )
            { // Source is 8bpp too, copy color table
                memcpy( lpTarget->bmiColors, lpSource->bmiColors, 256*sizeof(RGBQUAD) );
                return TRUE;
            }
            else
            { // Source is != 8bpp, use halftone palette
                HPALETTE        hPal;
                HDC            hDC = GetDC( NULL );
                PALETTEENTRY    pe[256];
                UINT            i;

                hPal = CreateHalftonePalette( hDC );
                ReleaseDC( NULL, hDC );
                GetPaletteEntries( hPal, 0, 256, pe );
                DeleteObject( hPal );
                for(i=0;i<256;i++)
                {
                    lpTarget->bmiColors[i].rgbRed = pe[i].peRed;
                    lpTarget->bmiColors[i].rgbGreen = pe[i].peGreen;
                    lpTarget->bmiColors[i].rgbBlue = pe[i].peBlue;
                    lpTarget->bmiColors[i].rgbReserved = pe[i].peFlags;
                }
                return TRUE;
            }
        break; // end 8bpp

        // 4bpp - need 16 entry color table
        case 4:
            if( lpSource->bmiHeader.biBitCount == 4 )
            { // Source is 4bpp too, copy color table
                memcpy( lpTarget->bmiColors, lpSource->bmiColors, 16*sizeof(RGBQUAD) );
                return TRUE;
            }
            else
            { // Source is != 4bpp, use system palette
                HPALETTE        hPal;
                PALETTEENTRY    pe[256];
                UINT            i;

                hPal = GetStockObject( DEFAULT_PALETTE );
                GetPaletteEntries( hPal, 0, 16, pe );
                for(i=0;i<16;i++)
                {
                    lpTarget->bmiColors[i].rgbRed = pe[i].peRed;
                    lpTarget->bmiColors[i].rgbGreen = pe[i].peGreen;
                    lpTarget->bmiColors[i].rgbBlue = pe[i].peBlue;
                    lpTarget->bmiColors[i].rgbReserved = pe[i].peFlags;
                }
                return TRUE;
            }
        break; // end 4bpp

        // 1bpp - need 2 entry mono color table
        case 1:
            lpTarget->bmiColors[0].rgbRed = 0;
            lpTarget->bmiColors[0].rgbGreen = 0;
            lpTarget->bmiColors[0].rgbBlue = 0;
            lpTarget->bmiColors[0].rgbReserved = 0;
            lpTarget->bmiColors[1].rgbRed = 255;
            lpTarget->bmiColors[1].rgbGreen = 255;
            lpTarget->bmiColors[1].rgbBlue = 255;
            lpTarget->bmiColors[1].rgbReserved = 0;
        break; // end 1bpp

        // no color table for the > 8bpp modes
        case 32:
        case 24:
        case 16:
        default:
            return TRUE;
        break;
    }
    return TRUE;
}
/* End CopyColorTable() *****************************************************/

/****************************************************************************
*
*     FUNCTION: SetMonoDIBPixel
*
*     PURPOSE:  Sets/Clears a pixel in a 1bpp DIB by directly poking the bits.
*
*     PARAMS:   LPBYTE pANDBits - pointer to the 1bpp image bits
*               DWORD  dwWidth- width of the DIB
*               DWORD  dwHeight- height of the DIB
*               DWORD  x        - x location of pixel to set/clear
*               DWORD  y        - y location of pixel to set/clear
*               BOOL   bWhite- TRUE to set pixel, FALSE to clear it
*
*     RETURNS:  void
*
* History:
*                July '95 - Created
*
\****************************************************************************/
void SetMonoDIBPixel( LPBYTE pANDBits, DWORD dwWidth, DWORD dwHeight, DWORD x, DWORD y, BOOL bWhite )
{
    DWORDByteIndex;
    BYTE    BitNumber;

    // Find the byte on which this scanline begins
    ByteIndex = (dwHeight - y - 1) * WIDTHBYTES(dwWidth);
    // Find the byte containing this pixel
    ByteIndex += (x >> 3);
    // Which bit is it?
    BitNumber = (BYTE)( 7 - (x % 8) );

    if( bWhite )
        // Turn it on
        pANDBits[ByteIndex] |= (1<<BitNumber);
    else
        // Turn it off
        pANDBits[ByteIndex] &= ~(1<<BitNumber);
}
/* End SetMonoDIBPixel() *****************************************************/

/****************************************************************************
*
*     FUNCTION: ReadBMPFile
*
*     PURPOSE:  Reads a BMP file into CF_DIB format
*
*     PARAMS:   LPCTSTR szFileName - the name of the file to read
*
*     RETURNS:  LPBYTE - pointer to the CF_DIB, NULL for failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
LPBYTE ReadBMPFile( LPCTSTR szFileName )
{
    HANDLE            hFile;
    BITMAPFILEHEADER    bfh;
    DWORD            dwBytes;
    LPBYTE            lpDIB = NULL, lpTemp = NULL;
    WORD                wPaletteSize = 0;
    DWORD            dwBitsSize = 0;

    // Open the file
    if( (hFile=CreateFile( szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE )
    {
        MessageBox( NULL, "Error opening file", szFileName, MB_OK );
        return NULL;
    }
    // Read the header
    if( ( ! ReadFile( hFile, &bfh, sizeof(BITMAPFILEHEADER), &dwBytes, NULL ) ) || ( dwBytes != sizeof( BITMAPFILEHEADER ) ) )
    {
        CloseHandle( hFile );
        MessageBox( NULL, "Error reading file", szFileName, MB_OK );
        return NULL;
    }
    // Does it look like a BMP file?
    if( ( bfh.bfType != 0x4d42 ) || (bfh.bfReserved1!=0) || (bfh.bfReserved2!=0) )
    {
        CloseHandle( hFile );
        MessageBox( NULL, "Not a recognised BMP format file", szFileName, MB_OK );
        return NULL;
    }
    // Allocate some memory
    if( (lpDIB = malloc( sizeof( BITMAPINFO ) )) == NULL )
    {
        CloseHandle( hFile );
        MessageBox( NULL, "Failed to allocate memory for DIB", szFileName, MB_OK );
        return NULL;
    }
    // Read in the BITMAPINFOHEADER
    if( (!ReadFile( hFile, lpDIB, sizeof(BITMAPINFOHEADER), &dwBytes, NULL )) || (dwBytes!=sizeof(BITMAPINFOHEADER)) )
    {
        CloseHandle( hFile );
        free( lpDIB );
        MessageBox( NULL, "Error reading file", szFileName, MB_OK );
        return NULL;
    }
    if( ((LPBITMAPINFOHEADER)lpDIB)->biSize != sizeof( BITMAPINFOHEADER ) )
    {
        CloseHandle( hFile );
        free( lpDIB );
        MessageBox( NULL, "OS/2 style BMPs Not Supported", szFileName, MB_OK );
        return NULL;
    }
    // How big are the elements?
    wPaletteSize = PaletteSize((LPSTR)lpDIB);
    dwBitsSize = ((LPBITMAPINFOHEADER)lpDIB)->biHeight * BytesPerLine((LPBITMAPINFOHEADER)lpDIB);
    // realloc to account for the total size of the DIB
    if( (lpTemp = realloc( lpDIB, sizeof( BITMAPINFOHEADER ) + wPaletteSize + dwBitsSize )) == NULL )
    {
        CloseHandle( hFile );
        MessageBox( NULL, "Failed to allocate memory for DIB", szFileName, MB_OK );
        free( lpDIB );
        return NULL;
    }
    lpDIB = lpTemp;
    // If there is a color table, read it
    if( wPaletteSize != 0 )
    {
        if( (!ReadFile( hFile, ((LPBITMAPINFO)lpDIB)->bmiColors, wPaletteSize, &dwBytes, NULL )) || (dwBytes!=wPaletteSize) )
        {
            CloseHandle( hFile );
            free( lpDIB );
            MessageBox( NULL, "Error reading file", szFileName, MB_OK );
            return NULL;
        }
    }
    // Seek to the bits
    // checking against 0 in case some bogus app didn't set this element
    if( bfh.bfOffBits != 0 )
    {
        if( SetFilePointer( hFile, bfh.bfOffBits, NULL, FILE_BEGIN ) == 0xffffffff )
        {
            CloseHandle( hFile );
            free( lpDIB );
            MessageBox( NULL, "Error reading file", szFileName, MB_OK );
            return NULL;
        }
    }
    // Read the image bits
    if( (!ReadFile( hFile, FindDIBBits(lpDIB), dwBitsSize, &dwBytes, NULL )) || (dwBytes!=dwBitsSize) )
    {
        CloseHandle( hFile );
        free( lpDIB );
        MessageBox( NULL, "Error reading file", szFileName, MB_OK );
        return NULL;
    }
    // clean up
    CloseHandle( hFile );
    return lpDIB;
}
/* End ReadBMPFile() ********************************************************/

/****************************************************************************
*
*     FUNCTION: WriteBMPFile
*
*     PURPOSE:  Writes a BMP file from CF_DIB format
*
*     PARAMS:   LPCTSTR szFileName - the name of the file to read
*               LPBYTE - pointer to the CF_DIB, NULL for failure
*
*     RETURNS:  BOOL - TRUE for success, FALSE for Failure
*
* History:
*                July '95 - Created
*
\****************************************************************************/
BOOL WriteBMPFile( LPCTSTR szFileName, LPBYTE lpDIB )
{
    HANDLE            hFile;
    BITMAPFILEHEADER    bfh;
    DWORD            dwBytes, dwBytesToWrite;
    LPBITMAPINFOHEADERlpbmih;

    // Open the file
    if( (hFile=CreateFile( szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE )
    {
        MessageBox( NULL, "Error opening file", szFileName, MB_OK );
        return FALSE;
    }
    bfh.bfType = 0x4d42;
    bfh.bfReserved1 = 0;
    bfh.bfReserved2 = 0;
    bfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + PaletteSize( lpDIB );
    bfh.bfSize = (bfh.bfOffBits + ((LPBITMAPINFOHEADER)lpDIB)->biHeight * BytesPerLine((LPBITMAPINFOHEADER)lpDIB))/4;
    // Write the header
    if( ( ! WriteFile( hFile, &bfh, sizeof(BITMAPFILEHEADER), &dwBytes, NULL ) ) || ( dwBytes != sizeof( BITMAPFILEHEADER ) ) )
    {
        CloseHandle( hFile );
        MessageBox( NULL, "Error Writing file", szFileName, MB_OK );
        return FALSE;
    }
    lpbmih = (LPBITMAPINFOHEADER)lpDIB;
    lpbmih->biHeight /= 2;
    dwBytesToWrite = bfh.bfOffBits + (lpbmih->biHeight * BytesPerLine(lpbmih));
    if( ( ! WriteFile( hFile, lpDIB, dwBytesToWrite, &dwBytes, NULL ) ) || ( dwBytes != dwBytesToWrite ) )
    {
        CloseHandle( hFile );
        MessageBox( NULL, "Error Writing file", szFileName, MB_OK );
        return FALSE;
    }
    lpbmih->biHeight *= 2;
    CloseHandle( hFile );
    return TRUE;
}
/* End WriteBMPFile() *******************************************************/


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by ICONPRO.RC
//
#define IDS_PROGNAME                    1
#define IDS_FILTERSTRING                1
#define IDS_EXEFILTERSTRING             2
#define IDS_BMPFILTERSTRING             3
#define ID_F_MENUOPTION1                100
#define ID_F_ABOUT                      101
#define ID_F_EXIT                       102
#define ICONPRO_MENU                    400
#define ICONPRO_ICON                    401
#define ICONPRO_ABOUT_DLG               402
#define ICONPRO_ICON1                   402
#define IDD_EXTRACTDLG                  404
#define IDD_ADDFORMATDLG                409
#define ID_FILE_NEW                     500
#define ID_FILE_OPEN                    501
#define ID_FILE_SAVE                    502
#define ID_FILE_SAVEAS                  503
#define ID_EDIT_COPY                    504
#define ID_EDIT_PASTE                   505
#define ID_EDIT_STRETCHPASTE            506
#define ID_EDIT_ADDFORMAT               507
#define ID_EDIT_REMOVEFORMAT            508
#define ID_WINDOW_ARRANGEICONS          509
#define ID_WINDOW_CASCADE               510
#define ID_WINDOW_TILE                  511
#define ID_HELP_ABOUT                   512
#define ID_HELP_CONTENTS                513
#define ID_FILE_CLOSE                   514
#define ID_WINDOW_TILEVERTICAL          515
#define ID_FILE_EXTRACT                 516
#define ID_EDIT_IMPORTBMP               518
#define ID_EDIT_STRETCHIMPORTBMP        519
#define ID_EDIT_EXPORTBMP               520
#define IDC_LIST1                       603
#define IDC_DISPLAYICON                 605
#define ID_WIDTHSLIDER                  609
#define ID_HEIGHTSLIDER                 610
#define ID_COLORSLIDER                  615
#define ID_WIDTHTEXT                    616
#define ID_HEIGHTTEXT                   617
#define IDC_SQUAREONLY                  618
#define IDM_WINDOWCHILD                 700
#define ID_GETNUMFORMATS                701
#define ID_UPDATETITLE                  703
#define ID_HASFILECHANGED               705
#define ID_COLORTEXT                    710
#define IDC_DISPLAYBOX                  -1
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        421
#define _APS_NEXT_COMMAND_VALUE         521
#define _APS_NEXT_CONTROL_VALUE         620
#define _APS_NEXT_SYMED_VALUE           706
#endif
#endif


README.TXT

Message Table

The MSGTABLE sample demonstrates how to construct and use a message table
resource in your application.

The sample message table (messages.mc) for the message compiler (MC.EXE)
shows the format of a typical message table source file. The message
table source file contains strings in both English and Japanese. Depending
on what your current language settings are, the correct message strings
will be displayed on the console. Note that some of the entries for each
message are optional but are shown for demonstration purposes. See the
documentation in the tools.hlp file on the message table compiler for more
info on which fields are optional.

The msgtest.exe executable shows how to load the message table DLL, how to
extract the message text from the resource with the FormatMessage API, and
how to decode the various bits in the message identifier. Note that the
messages.h include file to the executable is created during the make process
by the message compiler (mc.exe) as it is creating the binary resource file
from the message table source file.

The makefile demonstrates how to set up your dependancy rules for your
message table source files, output files, and message table DLL.

This sample could be easily modified to link the message table resource
directly into your executable rather than into a stand-alone DLL; there is
no requirement that the message table resouce be located only in a DLL,
though this is the common case.


MSGTEST.C

/* Microsoft Developer Support
   Copyright 1992 - 1998 Microsoft Corporation */

#include <windows.h>
#include <stdio.h>

/* messages.h is created by mc.exe when compiling messages.mc */
#include "messages.h"

#define MAX_MESSAGES 5
#define MAX_MSG_LENGTH 1024
#define SEVERITY_MASK 0xC0000000
#define FACILITY_MASK 0x0FFF0000
#define MSG_ID_MASK 0x0000FFFF

/********************************************************************
* FUNCTION: myPutMsg(HINSTANCE hLib, LPVOID lpArgs, DWORD dwMsgId,  *
*                    DWORD dwLangId)                                *
*                                                                   *
* PURPOSE: format and output error dwMsgId, using string defined    *
*          for language dwLangId, with insert strings lpArgs, from  *
*          the messagetable resource in the DLL referenced by       *
*          handle hLib                                              *
*                                                                   *
* INPUT: Library handle, insert strings, message ID number, and     *
*        language ID as defined in the .mc file                     *
*                                                                   *
* RETURNS: none                                                     *
********************************************************************/

void myPutMsg(HINSTANCE hLib, LPVOID lpArgs, DWORD dwMsgId, DWORD dwLangId)
{
  BOOL bSuccess;
  LPTSTR msgBuf;  /* hold text of the error message that we build */
  int dwCode;  /* hold various codes extracted from dwMsgId */

  /* Here is the layout of the message ID:

   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
  +---+-+-+-----------------------+-------------------------------+
  |Sev|C|R|     Facility          |               Code            |
  +---+-+-+-----------------------+-------------------------------+

  where

      Sev - is the severity code
      C - is the Customer code flag
      R - is a reserved bit
      Facility - is the facility code
      Code - is the facility's status code
  */

  printf("Severity: ");
  /* output the severity and facility code. Mask off the severity */
  /* bits with SEVERITY_MASK and shift them down */
  dwCode = (dwMsgId & SEVERITY_MASK) >> 30;
  switch (dwCode)
    {
    case STATUS_SEVERITY_WARNING:
      printf("STATUS_SEVERITY_WARNING"); break;
    case STATUS_SEVERITY_SUCCESS:
      printf("STATUS_SEVERITY_SUCCESS"); break;
    case STATUS_SEVERITY_INFORMATIONAL:
      printf("STATUS_SEVERITY_INFORMATIONAL"); break;
    case STATUS_SEVERITY_ERROR:
      printf("STATUS_SEVERITY_ERROR"); break;
    default:
      printf("Unknown!"); break;
    }
  printf ("\nFacility: ");
  /* Mask off the facility bits with FACILITY_MASK and shift them down */
  dwCode = (dwMsgId & FACILITY_MASK) >> 16;
  switch (dwCode)
    {
    case FACILITY_SYSTEM:
      printf("FACILITY_SYSTEM"); break;
    case FACILITY_STUBS:
      printf("FACILITY_STUBS"); break;
    case FACILITY_RUNTIME:
      printf("FACILITY_RUNTIME"); break;
    case FACILITY_IO_ERROR_CODE:
      printf("FACILITY_IO_ERROR_CODE"); break;
    default:
      printf("Unknown!"); break;
    }
  /* retrieve and format the message from the messagetable DLL. */
  bSuccess = FormatMessage(
      FORMAT_MESSAGE_FROM_HMODULE | /* get the message from the DLL */
      FORMAT_MESSAGE_ALLOCATE_BUFFER | /* allocate the msg buffer for us */
      FORMAT_MESSAGE_ARGUMENT_ARRAY | /* lpArgs is an array of 32-bit values */
      60, /* line length for the mesages */
      hLib, /* the messagetable DLL handle */
      dwMsgId, /* message ID */
      dwLangId, /* language ID as defined in .mc file */
      (LPTSTR) &msgBuf, /* address of pointer to buffer for message */
      MAX_MSG_LENGTH, /* maximum size of the message buffer */
      lpArgs); /* array of insert strings for the message */
  if (!bSuccess)
    printf("Error %d from FormatMessage\n", GetLastError());
  else
    {
    /* mask off the actual message number with MSG_ID_MASK and show it */
    printf("\nError: %d: %s", dwMsgId & MSG_ID_MASK, msgBuf);
    /* Free the buffer that FormatMessage allocated for us. */
    LocalFree((HLOCAL) msgBuf);
    }
  puts("\n__________\n");
  CloseHandle(hLib);
}

/********************************************************************
* FUNCTION: main()                                                  *
*                                                                   *
* PURPOSE: Load the message resource DLL, and call myPutMsg() to    *
*          format and output error messages to the user             *
*                                                                   *
* INPUT: none                                                       *
*                                                                   *
* RETURNS: none                                                     *
********************************************************************/

int main()
{
  HINSTANCE hLib;  /* handle to the messagetable DLL */
  PTCHAR aInsertStrs[8];  /* array of 32-bit insert values for FormatMessage*/
  WORD wLangID;

  /* Check to make sure we are running on Windows NT */
  if( GetVersion() & 0x80000000 )
    {
    MessageBox(NULL, "Sorry, this application requires Windows NT.\n"
        "This application will now terminate.",
        "Error: Windows NT Required to Run",  MB_OK );
    return(1);
    }
  /* Load the resource library without calling any entry points since */
  /* this is a resource-only DLL */
  hLib = LoadLibraryEx("messages.dll", NULL, DONT_RESOLVE_DLL_REFERENCES);
  if (!hLib)
    printf("Error %d from LoadLibrary\n", GetLastError());
  /* Messages in the .mc file have been defined using standard locale ID's:
     see MAKELANGID and LANG_* definitions in winnt.h */

  wLangID = LANG_USER_DEFAULT;
  /* to force a different language, define the language ID as one of the values
     defined in the Language statement in the .mc file. Alternatively, you
     can choose a different language in the International applet in the control
     panel and stick with LANG_USER_DEFAULT. */
  //wLangID = 0x411;

  /* Output some error messages from the messagetable DLL */
  /* The first three have no insert strings */
  myPutMsg(hLib, NULL, MSG_BAD_COMMAND, wLangID);
  myPutMsg(hLib, NULL, MSG_BAD_PARM1, wLangID);
  myPutMsg(hLib, NULL, MSG_STRIKE_ANY_KEY, wLangID);
  /* wait for user to hit enter as per last error message */
  getchar();
  /* The next two messages contain insert strings - set up our array */
  /* of insert strings and pass the array to myPutMsg() */
  aInsertStrs[0] = "foo.c";
  aInsertStrs[1] = "BAR";
  myPutMsg(hLib, aInsertStrs, MSG_CMD_DELETE, wLangID);
  aInsertStrs[0] = (PTCHAR) 47;
  aInsertStrs[1] = (PTCHAR) 5;
  myPutMsg(hLib, aInsertStrs, MSG_RETRYS, wLangID);
  return(0);
}


BANDOBJS.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          BandObjs.cpp

   Description:   Contains DLLMain and standard OLE COM object creation stuff.

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

/**************************************************************************
   #include statements
**************************************************************************/

#include <ole2.h>
#include <comcat.h>
#include <olectl.h>
#include "ClsFact.h"

/**************************************************************************
   GUID stuff
**************************************************************************/

//this part is only done once
//if you need to use the GUID in another file, just include Guid.h
#pragma data_seg(".text")
#define INITGUID
#include <initguid.h>
#include <shlguid.h>
#include "Guid.h"
#pragma data_seg()

/**************************************************************************
   private function prototypes
**************************************************************************/

extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID);
BOOL RegisterServer(CLSID, LPTSTR);
BOOL RegisterComCat(CLSID, CATID);

/**************************************************************************
   global variables
**************************************************************************/

HINSTANCE   g_hInst;
UINT        g_DllRefCount;

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

   DllMain

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

extern "C" BOOL WINAPI DllMain(  HINSTANCE hInstance,
                                 DWORD dwReason,
                                 LPVOID lpReserved)
{
switch(dwReason)
   {
   case DLL_PROCESS_ATTACH:
      g_hInst = hInstance;
      break;

   case DLL_PROCESS_DETACH:
      break;
   }

return TRUE;
}

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

   DllCanUnloadNow

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

STDAPI DllCanUnloadNow(void)
{
return (g_DllRefCount ? S_FALSE : S_OK);
}

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

   DllGetClassObject

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

STDAPI DllGetClassObject(  REFCLSID rclsid,
                           REFIID riid,
                           LPVOID *ppReturn)
{
*ppReturn = NULL;

//if we don't support this classid, return the proper error code
if(   !IsEqualCLSID(rclsid, CLSID_SampleDeskBand) &&
      !IsEqualCLSID(rclsid, CLSID_SampleExplorerBar) &&
      !IsEqualCLSID(rclsid, CLSID_SampleCommBand))
   return CLASS_E_CLASSNOTAVAILABLE;

//create a CClassFactory object and check it for validity
CClassFactory *pClassFactory = new CClassFactory(rclsid);
if(NULL == pClassFactory)
   return E_OUTOFMEMORY;

//get the QueryInterface return for our return value
HRESULT hResult = pClassFactory->QueryInterface(riid, ppReturn);

//call Release to decement the ref count - creating the object set it to one
//and QueryInterface incremented it - since its being used externally (not by
//us), we only want the ref count to be 1
pClassFactory->Release();

//return the result from QueryInterface
return hResult;
}

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

   DllRegisterServer

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

STDAPI DllRegisterServer(void)
{
//Register the desk band object.
if(!RegisterServer(CLSID_SampleDeskBand, TEXT("Sample &Desk Band")))
   return SELFREG_E_CLASS;

//Register the component categories for the desk band object.
if(!RegisterComCat(CLSID_SampleDeskBand, CATID_DeskBand))
   return SELFREG_E_CLASS;

//Register the explorer bar object.
if(!RegisterServer(CLSID_SampleExplorerBar, TEXT("Sample &Explorer Bar")))
   return SELFREG_E_CLASS;

//Register the component categories for the explorer bar object.
if(!RegisterComCat(CLSID_SampleExplorerBar, CATID_InfoBand))
   return SELFREG_E_CLASS;

//Register the comm band object.
if(!RegisterServer(CLSID_SampleCommBand, TEXT("Sample &Comm Band")))
   return SELFREG_E_CLASS;

//Register the component categories for the comm band object.
if(!RegisterComCat(CLSID_SampleCommBand, CATID_CommBand))
   return SELFREG_E_CLASS;

return S_OK;
}

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

   RegisterServer

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

typedef struct{
   HKEY  hRootKey;
   LPTSTR szSubKey;//TCHAR szSubKey[MAX_PATH];
   LPTSTR lpszValueName;
   LPTSTR szData;//TCHAR szData[MAX_PATH];
}DOREGSTRUCT, *LPDOREGSTRUCT;

BOOL RegisterServer(CLSID clsid, LPTSTR lpszTitle)
{
int      i;
HKEY     hKey;
LRESULT  lResult;
DWORD    dwDisp;
TCHAR    szSubKey[MAX_PATH];
TCHAR    szCLSID[MAX_PATH];
TCHAR    szModule[MAX_PATH];
LPWSTR   pwsz;

//get the CLSID in string form
StringFromIID(clsid, &pwsz);

if(pwsz)
   {
#ifdef UNICODE
   lstrcpy(szCLSID, pwsz);
#else
   WideCharToMultiByte( CP_ACP,
                        0,
                        pwsz,
                        -1,
                        szCLSID,
                        ARRAYSIZE(szCLSID),
                        NULL,
                        NULL);
#endif

   //free the string
   LPMALLOC pMalloc;
   CoGetMalloc(1, &pMalloc);
   pMalloc->Free(pwsz);
   pMalloc->Release();
   }

//get this app's path and file name
GetModuleFileName(g_hInst, szModule, ARRAYSIZE(szModule));

DOREGSTRUCT ClsidEntries[] = {HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s"),                  NULL,                   lpszTitle,
                              HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s\\InprocServer32"),  NULL,                   szModule,
                              HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s\\InprocServer32"),  TEXT("ThreadingModel"), TEXT("Apartment"),
                              NULL,                NULL,                               NULL,                   NULL};

//register the CLSID entries
for(i = 0; ClsidEntries[i].hRootKey; i++)
   {
   //create the sub key string - for this case, insert the file extension
   wsprintf(szSubKey, ClsidEntries[i].szSubKey, szCLSID);

   lResult = RegCreateKeyEx(  ClsidEntries[i].hRootKey,
                              szSubKey,
                              0,
                              NULL,
                              REG_OPTION_NON_VOLATILE,
                              KEY_WRITE,
                              NULL,
                              &hKey,
                              &dwDisp);

   if(NOERROR == lResult)
      {
      TCHAR szData[MAX_PATH];

      //if necessary, create the value string
      wsprintf(szData, ClsidEntries[i].szData, szModule);

      lResult = RegSetValueEx(   hKey,
                                 ClsidEntries[i].lpszValueName,
                                 0,
                                 REG_SZ,
                                 (LPBYTE)szData,
                                 lstrlen(szData) + 1);

      RegCloseKey(hKey);
      }
   else
      return FALSE;
   }

return TRUE;
}

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

   RegisterComCat

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

BOOL RegisterComCat(CLSID clsid, CATID CatID)
{
ICatRegister   *pcr;
HRESULT        hr = S_OK ;

CoInitialize(NULL);

hr = CoCreateInstance(  CLSID_StdComponentCategoriesMgr,
                        NULL,
                        CLSCTX_INPROC_SERVER,
                        IID_ICatRegister,
                        (LPVOID*)&pcr);

if(SUCCEEDED(hr))
   {
   hr = pcr->RegisterClassImplCategories(clsid, 1, &CatID);

   pcr->Release();
   }

CoUninitialize();

return SUCCEEDED(hr);
}


CLSFACT.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ClsFact.cpp

   Description:   Implements CClassFactory.

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "ClsFact.h"
#include "Guid.h"

/**************************************************************************
   private function prototypes
**************************************************************************/

/**************************************************************************
   global variables
**************************************************************************/

///////////////////////////////////////////////////////////////////////////
//
// IClassFactory implementation
//

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

   CClassFactory::CClassFactory

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

CClassFactory::CClassFactory(CLSID clsid)
{
m_clsidObject = clsid;
m_ObjRefCount = 1;
g_DllRefCount++;
}

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

   CClassFactory::~CClassFactory

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

CClassFactory::~CClassFactory()
{
g_DllRefCount--;
}

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

   CClassFactory::QueryInterface

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

STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

else if(IsEqualIID(riid, IID_IClassFactory))
   {
   *ppReturn = (IClassFactory*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CClassFactory::AddRef

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

STDMETHODIMP_(DWORD) CClassFactory::AddRef()
{
return ++m_ObjRefCount;
}

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

   CClassFactory::Release

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

STDMETHODIMP_(DWORD) CClassFactory::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

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

   CClassFactory::CreateInstance

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

STDMETHODIMP CClassFactory::CreateInstance(  LPUNKNOWN pUnknown,
                                             REFIID riid,
                                             LPVOID *ppObject)
{
HRESULT  hResult = E_FAIL;
LPVOID   pTemp = NULL;

*ppObject = NULL;

if(pUnknown != NULL)
   return CLASS_E_NOAGGREGATION;

//create the proper object
if(IsEqualCLSID(m_clsidObject, CLSID_SampleDeskBand))
   {
   CDeskBand *pDeskBand = new CDeskBand();
   if(NULL == pDeskBand)
      return E_OUTOFMEMORY;

   pTemp = pDeskBand;
   }

if(IsEqualCLSID(m_clsidObject, CLSID_SampleExplorerBar))
   {
   CExplorerBar *pExplorerBar = new CExplorerBar();
   if(NULL == pExplorerBar)
      return E_OUTOFMEMORY;

   pTemp = pExplorerBar;
   }

if(IsEqualCLSID(m_clsidObject, CLSID_SampleCommBand))
   {
   CCommBand *pCommBand = new CCommBand();
   if(NULL == pCommBand)
      return E_OUTOFMEMORY;

   pTemp = pCommBand;
   }

if(pTemp)
   {
   //get the QueryInterface return for our return value
   hResult = ((LPUNKNOWN)pTemp)->QueryInterface(riid, ppObject);

   //call Release to decement the ref count
   ((LPUNKNOWN)pTemp)->Release();
   }

return hResult;
}

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

   CClassFactory::LockServer

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

STDMETHODIMP CClassFactory::LockServer(BOOL)
{
return E_NOTIMPL;
}


CLSFACT.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ClsFact.h

   Description:   CClassFactory definitions.

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

#ifndef CLASSFACTORY_H
#define CLASSFACTORY_H

#include <windows.h>
#include "Globals.h"
#include "DeskBand.h"
#include "ExplrBar.h"
#include "CommBand.h"

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

   CClassFactory class definition

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

class CClassFactory : public IClassFactory
{
protected:
   DWORD m_ObjRefCount;

public:
   CClassFactory(CLSID);
   ~CClassFactory();

   //IUnknown methods
   STDMETHODIMP QueryInterface(REFIID, LPVOID*);
   STDMETHODIMP_(DWORD) AddRef();
   STDMETHODIMP_(DWORD) Release();

   //IClassFactory methods
   STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID*);
   STDMETHODIMP LockServer(BOOL);

private:
   CLSID m_clsidObject;
};

#endif   //CLASSFACTORY_H


COMMBAND.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          CommBand.cpp

   Description:   Implements CCommBand

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "CommBand.h"
#include "Guid.h"

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

   CCommBand::CCommBand()

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

CCommBand::CCommBand()
{
m_pSite = NULL;

m_hWnd = NULL;
m_hwndParent = NULL;

m_bFocus = FALSE;

m_dwViewMode = 0;
m_dwBandID = 0;

m_ObjRefCount = 1;
g_DllRefCount++;
}

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

   CCommBand::~CCommBand()

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

CCommBand::~CCommBand()
{
//this should have been freed in a call to SetSite(NULL), but just to be safe
if(m_pSite)
   {
   m_pSite->Release();
   m_pSite = NULL;
   }

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

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

   CCommBand::QueryInterface

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

STDMETHODIMP CCommBand::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IOleWindow
else if(IsEqualIID(riid, IID_IOleWindow))
   {
   *ppReturn = (IOleWindow*)this;
   }

//IDockingWindow
else if(IsEqualIID(riid, IID_IDockingWindow))
   {
   *ppReturn = (IDockingWindow*)this;
   }

//IInputObject
else if(IsEqualIID(riid, IID_IInputObject))
   {
   *ppReturn = (IInputObject*)this;
   }

//IObjectWithSite
else if(IsEqualIID(riid, IID_IObjectWithSite))
   {
   *ppReturn = (IObjectWithSite*)this;
   }

//IDeskBand
else if(IsEqualIID(riid, IID_IDeskBand))
   {
   *ppReturn = (IDeskBand*)this;
   }

//IPersist
else if(IsEqualIID(riid, IID_IPersist))
   {
   *ppReturn = (IPersist*)this;
   }

//IPersistStream
else if(IsEqualIID(riid, IID_IPersistStream))
   {
   *ppReturn = (IPersistStream*)this;
   }

//IContextMenu
else if(IsEqualIID(riid, IID_IContextMenu))
   {
   *ppReturn = (IContextMenu*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CCommBand::AddRef

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

STDMETHODIMP_(DWORD) CCommBand::AddRef()
{
return ++m_ObjRefCount;
}

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

   CCommBand::Release

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

STDMETHODIMP_(DWORD) CCommBand::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IOleWindow Implementation
//

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

   CCommBand::GetWindow()

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

STDMETHODIMP CCommBand::GetWindow(HWND *phWnd)
{
*phWnd = m_hWnd;

return S_OK;
}

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

   CCommBand::ContextSensitiveHelp()

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

STDMETHODIMP CCommBand::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IDockingWindow Implementation
//

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

   CCommBand::ShowDW()

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

STDMETHODIMP CCommBand::ShowDW(BOOL fShow)
{
if(m_hWnd)
   {
   if(fShow)
      {
      //show our window
      ShowWindow(m_hWnd, SW_SHOW);
      }
   else
      {
      //hide our window
      ShowWindow(m_hWnd, SW_HIDE);
      }
   }

return S_OK;
}

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

   CCommBand::CloseDW()

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

STDMETHODIMP CCommBand::CloseDW(DWORD dwReserved)
{
ShowDW(FALSE);

if(IsWindow(m_hWnd))
   DestroyWindow(m_hWnd);

m_hWnd = NULL;

return S_OK;
}

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

   CCommBand::ResizeBorderDW()

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

STDMETHODIMP CCommBand::ResizeBorderDW(   LPCRECT prcBorder,
                                          IUnknown* punkSite,
                                          BOOL fReserved)
{
/*
This method is never called for Band Objects.
*/
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IInputObject Implementation
//

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

   CCommBand::UIActivateIO()

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

STDMETHODIMP CCommBand::UIActivateIO(BOOL fActivate, LPMSG pMsg)
{
if(fActivate)
   SetFocus(m_hWnd);

return S_OK;
}

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

   CCommBand::HasFocusIO()

   If this window or one of its decendants has the focus, return S_OK. Return
   S_FALSE if we don't have the focus.

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

STDMETHODIMP CCommBand::HasFocusIO(void)
{
if(m_bFocus)
   return S_OK;

return S_FALSE;
}

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

   CCommBand::TranslateAcceleratorIO()

   If the accelerator is translated, return S_OK or S_FALSE otherwise.

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

STDMETHODIMP CCommBand::TranslateAcceleratorIO(LPMSG pMsg)
{
return S_FALSE;
}

///////////////////////////////////////////////////////////////////////////
//
// IObjectWithSite implementations
//

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

   CCommBand::SetSite()

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

STDMETHODIMP CCommBand::SetSite(IUnknown* punkSite)
{
//If a site is being held, release it.
if(m_pSite)
   {
   m_pSite->Release();
   m_pSite = NULL;
   }

//If punkSite is not NULL, a new site is being set.
if(punkSite)
   {
   //Get the parent window.
   IOleWindow  *pOleWindow;

   m_hwndParent = NULL;

   if(SUCCEEDED(punkSite->QueryInterface(IID_IOleWindow, (LPVOID*)&pOleWindow)))
      {
      pOleWindow->GetWindow(&m_hwndParent);
      pOleWindow->Release();
      }

   if(!m_hwndParent)
      return E_FAIL;

   if(!RegisterAndCreateWindow())
      return E_FAIL;

   //Get and keep the IInputObjectSite pointer.
   if(SUCCEEDED(punkSite->QueryInterface(IID_IInputObjectSite, (LPVOID*)&m_pSite)))
      {
      return S_OK;
      }

   return E_FAIL;
   }

return S_OK;
}

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

   CCommBand::GetSite()

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

STDMETHODIMP CCommBand::GetSite(REFIID riid, LPVOID *ppvReturn)
{
*ppvReturn = NULL;

if(m_pSite)
   return m_pSite->QueryInterface(riid, ppvReturn);

return E_FAIL;
}

///////////////////////////////////////////////////////////////////////////
//
// IDeskBand implementation
//

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

   CCommBand::GetBandInfo()

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

STDMETHODIMP CCommBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi)
{
if(pdbi)
   {
   m_dwBandID = dwBandID;
   m_dwViewMode = dwViewMode;

   if(pdbi->dwMask & DBIM_MINSIZE)
      {
      pdbi->ptMinSize.x = 0;
      pdbi->ptMinSize.y = 30;
      }

   if(pdbi->dwMask & DBIM_MAXSIZE)
      {
      pdbi->ptMaxSize.x = -1;
      pdbi->ptMaxSize.y = 30;
      }

   if(pdbi->dwMask & DBIM_INTEGRAL)
      {
      pdbi->ptIntegral.x = 1;
      pdbi->ptIntegral.y = 1;
      }

   if(pdbi->dwMask & DBIM_ACTUAL)
      {
      pdbi->ptActual.x = 0;
      pdbi->ptActual.y = 0;
      }

   if(pdbi->dwMask & DBIM_TITLE)
      {
      lstrcpyW(pdbi->wszTitle, L"");
      }

   if(pdbi->dwMask & DBIM_MODEFLAGS)
      {
      pdbi->dwModeFlags = DBIMF_NORMAL;
      }

   if(pdbi->dwMask & DBIM_BKCOLOR)
      {
      //Use the default background color by removing this flag.
      pdbi->dwMask &= ~DBIM_BKCOLOR;
      }

   return S_OK;
   }

return E_INVALIDARG;
}

///////////////////////////////////////////////////////////////////////////
//
// IPersistStream implementations
//
// This is only supported to allow the desk band to be dropped on the
// desktop and to prevent multiple instances of the desk band from showing
// up in the context menu. This desk band doesn't actually persist any data.
//

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

   CCommBand::GetClassID()

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

STDMETHODIMP CCommBand::GetClassID(LPCLSID pClassID)
{
*pClassID = CLSID_SampleCommBand;

return S_OK;
}

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

   CCommBand::IsDirty()

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

STDMETHODIMP CCommBand::IsDirty(void)
{
return S_FALSE;
}

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

   CCommBand::Load()

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

STDMETHODIMP CCommBand::Load(LPSTREAM pStream)
{
return S_OK;
}

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

   CCommBand::Save()

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

STDMETHODIMP CCommBand::Save(LPSTREAM pStream, BOOL fClearDirty)
{
return S_OK;
}

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

   CCommBand::GetSizeMax()

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

STDMETHODIMP CCommBand::GetSizeMax(ULARGE_INTEGER *pul)
{
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IContextMenu Implementation
//

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

   CCommBand::QueryContextMenu()

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

STDMETHODIMP CCommBand::QueryContextMenu( HMENU hMenu,
                                          UINT indexMenu,
                                          UINT idCmdFirst,
                                          UINT idCmdLast,
                                          UINT uFlags)
{
if(!(CMF_DEFAULTONLY & uFlags))
   {
   InsertMenu( hMenu,
               indexMenu,
               MF_STRING | MF_BYPOSITION,
               idCmdFirst + IDM_COMMAND,
               "&Sample Comm Band Command");

   return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_COMMAND + 1));
   }

return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}

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

   CCommBand::InvokeCommand()

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

STDMETHODIMP CCommBand::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
switch (LOWORD(lpcmi->lpVerb))
   {
   case IDM_COMMAND:
      MessageBox(lpcmi->hwnd, TEXT("Comm Band Command selected."), TEXT("Sample Comm Band"), MB_OK | MB_ICONINFORMATION);
      break;

   default:
      return E_INVALIDARG;
   }

return NOERROR;
}

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

   CCommBand::GetCommandString()

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

STDMETHODIMP CCommBand::GetCommandString( UINT idCommand,
                                          UINT uFlags,
                                          LPUINT lpReserved,
                                          LPSTR lpszName,
                                          UINT uMaxNameLen)
{
HRESULT  hr = E_INVALIDARG;

switch(uFlags)
   {
   case GCS_HELPTEXT:
      switch(idCommand)
         {
         case IDM_COMMAND:
            lstrcpy(lpszName, TEXT("Comm Band command help text"));
            hr = NOERROR;
            break;
         }
      break;

   case GCS_VERB:
      switch(idCommand)
         {
         case IDM_COMMAND:
            lstrcpy(lpszName, TEXT("command"));
            hr = NOERROR;
            break;
         }
      break;

   case GCS_VALIDATE:
      hr = NOERROR;
      break;
   }

return hr;
}

///////////////////////////////////////////////////////////////////////////
//
// private method implementations
//

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

   CCommBand::WndProc()

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

LRESULT CALLBACK CCommBand::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
CCommBand  *pThis = (CCommBand*)GetWindowLong(hWnd, GWL_USERDATA);

switch (uMessage)
   {
   case WM_NCCREATE:
      {
      LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
      pThis = (CCommBand*)(lpcs->lpCreateParams);
      SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);

      //set the window handle
      pThis->m_hWnd = hWnd;
      }
      break;

   case WM_PAINT:
      return pThis->OnPaint();

   case WM_COMMAND:
      return pThis->OnCommand(wParam, lParam);

   case WM_SETFOCUS:
      return pThis->OnSetFocus();

   case WM_KILLFOCUS:
      return pThis->OnKillFocus();
   }

return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

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

   CCommBand::OnPaint()

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

LRESULT CCommBand::OnPaint(void)
{
PAINTSTRUCT ps;
RECT        rc;

BeginPaint(m_hWnd, &ps);

GetClientRect(m_hWnd, &rc);
SetTextColor(ps.hdc, RGB(0, 0, 0));
SetBkMode(ps.hdc, TRANSPARENT);
DrawText(ps.hdc, TEXT("Sample Comm Band"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

EndPaint(m_hWnd, &ps);

return 0;
}

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

   CCommBand::OnCommand()

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

LRESULT CCommBand::OnCommand(WPARAM wParam, LPARAM lParam)
{
return 0;
}

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

   CCommBand::FocusChange()

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

void CCommBand::FocusChange(BOOL bFocus)
{
m_bFocus = bFocus;

//inform the input object site that the focus has changed
if(m_pSite)
   {
   m_pSite->OnFocusChangeIS((IDockingWindow*)this, bFocus);
   }
}

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

   CCommBand::OnSetFocus()

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

LRESULT CCommBand::OnSetFocus(void)
{
FocusChange(TRUE);

return 0;
}

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

   CCommBand::OnKillFocus()

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

LRESULT CCommBand::OnKillFocus(void)
{
FocusChange(FALSE);

return 0;
}

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

   CCommBand::RegisterAndCreateWindow()

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

BOOL CCommBand::RegisterAndCreateWindow(void)
{
//If the window doesn't exist yet, create it now.
if(!m_hWnd)
   {
   //Can't create a child window without a parent.
   if(!m_hwndParent)
      {
      return FALSE;
      }

   //If the window class has not been registered, then do so.
   WNDCLASS wc;
   if(!GetClassInfo(g_hInst, CB_CLASS_NAME, &wc))
      {
      ZeroMemory(&wc, sizeof(wc));
      wc.style          = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
      wc.lpfnWndProc    = (WNDPROC)WndProc;
      wc.cbClsExtra     = 0;
      wc.cbWndExtra     = 0;
      wc.hInstance      = g_hInst;
      wc.hIcon          = NULL;
      wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground  = (HBRUSH)CreateSolidBrush(RGB(0, 192, 0));
      wc.lpszMenuName   = NULL;
      wc.lpszClassName  = CB_CLASS_NAME;

      if(!RegisterClass(&wc))
         {
         //If RegisterClass fails, CreateWindow below will fail.
         }
      }

   RECT  rc;

   GetClientRect(m_hwndParent, &rc);

   //Create the window. The WndProc will set m_hWnd.
   CreateWindowEx(   0,
                     CB_CLASS_NAME,
                     NULL,
                     WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER,
                     rc.left,
                     rc.top,
                     rc.right - rc.left,
                     rc.bottom - rc.top,
                     m_hwndParent,
                     NULL,
                     g_hInst,
                     (LPVOID)this);

   }

return (NULL != m_hWnd);
}


COMMBAND.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          CommBand.h

   Description:   CCommBand definitions.

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

#include <windows.h>
#include <shlobj.h>

#include "Globals.h"

#ifndef COMMBAND_H
#define COMMBAND_H

#define CB_CLASS_NAME (TEXT("CommBandSampleClass"))

#define IDM_COMMAND  0

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

   CCommBand class definition

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

class CCommBand : public IDeskBand,
                  public IInputObject,
                  public IObjectWithSite,
                  public IPersistStream,
                  public IContextMenu
{
protected:
   DWORD m_ObjRefCount;

public:
   CCommBand();
   ~CCommBand();

   //IUnknown methods
   STDMETHODIMP QueryInterface(REFIID, LPVOID*);
   STDMETHODIMP_(DWORD) AddRef();
   STDMETHODIMP_(DWORD) Release();

   //IOleWindow methods
   STDMETHOD (GetWindow) (HWND*);
   STDMETHOD (ContextSensitiveHelp) (BOOL);

   //IDockingWindow methods
   STDMETHOD (ShowDW) (BOOL fShow);
   STDMETHOD (CloseDW) (DWORD dwReserved);
   STDMETHOD (ResizeBorderDW) (LPCRECT prcBorder, IUnknown* punkToolbarSite, BOOL fReserved);

   //IDeskBand methods
   STDMETHOD (GetBandInfo) (DWORD, DWORD, DESKBANDINFO*);

   //IInputObject methods
   STDMETHOD (UIActivateIO) (BOOL, LPMSG);
   STDMETHOD (HasFocusIO) (void);
   STDMETHOD (TranslateAcceleratorIO) (LPMSG);

   //IObjectWithSite methods
   STDMETHOD (SetSite) (IUnknown*);
   STDMETHOD (GetSite) (REFIID, LPVOID*);

   //IPersistStream methods
   STDMETHOD (GetClassID) (LPCLSID);
   STDMETHOD (IsDirty) (void);
   STDMETHOD (Load) (LPSTREAM);
   STDMETHOD (Save) (LPSTREAM, BOOL);
   STDMETHOD (GetSizeMax) (ULARGE_INTEGER*);

   //IContextMenu methods
   STDMETHOD (QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT);
   STDMETHOD (InvokeCommand)(LPCMINVOKECOMMANDINFO);
   STDMETHOD (GetCommandString)(UINT, UINT, LPUINT, LPSTR, UINT);

private:
BOOL m_bFocus;
HWND m_hwndParent;
HWND m_hWnd;
DWORD m_dwViewMode;
   DWORD m_dwBandID;
   IInputObjectSite *m_pSite;

private:
void FocusChange(BOOL);
   LRESULT OnKillFocus(void);
LRESULT OnSetFocus(void);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
LRESULT OnPaint(void);
LRESULT OnCommand(WPARAM wParam, LPARAM lParam);
   BOOL RegisterAndCreateWindow(void);
};

#endif   //COMMBAND_H


DESKBAND.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          DeskBand.cpp

   Description:   Implements CDeskBand

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "DeskBand.h"
#include "Guid.h"

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

   CDeskBand::CDeskBand()

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

CDeskBand::CDeskBand()
{
m_pSite = NULL;

m_hWnd = NULL;
m_hwndParent = NULL;

m_bFocus = FALSE;

m_dwViewMode = 0;
m_dwBandID = 0;

m_ObjRefCount = 1;
g_DllRefCount++;
}

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

   CDeskBand::~CDeskBand()

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

CDeskBand::~CDeskBand()
{
//this should have been freed in a call to SetSite(NULL), but just to be safe
if(m_pSite)
   {
   m_pSite->Release();
   m_pSite = NULL;
   }

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

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

   CDeskBand::QueryInterface

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

STDMETHODIMP CDeskBand::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IOleWindow
else if(IsEqualIID(riid, IID_IOleWindow))
   {
   *ppReturn = (IOleWindow*)this;
   }

//IDockingWindow
else if(IsEqualIID(riid, IID_IDockingWindow))
   {
   *ppReturn = (IDockingWindow*)this;
   }

//IInputObject
else if(IsEqualIID(riid, IID_IInputObject))
   {
   *ppReturn = (IInputObject*)this;
   }

//IObjectWithSite
else if(IsEqualIID(riid, IID_IObjectWithSite))
   {
   *ppReturn = (IObjectWithSite*)this;
   }

//IDeskBand
else if(IsEqualIID(riid, IID_IDeskBand))
   {
   *ppReturn = (IDeskBand*)this;
   }

//IPersist
else if(IsEqualIID(riid, IID_IPersist))
   {
   *ppReturn = (IPersist*)this;
   }

//IPersistStream
else if(IsEqualIID(riid, IID_IPersistStream))
   {
   *ppReturn = (IPersistStream*)this;
   }

//IContextMenu
else if(IsEqualIID(riid, IID_IContextMenu))
   {
   *ppReturn = (IContextMenu*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CDeskBand::AddRef

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

STDMETHODIMP_(DWORD) CDeskBand::AddRef()
{
return ++m_ObjRefCount;
}

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

   CDeskBand::Release

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

STDMETHODIMP_(DWORD) CDeskBand::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IOleWindow Implementation
//

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

   CDeskBand::GetWindow()

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

STDMETHODIMP CDeskBand::GetWindow(HWND *phWnd)
{
*phWnd = m_hWnd;

return S_OK;
}

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

   CDeskBand::ContextSensitiveHelp()

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

STDMETHODIMP CDeskBand::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IDockingWindow Implementation
//

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

   CDeskBand::ShowDW()

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

STDMETHODIMP CDeskBand::ShowDW(BOOL fShow)
{
if(m_hWnd)
   {
   if(fShow)
      {
      //show our window
      ShowWindow(m_hWnd, SW_SHOW);
      }
   else
      {
      //hide our window
      ShowWindow(m_hWnd, SW_HIDE);
      }
   }

return S_OK;
}

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

   CDeskBand::CloseDW()

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

STDMETHODIMP CDeskBand::CloseDW(DWORD dwReserved)
{
ShowDW(FALSE);

if(IsWindow(m_hWnd))
   DestroyWindow(m_hWnd);

m_hWnd = NULL;

return S_OK;
}

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

   CDeskBand::ResizeBorderDW()

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

STDMETHODIMP CDeskBand::ResizeBorderDW(   LPCRECT prcBorder,
                                          IUnknown* punkSite,
                                          BOOL fReserved)
{
/*
This method is never called for Band Objects.
*/
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IInputObject Implementation
//

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

   CDeskBand::UIActivateIO()

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

STDMETHODIMP CDeskBand::UIActivateIO(BOOL fActivate, LPMSG pMsg)
{
if(fActivate)
   SetFocus(m_hWnd);

return S_OK;
}

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

   CDeskBand::HasFocusIO()

   If this window or one of its decendants has the focus, return S_OK. Return
   S_FALSE if we don't have the focus.

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

STDMETHODIMP CDeskBand::HasFocusIO(void)
{
if(m_bFocus)
   return S_OK;

return S_FALSE;
}

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

   CDeskBand::TranslateAcceleratorIO()

   If the accelerator is translated, return S_OK or S_FALSE otherwise.

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

STDMETHODIMP CDeskBand::TranslateAcceleratorIO(LPMSG pMsg)
{
return S_FALSE;
}

///////////////////////////////////////////////////////////////////////////
//
// IObjectWithSite implementations
//

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

   CDeskBand::SetSite()

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

STDMETHODIMP CDeskBand::SetSite(IUnknown* punkSite)
{
//If a site is being held, release it.
if(m_pSite)
   {
   m_pSite->Release();
   m_pSite = NULL;
   }

//If punkSite is not NULL, a new site is being set.
if(punkSite)
   {
   //Get the parent window.
   IOleWindow  *pOleWindow;

   m_hwndParent = NULL;

   if(SUCCEEDED(punkSite->QueryInterface(IID_IOleWindow, (LPVOID*)&pOleWindow)))
      {
      pOleWindow->GetWindow(&m_hwndParent);
      pOleWindow->Release();
      }

   if(!m_hwndParent)
      return E_FAIL;

   if(!RegisterAndCreateWindow())
      return E_FAIL;

   //Get and keep the IInputObjectSite pointer.
   if(SUCCEEDED(punkSite->QueryInterface(IID_IInputObjectSite, (LPVOID*)&m_pSite)))
      {
      return S_OK;
      }

   return E_FAIL;
   }

return S_OK;
}

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

   CDeskBand::GetSite()

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

STDMETHODIMP CDeskBand::GetSite(REFIID riid, LPVOID *ppvReturn)
{
*ppvReturn = NULL;

if(m_pSite)
   return m_pSite->QueryInterface(riid, ppvReturn);

return E_FAIL;
}

///////////////////////////////////////////////////////////////////////////
//
// IDeskBand implementation
//

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

   CDeskBand::GetBandInfo()

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

STDMETHODIMP CDeskBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi)
{
if(pdbi)
   {
   m_dwBandID = dwBandID;
   m_dwViewMode = dwViewMode;

   if(pdbi->dwMask & DBIM_MINSIZE)
      {
      pdbi->ptMinSize.x = MIN_SIZE_X;
      pdbi->ptMinSize.y = MIN_SIZE_Y;
      }

   if(pdbi->dwMask & DBIM_MAXSIZE)
      {
      pdbi->ptMaxSize.x = -1;
      pdbi->ptMaxSize.y = -1;
      }

   if(pdbi->dwMask & DBIM_INTEGRAL)
      {
      pdbi->ptIntegral.x = 1;
      pdbi->ptIntegral.y = 1;
      }

   if(pdbi->dwMask & DBIM_ACTUAL)
      {
      pdbi->ptActual.x = 0;
      pdbi->ptActual.y = 0;
      }

   if(pdbi->dwMask & DBIM_TITLE)
      {
      lstrcpyW(pdbi->wszTitle, L"Sample Desk Band");
      }

   if(pdbi->dwMask & DBIM_MODEFLAGS)
      {
      pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;
      }

   if(pdbi->dwMask & DBIM_BKCOLOR)
      {
      //Use the default background color by removing this flag.
      pdbi->dwMask &= ~DBIM_BKCOLOR;
      }

   return S_OK;
   }

return E_INVALIDARG;
}

///////////////////////////////////////////////////////////////////////////
//
// IPersistStream implementations
//
// This is only supported to allow the desk band to be dropped on the
// desktop and to prevent multiple instances of the desk band from showing
// up in the context menu. This desk band doesn't actually persist any data.
//

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

   CDeskBand::GetClassID()

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

STDMETHODIMP CDeskBand::GetClassID(LPCLSID pClassID)
{
*pClassID = CLSID_SampleDeskBand;

return S_OK;
}

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

   CDeskBand::IsDirty()

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

STDMETHODIMP CDeskBand::IsDirty(void)
{
return S_FALSE;
}

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

   CDeskBand::Load()

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

STDMETHODIMP CDeskBand::Load(LPSTREAM pStream)
{
return S_OK;
}

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

   CDeskBand::Save()

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

STDMETHODIMP CDeskBand::Save(LPSTREAM pStream, BOOL fClearDirty)
{
return S_OK;
}

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

   CDeskBand::GetSizeMax()

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

STDMETHODIMP CDeskBand::GetSizeMax(ULARGE_INTEGER *pul)
{
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IContextMenu Implementation
//

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

   CDeskBand::QueryContextMenu()

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

STDMETHODIMP CDeskBand::QueryContextMenu( HMENU hMenu,
                                          UINT indexMenu,
                                          UINT idCmdFirst,
                                          UINT idCmdLast,
                                          UINT uFlags)
{
if(!(CMF_DEFAULTONLY & uFlags))
   {
   InsertMenu( hMenu,
               indexMenu,
               MF_STRING | MF_BYPOSITION,
               idCmdFirst + IDM_COMMAND,
               "&Sample Desk Band Command");

   return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_COMMAND + 1));
   }

return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}

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

   CDeskBand::InvokeCommand()

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

STDMETHODIMP CDeskBand::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
switch (LOWORD(lpcmi->lpVerb))
   {
   case IDM_COMMAND:
      MessageBox(lpcmi->hwnd, TEXT("Desk Band Command selected."), TEXT("Sample Desk Band"), MB_OK | MB_ICONINFORMATION);
      break;

   default:
      return E_INVALIDARG;
   }

return NOERROR;
}

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

   CDeskBand::GetCommandString()

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

STDMETHODIMP CDeskBand::GetCommandString( UINT idCommand,
                                          UINT uFlags,
                                          LPUINT lpReserved,
                                          LPSTR lpszName,
                                          UINT uMaxNameLen)
{
HRESULT  hr = E_INVALIDARG;

switch(uFlags)
   {
   case GCS_HELPTEXT:
      switch(idCommand)
         {
         case IDM_COMMAND:
            lstrcpy(lpszName, TEXT("Desk Band command help text"));
            hr = NOERROR;
            break;
         }
      break;

   case GCS_VERB:
      switch(idCommand)
         {
         case IDM_COMMAND:
            lstrcpy(lpszName, TEXT("command"));
            hr = NOERROR;
            break;
         }
      break;

   case GCS_VALIDATE:
      hr = NOERROR;
      break;
   }

return hr;
}

///////////////////////////////////////////////////////////////////////////
//
// private method implementations
//

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

   CDeskBand::WndProc()

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

LRESULT CALLBACK CDeskBand::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
CDeskBand  *pThis = (CDeskBand*)GetWindowLong(hWnd, GWL_USERDATA);

switch (uMessage)
   {
   case WM_NCCREATE:
      {
      LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
      pThis = (CDeskBand*)(lpcs->lpCreateParams);
      SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);

      //set the window handle
      pThis->m_hWnd = hWnd;
      }
      break;

   case WM_PAINT:
      return pThis->OnPaint();

   case WM_COMMAND:
      return pThis->OnCommand(wParam, lParam);

   case WM_SETFOCUS:
      return pThis->OnSetFocus();

   case WM_KILLFOCUS:
      return pThis->OnKillFocus();
   }

return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

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

   CDeskBand::OnPaint()

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

LRESULT CDeskBand::OnPaint(void)
{
PAINTSTRUCT ps;
RECT        rc;

BeginPaint(m_hWnd, &ps);

GetClientRect(m_hWnd, &rc);
SetTextColor(ps.hdc, RGB(0, 0, 0));
SetBkMode(ps.hdc, TRANSPARENT);
DrawText(ps.hdc, TEXT("Sample Desk Band"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

EndPaint(m_hWnd, &ps);

return 0;
}

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

   CDeskBand::OnCommand()

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

LRESULT CDeskBand::OnCommand(WPARAM wParam, LPARAM lParam)
{
return 0;
}

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

   CDeskBand::FocusChange()

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

void CDeskBand::FocusChange(BOOL bFocus)
{
m_bFocus = bFocus;

//inform the input object site that the focus has changed
if(m_pSite)
   {
   m_pSite->OnFocusChangeIS((IDockingWindow*)this, bFocus);
   }
}

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

   CDeskBand::OnSetFocus()

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

LRESULT CDeskBand::OnSetFocus(void)
{
FocusChange(TRUE);

return 0;
}

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

   CDeskBand::OnKillFocus()

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

LRESULT CDeskBand::OnKillFocus(void)
{
FocusChange(FALSE);

return 0;
}

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

   CDeskBand::RegisterAndCreateWindow()

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

BOOL CDeskBand::RegisterAndCreateWindow(void)
{
//If the window doesn't exist yet, create it now.
if(!m_hWnd)
   {
   //Can't create a child window without a parent.
   if(!m_hwndParent)
      {
      return FALSE;
      }

   //If the window class has not been registered, then do so.
   WNDCLASS wc;
   if(!GetClassInfo(g_hInst, DB_CLASS_NAME, &wc))
      {
      ZeroMemory(&wc, sizeof(wc));
      wc.style          = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
      wc.lpfnWndProc    = (WNDPROC)WndProc;
      wc.cbClsExtra     = 0;
      wc.cbWndExtra     = 0;
      wc.hInstance      = g_hInst;
      wc.hIcon          = NULL;
      wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground  = (HBRUSH)CreateSolidBrush(RGB(192, 0, 0));
      wc.lpszMenuName   = NULL;
      wc.lpszClassName  = DB_CLASS_NAME;

      if(!RegisterClass(&wc))
         {
         //If RegisterClass fails, CreateWindow below will fail.
         }
      }

   RECT  rc;

   GetClientRect(m_hwndParent, &rc);

   //Create the window. The WndProc will set m_hWnd.
   CreateWindowEx(   0,
                     DB_CLASS_NAME,
                     NULL,
                     WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER,
                     rc.left,
                     rc.top,
                     rc.right - rc.left,
                     rc.bottom - rc.top,
                     m_hwndParent,
                     NULL,
                     g_hInst,
                     (LPVOID)this);

   }

return (NULL != m_hWnd);
}


DESKBAND.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          DeskBand.h

   Description:   CDeskBand definitions.

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

#include <windows.h>
#include <shlobj.h>

#include "Globals.h"

#ifndef DESKBAND_H
#define DESKBAND_H

#define DB_CLASS_NAME (TEXT("DeskBandSampleClass"))

#define MIN_SIZE_X   10
#define MIN_SIZE_Y   10

#define IDM_COMMAND  0

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

   CDeskBand class definition

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

class CDeskBand : public IDeskBand,
                  public IInputObject,
                  public IObjectWithSite,
                  public IPersistStream,
                  public IContextMenu
{
protected:
   DWORD m_ObjRefCount;

public:
   CDeskBand();
   ~CDeskBand();

   //IUnknown methods
   STDMETHODIMP QueryInterface(REFIID, LPVOID*);
   STDMETHODIMP_(DWORD) AddRef();
   STDMETHODIMP_(DWORD) Release();

   //IOleWindow methods
   STDMETHOD (GetWindow) (HWND*);
   STDMETHOD (ContextSensitiveHelp) (BOOL);

   //IDockingWindow methods
   STDMETHOD (ShowDW) (BOOL fShow);
   STDMETHOD (CloseDW) (DWORD dwReserved);
   STDMETHOD (ResizeBorderDW) (LPCRECT prcBorder, IUnknown* punkToolbarSite, BOOL fReserved);

   //IDeskBand methods
   STDMETHOD (GetBandInfo) (DWORD, DWORD, DESKBANDINFO*);

   //IInputObject methods
   STDMETHOD (UIActivateIO) (BOOL, LPMSG);
   STDMETHOD (HasFocusIO) (void);
   STDMETHOD (TranslateAcceleratorIO) (LPMSG);

   //IObjectWithSite methods
   STDMETHOD (SetSite) (IUnknown*);
   STDMETHOD (GetSite) (REFIID, LPVOID*);

   //IPersistStream methods
   STDMETHOD (GetClassID) (LPCLSID);
   STDMETHOD (IsDirty) (void);
   STDMETHOD (Load) (LPSTREAM);
   STDMETHOD (Save) (LPSTREAM, BOOL);
   STDMETHOD (GetSizeMax) (ULARGE_INTEGER*);

   //IContextMenu methods
   STDMETHOD (QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT);
   STDMETHOD (InvokeCommand)(LPCMINVOKECOMMANDINFO);
   STDMETHOD (GetCommandString)(UINT, UINT, LPUINT, LPSTR, UINT);

private:
BOOL m_bFocus;
HWND m_hwndParent;
HWND m_hWnd;
DWORD m_dwViewMode;
   DWORD m_dwBandID;
   IInputObjectSite *m_pSite;

private:
void FocusChange(BOOL);
   LRESULT OnKillFocus(void);
LRESULT OnSetFocus(void);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
LRESULT OnPaint(void);
LRESULT OnCommand(WPARAM wParam, LPARAM lParam);
   BOOL RegisterAndCreateWindow(void);
};

#endif   //DESKBAND_H


EXPLRBAR.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ExplorerBar.cpp

   Description:   Implements CExplorerBar

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "ExplrBar.h"
#include "Guid.h"

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

   CExplorerBar::CExplorerBar()

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

CExplorerBar::CExplorerBar()
{
m_pSite = NULL;

m_hWnd = NULL;
m_hwndParent = NULL;

m_bFocus = FALSE;

m_dwViewMode = 0;
m_dwBandID = 0;

m_ObjRefCount = 1;
g_DllRefCount++;
}

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

   CExplorerBar::~CExplorerBar()

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

CExplorerBar::~CExplorerBar()
{
//this should have been freed in a call to SetSite(NULL), but just to be safe
if(m_pSite)
   {
   m_pSite->Release();
   m_pSite = NULL;
   }

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

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

   CExplorerBar::QueryInterface

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

STDMETHODIMP CExplorerBar::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IOleWindow
else if(IsEqualIID(riid, IID_IOleWindow))
   {
   *ppReturn = (IOleWindow*)this;
   }

//IDockingWindow
else if(IsEqualIID(riid, IID_IDockingWindow))
   {
   *ppReturn = (IDockingWindow*)this;
   }

//IInputObject
else if(IsEqualIID(riid, IID_IInputObject))
   {
   *ppReturn = (IInputObject*)this;
   }

//IObjectWithSite
else if(IsEqualIID(riid, IID_IObjectWithSite))
   {
   *ppReturn = (IObjectWithSite*)this;
   }

//IDeskBand
else if(IsEqualIID(riid, IID_IDeskBand))
   {
   *ppReturn = (IDeskBand*)this;
   }

//IPersist
else if(IsEqualIID(riid, IID_IPersist))
   {
   *ppReturn = (IPersist*)this;
   }

//IPersistStream
else if(IsEqualIID(riid, IID_IPersistStream))
   {
   *ppReturn = (IPersistStream*)this;
   }

//IContextMenu
else if(IsEqualIID(riid, IID_IContextMenu))
   {
   *ppReturn = (IContextMenu*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CExplorerBar::AddRef

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

STDMETHODIMP_(DWORD) CExplorerBar::AddRef()
{
return ++m_ObjRefCount;
}

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

   CExplorerBar::Release

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

STDMETHODIMP_(DWORD) CExplorerBar::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IOleWindow Implementation
//

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

   CExplorerBar::GetWindow()

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

STDMETHODIMP CExplorerBar::GetWindow(HWND *phWnd)
{
*phWnd = m_hWnd;

return S_OK;
}

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

   CExplorerBar::ContextSensitiveHelp()

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

STDMETHODIMP CExplorerBar::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IDockingWindow Implementation
//

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

   CExplorerBar::ShowDW()

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

STDMETHODIMP CExplorerBar::ShowDW(BOOL fShow)
{
if(m_hWnd)
   {
   if(fShow)
      {
      //show our window
      ShowWindow(m_hWnd, SW_SHOW);
      }
   else
      {
      //hide our window
      ShowWindow(m_hWnd, SW_HIDE);
      }
   }

return S_OK;
}

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

   CExplorerBar::CloseDW()

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

STDMETHODIMP CExplorerBar::CloseDW(DWORD dwReserved)
{
ShowDW(FALSE);

if(IsWindow(m_hWnd))
   DestroyWindow(m_hWnd);

m_hWnd = NULL;

return S_OK;
}

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

   CExplorerBar::ResizeBorderDW()

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

STDMETHODIMP CExplorerBar::ResizeBorderDW(   LPCRECT prcBorder,
                                          IUnknown* punkSite,
                                          BOOL fReserved)
{
/*
This method is never called for Band Objects.
*/
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IInputObject Implementation
//

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

   CExplorerBar::UIActivateIO()

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

STDMETHODIMP CExplorerBar::UIActivateIO(BOOL fActivate, LPMSG pMsg)
{
if(fActivate)
   SetFocus(m_hWnd);

return S_OK;
}

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

   CExplorerBar::HasFocusIO()

   If this window or one of its decendants has the focus, return S_OK. Return
   S_FALSE if we don't have the focus.

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

STDMETHODIMP CExplorerBar::HasFocusIO(void)
{
if(m_bFocus)
   return S_OK;

return S_FALSE;
}

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

   CExplorerBar::TranslateAcceleratorIO()

   If the accelerator is translated, return S_OK or S_FALSE otherwise.

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

STDMETHODIMP CExplorerBar::TranslateAcceleratorIO(LPMSG pMsg)
{
return S_FALSE;
}

///////////////////////////////////////////////////////////////////////////
//
// IObjectWithSite implementations
//

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

   CExplorerBar::SetSite()

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

STDMETHODIMP CExplorerBar::SetSite(IUnknown* punkSite)
{
//If a site is being held, release it.
if(m_pSite)
   {
   m_pSite->Release();
   m_pSite = NULL;
   }

//If punkSite is not NULL, a new site is being set.
if(punkSite)
   {
   //Get the parent window.
   IOleWindow  *pOleWindow;

   m_hwndParent = NULL;

   if(SUCCEEDED(punkSite->QueryInterface(IID_IOleWindow, (LPVOID*)&pOleWindow)))
      {
      pOleWindow->GetWindow(&m_hwndParent);
      pOleWindow->Release();
      }

   if(!m_hwndParent)
      return E_FAIL;

   if(!RegisterAndCreateWindow())
      return E_FAIL;

   //Get and keep the IInputObjectSite pointer.
   if(SUCCEEDED(punkSite->QueryInterface(IID_IInputObjectSite, (LPVOID*)&m_pSite)))
      {
      return S_OK;
      }

   return E_FAIL;
   }

return S_OK;
}

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

   CExplorerBar::GetSite()

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

STDMETHODIMP CExplorerBar::GetSite(REFIID riid, LPVOID *ppvReturn)
{
*ppvReturn = NULL;

if(m_pSite)
   return m_pSite->QueryInterface(riid, ppvReturn);

return E_FAIL;
}

///////////////////////////////////////////////////////////////////////////
//
// IDeskBand implementation
//

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

   CExplorerBar::GetBandInfo()

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

STDMETHODIMP CExplorerBar::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi)
{
if(pdbi)
   {
   m_dwBandID = dwBandID;
   m_dwViewMode = dwViewMode;

   if(pdbi->dwMask & DBIM_MINSIZE)
      {
      pdbi->ptMinSize.x = MIN_SIZE_X;
      pdbi->ptMinSize.y = MIN_SIZE_Y;
      }

   if(pdbi->dwMask & DBIM_MAXSIZE)
      {
      pdbi->ptMaxSize.x = -1;
      pdbi->ptMaxSize.y = -1;
      }

   if(pdbi->dwMask & DBIM_INTEGRAL)
      {
      pdbi->ptIntegral.x = 1;
      pdbi->ptIntegral.y = 1;
      }

   if(pdbi->dwMask & DBIM_ACTUAL)
      {
      pdbi->ptActual.x = 0;
      pdbi->ptActual.y = 0;
      }

   if(pdbi->dwMask & DBIM_TITLE)
      {
      lstrcpyW(pdbi->wszTitle, L"Sample Explorer Bar");
      }

   if(pdbi->dwMask & DBIM_MODEFLAGS)
      {
      pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;
      }

   if(pdbi->dwMask & DBIM_BKCOLOR)
      {
      //Use the default background color by removing this flag.
      pdbi->dwMask &= ~DBIM_BKCOLOR;
      }

   return S_OK;
   }

return E_INVALIDARG;
}

///////////////////////////////////////////////////////////////////////////
//
// IPersistStream implementations
//
// This is only supported to allow the desk band to be dropped on the
// desktop and to prevent multiple instances of the desk band from showing
// up in the context menu. This desk band doesn't actually persist any data.
//

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

   CExplorerBar::GetClassID()

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

STDMETHODIMP CExplorerBar::GetClassID(LPCLSID pClassID)
{
*pClassID = CLSID_SampleExplorerBar;

return S_OK;
}

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

   CExplorerBar::IsDirty()

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

STDMETHODIMP CExplorerBar::IsDirty(void)
{
return S_FALSE;
}

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

   CExplorerBar::Load()

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

STDMETHODIMP CExplorerBar::Load(LPSTREAM pStream)
{
return S_OK;
}

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

   CExplorerBar::Save()

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

STDMETHODIMP CExplorerBar::Save(LPSTREAM pStream, BOOL fClearDirty)
{
return S_OK;
}

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

   CExplorerBar::GetSizeMax()

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

STDMETHODIMP CExplorerBar::GetSizeMax(ULARGE_INTEGER *pul)
{
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IContextMenu Implementation
//

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

   CExplorerBar::QueryContextMenu()

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

STDMETHODIMP CExplorerBar::QueryContextMenu( HMENU hMenu,
                                             UINT indexMenu,
                                             UINT idCmdFirst,
                                             UINT idCmdLast,
                                             UINT uFlags)
{
if(!(CMF_DEFAULTONLY & uFlags))
   {
   InsertMenu( hMenu,
               indexMenu,
               MF_STRING | MF_BYPOSITION,
               idCmdFirst + IDM_COMMAND,
               "&Sample Explorer Bar Command");

   return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_COMMAND + 1));
   }

return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}

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

   CExplorerBar::InvokeCommand()

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

STDMETHODIMP CExplorerBar::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
switch (LOWORD(lpcmi->lpVerb))
   {
   case IDM_COMMAND:
      MessageBox(lpcmi->hwnd, TEXT("Explorer Bar Command selected."), TEXT("Sample Explorer Bar"), MB_OK | MB_ICONINFORMATION);
      break;

   default:
      return E_INVALIDARG;
   }

return NOERROR;
}

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

   CExplorerBar::GetCommandString()

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

STDMETHODIMP CExplorerBar::GetCommandString( UINT idCommand,
                                             UINT uFlags,
                                             LPUINT lpReserved,
                                             LPSTR lpszName,
                                             UINT uMaxNameLen)
{
HRESULT  hr = E_INVALIDARG;

switch(uFlags)
   {
   case GCS_HELPTEXT:
      switch(idCommand)
         {
         case IDM_COMMAND:
            lstrcpy(lpszName, TEXT("Explorer Bar command help text"));
            hr = NOERROR;
            break;
         }
      break;

   case GCS_VERB:
      switch(idCommand)
         {
         case IDM_COMMAND:
            lstrcpy(lpszName, TEXT("command"));
            hr = NOERROR;
            break;
         }
      break;

   case GCS_VALIDATE:
      hr = NOERROR;
      break;
   }

return hr;
}

///////////////////////////////////////////////////////////////////////////
//
// private method implementations
//

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

   CExplorerBar::WndProc()

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

LRESULT CALLBACK CExplorerBar::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
CExplorerBar  *pThis = (CExplorerBar*)GetWindowLong(hWnd, GWL_USERDATA);

switch (uMessage)
   {
   case WM_NCCREATE:
      {
      LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
      pThis = (CExplorerBar*)(lpcs->lpCreateParams);
      SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);

      //set the window handle
      pThis->m_hWnd = hWnd;
      }
      break;

   case WM_PAINT:
      return pThis->OnPaint();

   case WM_COMMAND:
      return pThis->OnCommand(wParam, lParam);

   case WM_SETFOCUS:
      return pThis->OnSetFocus();

   case WM_KILLFOCUS:
      return pThis->OnKillFocus();
   }

return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

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

   CExplorerBar::OnPaint()

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

LRESULT CExplorerBar::OnPaint(void)
{
PAINTSTRUCT ps;
RECT        rc;

BeginPaint(m_hWnd, &ps);

GetClientRect(m_hWnd, &rc);
SetTextColor(ps.hdc, RGB(255, 255, 255));
SetBkMode(ps.hdc, TRANSPARENT);
DrawText(ps.hdc, TEXT("Sample Explorer Bar"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

EndPaint(m_hWnd, &ps);

return 0;
}

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

   CExplorerBar::OnCommand()

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

LRESULT CExplorerBar::OnCommand(WPARAM wParam, LPARAM lParam)
{
return 0;
}

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

   CExplorerBar::FocusChange()

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

void CExplorerBar::FocusChange(BOOL bFocus)
{
m_bFocus = bFocus;

//inform the input object site that the focus has changed
if(m_pSite)
   {
   m_pSite->OnFocusChangeIS((IDockingWindow*)this, bFocus);
   }
}

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

   CExplorerBar::OnSetFocus()

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

LRESULT CExplorerBar::OnSetFocus(void)
{
FocusChange(TRUE);

return 0;
}

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

   CExplorerBar::OnKillFocus()

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

LRESULT CExplorerBar::OnKillFocus(void)
{
FocusChange(FALSE);

return 0;
}

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

   CExplorerBar::RegisterAndCreateWindow()

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

BOOL CExplorerBar::RegisterAndCreateWindow(void)
{
//If the window doesn't exist yet, create it now.
if(!m_hWnd)
   {
   //Can't create a child window without a parent.
   if(!m_hwndParent)
      {
      return FALSE;
      }

   //If the window class has not been registered, then do so.
   WNDCLASS wc;
   if(!GetClassInfo(g_hInst, EB_CLASS_NAME, &wc))
      {
      ZeroMemory(&wc, sizeof(wc));
      wc.style          = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
      wc.lpfnWndProc    = (WNDPROC)WndProc;
      wc.cbClsExtra     = 0;
      wc.cbWndExtra     = 0;
      wc.hInstance      = g_hInst;
      wc.hIcon          = NULL;
      wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground  = (HBRUSH)CreateSolidBrush(RGB(0, 0, 192));
      wc.lpszMenuName   = NULL;
      wc.lpszClassName  = EB_CLASS_NAME;

      if(!RegisterClass(&wc))
         {
         //If RegisterClass fails, CreateWindow below will fail.
         }
      }

   RECT  rc;

   GetClientRect(m_hwndParent, &rc);

   //Create the window. The WndProc will set m_hWnd.
   CreateWindowEx(   0,
                     EB_CLASS_NAME,
                     NULL,
                     WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER,
                     rc.left,
                     rc.top,
                     rc.right - rc.left,
                     rc.bottom - rc.top,
                     m_hwndParent,
                     NULL,
                     g_hInst,
                     (LPVOID)this);

   }

return (NULL != m_hWnd);
}


EXPLRBAR.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ExplorerBar.h

   Description:   CExplorerBar definitions.

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

#include <windows.h>
#include <shlobj.h>

#include "Globals.h"

#ifndef EXPLORERBAR_H
#define EXPLORERBAR_H

#define EB_CLASS_NAME (TEXT("ExplorerBarSampleClass"))

#define MIN_SIZE_X   10
#define MIN_SIZE_Y   10

#define IDM_COMMAND  0

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

   CExplorerBar class definition

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

class CExplorerBar : public IDeskBand,
                     public IInputObject,
                     public IObjectWithSite,
                     public IPersistStream,
                     public IContextMenu
{
protected:
   DWORD m_ObjRefCount;

public:
   CExplorerBar();
   ~CExplorerBar();

   //IUnknown methods
   STDMETHODIMP QueryInterface(REFIID, LPVOID*);
   STDMETHODIMP_(DWORD) AddRef();
   STDMETHODIMP_(DWORD) Release();

   //IOleWindow methods
   STDMETHOD (GetWindow) (HWND*);
   STDMETHOD (ContextSensitiveHelp) (BOOL);

   //IDockingWindow methods
   STDMETHOD (ShowDW) (BOOL fShow);
   STDMETHOD (CloseDW) (DWORD dwReserved);
   STDMETHOD (ResizeBorderDW) (LPCRECT prcBorder, IUnknown* punkToolbarSite, BOOL fReserved);

   //IDeskBand methods
   STDMETHOD (GetBandInfo) (DWORD, DWORD, DESKBANDINFO*);

   //IInputObject methods
   STDMETHOD (UIActivateIO) (BOOL, LPMSG);
   STDMETHOD (HasFocusIO) (void);
   STDMETHOD (TranslateAcceleratorIO) (LPMSG);

   //IObjectWithSite methods
   STDMETHOD (SetSite) (IUnknown*);
   STDMETHOD (GetSite) (REFIID, LPVOID*);

   //IPersistStream methods
   STDMETHOD (GetClassID) (LPCLSID);
   STDMETHOD (IsDirty) (void);
   STDMETHOD (Load) (LPSTREAM);
   STDMETHOD (Save) (LPSTREAM, BOOL);
   STDMETHOD (GetSizeMax) (ULARGE_INTEGER*);

   //IContextMenu methods
   STDMETHOD (QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT);
   STDMETHOD (InvokeCommand)(LPCMINVOKECOMMANDINFO);
   STDMETHOD (GetCommandString)(UINT, UINT, LPUINT, LPSTR, UINT);

private:
BOOL m_bFocus;
HWND m_hwndParent;
HWND m_hWnd;
DWORD m_dwViewMode;
   DWORD m_dwBandID;
   IInputObjectSite *m_pSite;

private:
void FocusChange(BOOL);
   LRESULT OnKillFocus(void);
LRESULT OnSetFocus(void);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
LRESULT OnPaint(void);
LRESULT OnCommand(WPARAM wParam, LPARAM lParam);
   BOOL RegisterAndCreateWindow(void);
};

#endif   //EXPLORERBAR_H


GLOBALS.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          Globals.h

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

/**************************************************************************
   global variables
**************************************************************************/

extern HINSTANCE  g_hInst;
extern UINT       g_DllRefCount;

#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))


GUID.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          Guid.h

   Description:   Private GUID definition.

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

// {85E0B170-04FA-11d1-B7DA-00A0C90348D6}
DEFINE_GUID(CLSID_SampleDeskBand,
0x85e0b170, 0x4fa, 0x11d1, 0xb7, 0xda, 0x0, 0xa0, 0xc9, 0x3, 0x48, 0xd6);

// {85E0B171-04FA-11d1-B7DA-00A0C90348D6}
DEFINE_GUID(CLSID_SampleExplorerBar,
0x85e0b171, 0x4fa, 0x11d1, 0xb7, 0xda, 0x0, 0xa0, 0xc9, 0x3, 0x48, 0xd6);

// {85E0B172-04FA-11d1-B7DA-00A0C90348D6}
DEFINE_GUID(CLSID_SampleCommBand,
0x85e0b172, 0x4fa, 0x11d1, 0xb7, 0xda, 0x0, 0xa0, 0xc9, 0x3, 0x48, 0xd6);


README.TXT

Cabview Sample
==============

The CABVIEW samples implements a namespace extension that is presented in
the Explorer's namespace.  It also parses and extracts component files
from CAB files.  The library for working with CAB files is provided for
x86 only.

After the sample DLL has been built, to install the sample:
1. Right Click on the CABVIEW.INF file and select Install from the menu

To run the sample
1. Through the explorer browse to a directory containing a .CAB file such
   as the Windows 95 release CD.
2. You will observe that the icon shown by Explorer for this cab file is
   CABVIEW.ICO, if your Cabview extension has been installed correctly.
3. Double click on the CAB file and a new Explorer window will be launched.
4. This will show the contents of the CAB file.
5. You can extract a file from the window by double clicking on the file icon.


CABITMS.H

//*******************************************************************************************
//
// Filename : CabItms.h
//
//Definitions of CCabItems and CCabExtract
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _CABITMS_H_
#define _CABITMS_H_

#include "fdi.h"

class CCabItems
{
public:
typedef void (CALLBACK *PFNCABITEM)(LPCSTR pszFile, DWORD dwSize, UINT date,
UINT time, UINT attribs, LPARAM lParam);

CCabItems(LPSTR szCabFile) {lstrcpyn(m_szCabFile, szCabFile, sizeof(m_szCabFile));}
~CCabItems() {}

BOOL EnumItems(PFNCABITEM pfnCallBack, LPARAM lParam);

private:
char m_szCabFile[MAX_PATH];
} ;

class CCabExtract
{
public:
#define DIR_MEM ((LPCSTR)1)

#define EXTRACT_FALSE ((HGLOBAL *)0)
#define EXTRACT_TRUE ((HGLOBAL *)1)

typedef HGLOBAL * (CALLBACK *PFNCABEXTRACT)(LPCSTR pszFile, DWORD dwSize, UINT date,
UINT time, UINT attribs, LPARAM lParam);

CCabExtract(LPSTR szCabFile) {lstrcpyn(m_szCabFile, szCabFile, sizeof(m_szCabFile));}
~CCabExtract() {}

BOOL ExtractItems(HWND hwndOwner, LPCSTR szDir, PFNCABEXTRACT pfnCallBack, LPARAM lParam);

private:
char m_szCabFile[MAX_PATH];
} ;

#endif // _CABITMS_H_


CABOBJ.H

//*******************************************************************************************
//
// Filename : CabObj.h
//
// Shell interfaces IPersistFolder, IShellBrowser, IShellView ICommdlgBrowser
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _CABOBJ_H_
#define _CABOBJ_H_
#include <shlobj.h>
void WINAPI SHFree(LPVOID pv);

DEFINE_SHLGUID(IID_IPersistFolder,   0x000214EAL, 0, 0);
DEFINE_SHLGUID(IID_IShellBrowser,   0x000214E2L, 0, 0);
DEFINE_SHLGUID(IID_IShellView,      0x000214E3L, 0, 0);

#define _IOffset(class, itf)         ((UINT)&(((class *)0)->itf))
#define IToClass(class, itf, pitf)   ((class  *)(((LPSTR)pitf)-_IOffset(class, itf)))

#define STRRET_OLESTR   0x0000

#endif // _CABOBJ_H_


CABP.H

//*******************************************************************************************
//
// Filename : CabP.h
//
// Some useful macros and routines
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _CABP_H_
#define _CABP_H_

#ifndef RC_INVOKED
#pragma pack(1)         /* Assume byte packing throughout */
#endif /* !RC_INVOKED */

#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif  /* __cplusplus */

//
// Private QueryContextMenuFlag passed from DefView
//
#define CMF_DVFILE       0x00010000     // "File" pulldown

#define ResultFromShort(i)  ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(i)))
#define ShortFromResult(r)  (short)SCODE_CODE(GetScode(r))

//
// OLE string
//
int OleStrToStrN(LPTSTR, int, LPCOLESTR, int);
int StrToOleStrN(LPOLESTR, int, LPCTSTR, int);
int OleStrToStr(LPTSTR, LPCOLESTR);
int StrToOleStr(LPOLESTR, LPCTSTR);

#ifdef __cplusplus
}
#endif  /* __cplusplus */

#ifndef RC_INVOKED
#pragma pack()
#endif  /* !RC_INVOKED */

#endif // _CABP_H_


CABVW2.H

//*******************************************************************************************
//
// Filename : Cabvw2.h
//
//Cab_MergeMenu
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _ISHELL2_H_
#define _ISHELL2_H_

#ifdef __cplusplus
extern "C" {
#endif
//===========================================================================
// ITEMIDLIST
//===========================================================================

// unsafe macros
#define _ILSkip(pidl, cb)       ((LPITEMIDLIST)(((BYTE*)(pidl))+cb))
#define _ILNext(pidl)           _ILSkip(pidl, (pidl)->mkid.cb)
#ifdef DEBUG
// Dugging aids for making sure we dont use free pidls
#define VALIDATE_PIDL(pidl) Assert((pidl)->mkid.cb != 0xC5C5)
#else
#define VALIDATE_PIDL(pidl)
#endif

void        ILFree(LPITEMIDLIST pidl);
LPITEMIDLIST ILClone(LPCITEMIDLIST pidl);
UINT ILGetSize(LPCITEMIDLIST pidl);

typedef void (WINAPI FAR* RUNDLLPROC)(HWND hwndStub,
        HINSTANCE hAppInstance,
        LPSTR lpszCmdLine, int nCmdShow);

UINT  Cab_MergeMenus(HMENU hmDst, HMENU hmSrc, UINT uInsert, UINT uIDAdjust, UINT uIDAdjustMax, ULONG uFlags);

//===================================================================
// Cab_MergeMenu parameter
//
#define MM_ADDSEPARATOR0x00000001L
#define MM_SUBMENUSHAVEIDS0x00000002L

#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))

#ifdef __cplusplus
}
#endif

#endif // _ISHELL2_H_


DA.C

// Dynamic Array APIs
//*******************************************************************************************
//
// Filename : Da.c
//
//Implementation of DPAs
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"
#include "dpda.h"
#include <assert.h>

#ifdef DEBUG
#define DPA_MAGIC   ('P' | ('A' << 256))
#define IsDPA(pdpa) ((pdpa) && (pdpa)->magic == DPA_MAGIC)
#else
#define IsDPA(pdsa)
#endif

#define ControlAlloc(hheap, cb)       HeapAlloc((hheap), HEAP_ZERO_MEMORY, (cb))
#define ControlReAlloc(hheap, pb, cb) HeapReAlloc((hheap), HEAP_ZERO_MEMORY, (pb),(cb))
#define ControlFree(hheap, pb)        HeapFree((hheap), 0, (pb))
#define ControlSize(hheap, pb)        HeapSize((hheap), 0, (LPCVOID)(pb))

typedef struct {
    void **pp;
    PFNDPACOMPARE pfnCmp;
    LPARAM lParam;
    int cp;
    void **ppT;
} SORTPARAMS;

BOOL DPA_MergeSort(SORTPARAMS * psp);
void DPA_MergeSort2(SORTPARAMS * psp, int iFirst, int cItems);

//================== Dynamic pointer array implementation ===========

typedef struct _DPA {
// NOTE: The following two fields MUST be defined in this order, at
// the beginning of the structure in order for the macro APIs to work.
//
    int cp;
    void **pp;

    HANDLE hheap;        // Heap to allocate from if NULL use shared

    int cpAlloc;
    int cpGrow;
#ifdef DEBUG
  UINT magic;
#endif
} DPA;

HANDLE g_hSharedHeap = NULL;

void * Alloc(long cb)
{
    // I will assume that this is the only one that needs the checks to
    // see if the heap has been previously created or not

    if (g_hSharedHeap == NULL)
    {
        g_hSharedHeap = HeapCreate(0, 1, 0);

        // If still NULL we have problems!
        if (g_hSharedHeap == NULL)
            return(NULL);
    }

    return HeapAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, cb);
}

void * ReAlloc(void * pb, long cb)
{
    if (pb == NULL)
        return Alloc(cb);
    return HeapReAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, pb, cb);
}

BOOL Free(void * pb)
{
    return HeapFree(g_hSharedHeap, 0, pb);
}

DWORD GetSize(void * pb)
{
    return HeapSize(g_hSharedHeap, 0, pb);
}

HDPA DPA_Create(int cpGrow)
{
    HDPA pdpa = Alloc(sizeof(DPA));
    if (pdpa)
    {
        pdpa->cp = 0;
        pdpa->cpAlloc = 0;
        pdpa->cpGrow = (cpGrow < 8 ? 8 : cpGrow);
        pdpa->pp = NULL;
        pdpa->hheap = g_hSharedHeap;   // Defaults to use shared one (for now...)
#ifdef DEBUG
        pdpa->magic = DPA_MAGIC;
#endif
    }
    return pdpa;
}

// Should nuke the standard DPA above...
HDPA DPA_CreateEx(int cpGrow, HANDLE hheap)
{
    HDPA pdpa;
    if (hheap == NULL)
    {
        pdpa = Alloc(sizeof(DPA));
        hheap = g_hSharedHeap;
    }
    else
        pdpa = ControlAlloc(hheap, sizeof(DPA));
    if (pdpa)
    {
        pdpa->cp = 0;
        pdpa->cpAlloc = 0;
        pdpa->cpGrow = (cpGrow < 8 ? 8 : cpGrow);
        pdpa->pp = NULL;
        pdpa->hheap = hheap;
#ifdef DEBUG
        pdpa->magic = DPA_MAGIC;
#endif
    }
    return pdpa;
}

BOOL DPA_Destroy(HDPA pdpa)
{
    //assert(IsDPA(pdpa));

    if (pdpa == NULL)       // allow NULL for low memory cases, still assert
        return TRUE;

    assert (pdpa->hheap);

#ifdef DEBUG
    pdpa->cp = 0;
    pdpa->cpAlloc = 0;
    pdpa->magic = 0;
#endif
    if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp))
        return FALSE;

    return ControlFree(pdpa->hheap, pdpa);
}

HDPA DPA_Clone(HDPA pdpa, HDPA pdpaNew)
{
    BOOL fAlloc = FALSE;

    if (!pdpaNew)
    {
        pdpaNew = DPA_CreateEx(pdpa->cpGrow, pdpa->hheap);
        if (!pdpaNew)
            return NULL;

        fAlloc = TRUE;
    }

    if (!DPA_Grow(pdpaNew, pdpa->cpAlloc)) {
if (!fAlloc)
    DPA_Destroy(pdpaNew);
return NULL;
    }

    pdpaNew->cp = pdpa->cp;
    hmemcpy(pdpaNew->pp, pdpa->pp, pdpa->cp * sizeof(void *));

    return pdpaNew;
}

void * DPA_GetPtr(HDPA pdpa, int index)
{
//    assert(IsDPA(pdpa));

    if (index < 0 || index >= pdpa->cp)
        return NULL;

    return pdpa->pp[index];
}

int DPA_GetPtrIndex(HDPA pdpa, void * p)
{
    void **pp;
    void **ppMax;

    if (pdpa->pp)
    {
        pp = pdpa->pp;
        ppMax = pp + pdpa->cp;
        for ( ; pp < ppMax; pp++)
        {
            if (*pp == p)
                return (pp - pdpa->pp);
        }
    }
    return -1;
}

BOOL DPA_Grow(HDPA pdpa, int cpAlloc)
{
    if (cpAlloc > pdpa->cpAlloc)
    {
        void **ppNew;

        cpAlloc = ((cpAlloc + pdpa->cpGrow - 1) / pdpa->cpGrow) * pdpa->cpGrow;

        if (pdpa->pp)
            ppNew = (void * *)ControlReAlloc(pdpa->hheap, pdpa->pp, cpAlloc * sizeof(void *));
        else
            ppNew = (void * *)ControlAlloc(pdpa->hheap, cpAlloc * sizeof(void *));
        if (!ppNew)
            return FALSE;

        pdpa->pp = ppNew;
        pdpa->cpAlloc = cpAlloc;
    }
    return TRUE;
}

BOOL DPA_SetPtr(HDPA pdpa, int index, void * p)
{
    if (index < 0)
    {
        // DebugMsg(DM_ERROR, "DPA: Invalid index: %d", index);
        return FALSE;
    }

    if (index >= pdpa->cp)
    {
        if (!DPA_Grow(pdpa, index + 1))
            return FALSE;
        pdpa->cp = index + 1;
    }

    pdpa->pp[index] = p;

    return TRUE;
}

int DPA_InsertPtr(HDPA pdpa, int index, void * p)
{
    if (index < 0)
    {
        return -1;
    }
    if (index > pdpa->cp)
        index = pdpa->cp;

    // Make sure we have room for one more item
    //
    if (pdpa->cp + 1 > pdpa->cpAlloc)
    {
        if (!DPA_Grow(pdpa, pdpa->cp + 1))
            return -1;
    }

    // If we are inserting, we need to slide everybody up
    //
    if (index < pdpa->cp)
    {
        hmemcpy(&pdpa->pp[index + 1], &pdpa->pp[index],
            (pdpa->cp - index) * sizeof(void *));
    }

    pdpa->pp[index] = p;
    pdpa->cp++;

    return index;
}

void * DPA_DeletePtr(HDPA pdpa, int index)
{
    void * p;

//    assert(IsDPA(pdpa));

    if (index < 0 || index >= pdpa->cp)
    {
     //   DebugMsg(DM_ERROR, "DPA: Invalid index: %d", index);
        return NULL;
    }

    p = pdpa->pp[index];

    if (index < pdpa->cp - 1)
    {
        hmemcpy(&pdpa->pp[index], &pdpa->pp[index + 1],
            (pdpa->cp - (index + 1)) * sizeof(void *));
    }
    pdpa->cp--;

    if (pdpa->cpAlloc - pdpa->cp > pdpa->cpGrow)
    {
        void **ppNew;
        ppNew = ControlReAlloc(pdpa->hheap, pdpa->pp, (pdpa->cpAlloc - pdpa->cpGrow) * sizeof(void *));

        assert(ppNew);
        pdpa->pp = ppNew;
        pdpa->cpAlloc -= pdpa->cpGrow;
    }
    return p;
}

BOOL DPA_DeleteAllPtrs(HDPA pdpa)
{
    if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp))
        return FALSE;
    pdpa->pp = NULL;
    pdpa->cp = pdpa->cpAlloc = 0;
    return TRUE;
}

BOOL DPA_Sort(HDPA pdpa, PFNDPACOMPARE pfnCmp, LPARAM lParam)
{
    SORTPARAMS sp;

    sp.cp = pdpa->cp;
    sp.pp = pdpa->pp;
    sp.pfnCmp = pfnCmp;
    sp.lParam = lParam;

    return DPA_MergeSort(&sp);
}

#define SortCompare(psp, pp1, i1, pp2, i2) \
(psp->pfnCmp(pp1[i1], pp2[i2], psp->lParam))

//
//  This function merges two sorted lists and makes one sorted list.
//   psp->pp[iFirst, iFirst+cItes/2-1], psp->pp[iFirst+cItems/2, iFirst+cItems-1]
//
void DPA_MergeThem(SORTPARAMS * psp, int iFirst, int cItems)
{
    //
    // Notes:
    //  This function is separated from DPA_MergeSort2() to avoid comsuming
    // stack variables. Never inline this.
    //
    int cHalf = cItems/2;
    int iIn1, iIn2, iOut;
    LPVOID * ppvSrc = &psp->pp[iFirst];

    // Copy the first part to temp storage so we can write directly into
    // the final buffer.  Note that this takes at most psp->cp/2 DWORD's
    hmemcpy(psp->ppT, ppvSrc, cHalf*sizeof(LPVOID));

    for (iIn1=0, iIn2=cHalf, iOut=0;;)
    {
if (SortCompare(psp, psp->ppT, iIn1, ppvSrc, iIn2) <= 0) {
    ppvSrc[iOut++] = psp->ppT[iIn1++];

    if (iIn1==cHalf) {
// We used up the first half; the rest of the second half
// should already be in place
break;
    }
} else {
    ppvSrc[iOut++] = ppvSrc[iIn2++];
    if (iIn2==cItems) {
// We used up the second half; copy the rest of the first half
// into place
hmemcpy(&ppvSrc[iOut], &psp->ppT[iIn1], (cItems-iOut)*sizeof(LPVOID));
break;
    }
}
    }
}

//
//  This function sorts a give list (psp->pp[iFirst,iFirst-cItems-1]).
//
void DPA_MergeSort2(SORTPARAMS * psp, int iFirst, int cItems)
{
    //
    // Notes:
    //   This function is recursively called. Therefore, we should minimize
    //  the number of local variables and parameters. At this point, we
    //  use one local variable and three parameters.
    //
    int cHalf;

    switch(cItems)
    {
    case 1:
return;

    case 2:
// Swap them, if they are out of order.
if (SortCompare(psp, psp->pp, iFirst, psp->pp, iFirst+1) > 0)
{
    psp->ppT[0] = psp->pp[iFirst];
    psp->pp[iFirst] = psp->pp[iFirst+1];
    psp->pp[iFirst+1] = psp->ppT[0];
}
break;

    default:
    cHalf = cItems/2;
// Sort each half
DPA_MergeSort2(psp, iFirst, cHalf);
DPA_MergeSort2(psp, iFirst+cHalf, cItems-cHalf);
// Then, merge them.
DPA_MergeThem(psp, iFirst, cItems);
break;
    }
}

BOOL DPA_MergeSort(SORTPARAMS * psp)
{
    if (psp->cp==0)
return TRUE;

    // Note that we divide by 2 below; we want to round down
    psp->ppT = LocalAlloc(LPTR, psp->cp/2 * sizeof(LPVOID));
    if (!psp->ppT)
return FALSE;

    DPA_MergeSort2(psp, 0, psp->cp);
    LocalFree(psp->ppT);
    return TRUE;
}

// Search function
//
int DPA_Search(HDPA pdpa, void * pFind, int iStart,
            PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT options)
{
    int cp = DPA_GetPtrCount(pdpa);

    assert(pfnCompare);
    assert(0 <= iStart);

    // Only allow these wierd flags if the list is sorted
    assert((options & DPAS_SORTED) || !(options & (DPAS_INSERTBEFORE | DPAS_INSERTAFTER)));

    if (!(options & DPAS_SORTED))
    {
        // Not sorted: do lisearch.
        int i;

        for (i = iStart; i < cp; i++)
        {
            if (0 == pfnCompare(pFind, DPA_FastGetPtr(pdpa, i), lParam))
                return i;
        }
        return -1;
    }
    else
    {
        // Search the array using binary search.  If several adjacent
        // elements match the target element, the index of the first
        // matching element is returned.

        int iRet = -1;      // assume no match
        BOOL bFound = FALSE;
        int nCmp = 0;
        int iLow = 0;       // Don't bother using iStart for binary search
        int iMid = 0;
        int iHigh = cp - 1;

        // (OK for cp == 0)
        while (iLow <= iHigh)
        {
            iMid = (iLow + iHigh) / 2;

            nCmp = pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid), lParam);

            if (0 > nCmp)
                iHigh = iMid - 1;       // First is smaller
            else if (0 < nCmp)
                iLow = iMid + 1;        // First is larger
            else
            {
                // Match; search back for first match
                bFound = TRUE;
                while (0 < iMid)
                {
                    if (0 != pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid-1), lParam))
                        break;
                    else
                        iMid--;
                }
                break;
            }
        }

        if (bFound)
        {
            assert(0 <= iMid);
            iRet = iMid;
        }

        // Did the search fail AND
        // is one of the strange search flags set?
        if (!bFound && (options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)))
        {
            // Yes; return the index where the target should be inserted
            // if not found
            if (0 < nCmp)       // First is larger
                iRet = iLow;
            else
                iRet = iMid;
            // (We don't distinguish between the two flags anymore)
        }
        else if ( !(options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)) )
        {
            // Sanity check with lisearch
            assert(DPA_Search(pdpa, pFind, iStart, pfnCompare, lParam, options & ~DPAS_SORTED) == iRet);
        }
        return iRet;
    }
}

//===========================================================================
//
// String ptr management routines
//
// Copy as much of *psz to *pszBuf as will fit
//
int Str_GetPtr(LPCSTR psz, LPSTR pszBuf, int cchBuf)
{
    int cch = 0;

    // if pszBuf is NULL, just return length of string.
    //
    if (!pszBuf && psz)
        return lstrlen(psz);

    if (cchBuf)
    {
        if (psz)
        {
            cch = lstrlen(psz);

            if (cch > cchBuf - 1)
                cch = cchBuf - 1;

            hmemcpy(pszBuf, psz, cch);
        }
        pszBuf[cch] = 0;
    }
    return cch;
}

BOOL Str_Set(LPSTR *ppsz, LPCSTR psz)
{
    if (!psz)
    {
        if (*ppsz)
        {
            LocalFree(*ppsz);
            *ppsz = NULL;
        }
    }
    else
    {
LPSTR pszNew;
        UINT cbSize = lstrlen(psz) + 1;
if (*ppsz)
    pszNew = LocalReAlloc(*ppsz, cbSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
else
    pszNew = LocalAlloc(LPTR, cbSize);

        if (!pszNew)
            return FALSE;

        lstrcpy(pszNew, psz);
        *ppsz = pszNew;
    }
    return TRUE;
}

// Set *ppsz to a copy of psz, reallocing as needed
//
BOOL Str_SetPtr(LPSTR * ppsz, LPCSTR psz)
{
    if (!psz)
    {
        if (*ppsz)
        {
            Free(*ppsz);
            *ppsz = NULL;
        }
    }
    else
    {
        LPSTR pszNew = (LPSTR)ReAlloc(*ppsz, lstrlen(psz) + 1);
        if (!pszNew)
            return FALSE;

        lstrcpy(pszNew, psz);
        *ppsz = pszNew;
    }
    return TRUE;
}


DATAOBJ.CPP

//*******************************************************************************************
//
// Filename : DataObj.cpp
//
//Implementation file for CObjFormats and CCabObj
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "thisdll.h"

#include "folder.h"
#include "dataobj.h"

#include "cabitms.h"

UINT CCabObj::s_uFileGroupDesc = 0;
UINT CCabObj::s_uFileContents = 0;

class CObjFormats : public IEnumFORMATETC
{
public:
CObjFormats(UINT cfmt, const FORMATETC afmt[]);
~CObjFormats();

    // *** IUnknown methods ***
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // *** IEnumFORMATETC methods ***
    STDMETHODIMP Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFethed);
    STDMETHODIMP Skip(ULONG celt);
    STDMETHODIMP Reset();
    STDMETHODIMP Clone(IEnumFORMATETC ** ppenum);

private:
CRefCount m_cRef;

CRefDll m_cRefDll;

UINT m_iFmt;
UINT m_cFmt;
FORMATETC *m_aFmt;
} ;

CObjFormats::CObjFormats(UINT cfmt, const FORMATETC afmt[])
{
m_iFmt = 0;
m_cFmt = cfmt;
m_aFmt = new FORMATETC[cfmt];

if (m_aFmt)
{
CopyMemory(m_aFmt, afmt, cfmt*sizeof(afmt[0]));
}
}

CObjFormats::~CObjFormats()
{
if (m_aFmt)
{
delete m_aFmt;
}
}

// *** IUnknown methods ***
STDMETHODIMP CObjFormats::QueryInterface(
   REFIID riid,
   LPVOID FAR* ppvObj)
{
*ppvObj = NULL;

if (!m_aFmt)
{
return(E_OUTOFMEMORY);
}

LPUNKNOWN pObj;

if (riid == IID_IUnknown)
{
pObj = (IUnknown*)((IEnumFORMATETC*)this);
}
else if (riid == IID_IEnumFORMATETC)
{
pObj = (IUnknown*)((IEnumFORMATETC*)this);
}
else
{
   return(E_NOINTERFACE);
}

pObj->AddRef();
*ppvObj = pObj;

return(NOERROR);
}

STDMETHODIMP_(ULONG) CObjFormats::AddRef(void)
{
return(m_cRef.AddRef());
}

STDMETHODIMP_(ULONG) CObjFormats::Release(void)
{
if (!m_cRef.Release())
{
   delete this;
return(0);
}

return(m_cRef.GetRef());
}

STDMETHODIMP CObjFormats::Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFethed)
{
UINT cfetch;
HRESULT hres = S_FALSE;// assume less numbers

if (m_iFmt < m_cFmt)
{
cfetch = m_cFmt - m_iFmt;
if (cfetch >= celt)
{
cfetch = celt;
hres = S_OK;
}

CopyMemory(rgelt, &m_aFmt[m_iFmt], cfetch * sizeof(FORMATETC));
m_iFmt += cfetch;
}
else
{
cfetch = 0;
}

if (pceltFethed)
{
*pceltFethed = cfetch;
}

return hres;
}

STDMETHODIMP CObjFormats::Skip(ULONG celt)
{
m_iFmt += celt;
if (m_iFmt > m_cFmt)
{
m_iFmt = m_cFmt;
return S_FALSE;
}
return S_OK;
}

STDMETHODIMP CObjFormats::Reset()
{
m_iFmt = 0;
return S_OK;
}

STDMETHODIMP CObjFormats::Clone(IEnumFORMATETC ** ppenum)
{
return(E_NOTIMPL);
}

CCabObj::CCabObj(HWND hwndOwner, CCabFolder *pcf, LPCABITEM *apit, UINT cpit)
: m_lSel(8), m_lContents(NULL)
{
m_pcfHere = pcf;
pcf->AddRef();

m_hwndOwner = hwndOwner;
m_lSel.AddItems(apit, cpit);
}

CCabObj::~CCabObj()
{
m_pcfHere->Release();
}

// *** IUnknown methods ***
STDMETHODIMP CCabObj::QueryInterface(
   REFIID riid,
   LPVOID FAR* ppvObj)
{
*ppvObj = NULL;

if (m_lSel.GetState() == CCabItemList::State_OutOfMem)
{
return(E_OUTOFMEMORY);
}

LPUNKNOWN pObj;

if (riid == IID_IUnknown)
{
pObj = (IUnknown*)((IDataObject*)this);
}
else if (riid == IID_IDataObject)
{
pObj = (IUnknown*)((IDataObject*)this);
}
else
{
   return(E_NOINTERFACE);
}

pObj->AddRef();
*ppvObj = pObj;

return(NOERROR);
}

STDMETHODIMP_(ULONG) CCabObj::AddRef(void)
{
return(m_cRef.AddRef());
}

STDMETHODIMP_(ULONG) CCabObj::Release(void)
{
if (!m_cRef.Release())
{
   delete this;
return(0);
}

return(m_cRef.GetRef());
}

STDMETHODIMP CCabObj::GetData(FORMATETC *pfmt, STGMEDIUM *pmedium)
{
if (!InitFileGroupDesc())
{
return(E_UNEXPECTED);
}

if (pfmt->cfFormat == s_uFileGroupDesc)
{
if (pfmt->ptd==NULL && (pfmt->dwAspect&DVASPECT_CONTENT) && pfmt->lindex==-1
&& (pfmt->tymed&TYMED_HGLOBAL))
{
int cItems = m_lSel.GetCount();
if (cItems < 1)
{
return(E_UNEXPECTED);
}

FILEGROUPDESCRIPTOR *pfgd = (FILEGROUPDESCRIPTOR *)GlobalAlloc(GMEM_FIXED,
sizeof(FILEGROUPDESCRIPTOR) + (cItems-1)*sizeof(FILEDESCRIPTOR));
if (!pfgd)
{
return(E_OUTOFMEMORY);
}

pfgd->cItems = cItems;
for (--cItems; cItems>=0; --cItems)
{
LPCABITEM pItem = m_lSel[cItems];

pfgd->fgd[cItems].dwFlags = FD_ATTRIBUTES|FD_WRITESTIME|FD_FILESIZE;
pfgd->fgd[cItems].dwFileAttributes = pItem->uFileAttribs;
DosDateTimeToFileTime(pItem->uFileDate, pItem->uFileTime,
&pfgd->fgd[cItems].ftLastWriteTime);
pfgd->fgd[cItems].nFileSizeHigh = 0;
pfgd->fgd[cItems].nFileSizeLow  = pItem->dwFileSize;
lstrcpyn(pfgd->fgd[cItems].cFileName, pItem->szName,
sizeof(pfgd->fgd[cItems].cFileName));
}

pmedium->tymed = TYMED_HGLOBAL;
pmedium->hGlobal = (HGLOBAL)pfgd;
pmedium->pUnkForRelease = NULL;

return(NOERROR);
}

return(E_INVALIDARG);
}

if (!InitFileContents())
{
return(E_UNEXPECTED);
}

if (pfmt->cfFormat == s_uFileContents)
{
if (pfmt->ptd==NULL && (pfmt->dwAspect&DVASPECT_CONTENT)
&& (pfmt->tymed&TYMED_HGLOBAL))
{
int cItems = m_lSel.GetCount();
if (pfmt->lindex >= cItems)
{
return(E_INVALIDARG);
}

HRESULT hRes = InitContents();
if (FAILED(hRes))
{
return(hRes);
}

if (!m_lContents[pfmt->lindex])
{
return(E_OUTOFMEMORY);
}

// TODO: Maybe if we run out of memory I will just return an IStream for each
// file and take the time hit to extract each one separately
pmedium->tymed = TYMED_HGLOBAL;
pmedium->hGlobal = m_lContents[pfmt->lindex];
// We will delete these globals when we go away
pmedium->pUnkForRelease = (IDataObject*)this;
AddRef();

return(NOERROR);
}

return(E_INVALIDARG);
}

return(E_NOTIMPL);
}

STDMETHODIMP CCabObj::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
{
return(E_NOTIMPL);
}

STDMETHODIMP CCabObj::QueryGetData(FORMATETC *pfmt)
{
if (!InitFileGroupDesc())
{
return(E_UNEXPECTED);
}

if (pfmt->cfFormat == s_uFileGroupDesc)
{
if (pfmt->ptd==NULL && (pfmt->dwAspect&DVASPECT_CONTENT) && pfmt->lindex==-1
&& (pfmt->tymed&TYMED_HGLOBAL))
{
return(S_OK);
}

return(E_INVALIDARG);
}

if (!InitFileContents())
{
return(E_UNEXPECTED);
}

if (pfmt->cfFormat == s_uFileContents)
{
if (pfmt->ptd==NULL && (pfmt->dwAspect&DVASPECT_CONTENT)
&& (pfmt->tymed&TYMED_HGLOBAL))
{
return(S_OK);
}

return(E_INVALIDARG);
}

return(E_NOTIMPL);
}

STDMETHODIMP CCabObj::GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut)
{
return(E_NOTIMPL);
}

STDMETHODIMP CCabObj::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
{
return(E_NOTIMPL);
}

STDMETHODIMP CCabObj::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
{
if (!InitFileGroupDesc() || !InitFileContents())
{
return(E_UNEXPECTED);
}

    FORMATETC fmte[] = {
        {(USHORT)s_uFileContents,  NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
        {(USHORT)s_uFileGroupDesc, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL },
    };

CObjFormats *pFmts = new CObjFormats(2, fmte);
if (!pFmts)
{
return(E_OUTOFMEMORY);
}

pFmts->AddRef();
HRESULT hRes = pFmts->QueryInterface(IID_IEnumFORMATETC, (LPVOID *)ppenumFormatEtc);
pFmts->Release();

return(hRes);
}

STDMETHODIMP CCabObj::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink,
DWORD *pdwConnection)
{
return(E_NOTIMPL);
}

STDMETHODIMP CCabObj::DUnadvise(DWORD dwConnection)
{
return(E_NOTIMPL);
}

STDMETHODIMP CCabObj::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
{
return(E_NOTIMPL);
}

BOOL CCabObj::InitFileGroupDesc()
{
if (s_uFileGroupDesc)
{
return(TRUE);
}

s_uFileGroupDesc = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
return(s_uFileGroupDesc != 0);
}

BOOL CCabObj::InitFileContents()
{
if (s_uFileContents)
{
return(TRUE);
}

s_uFileContents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
return(s_uFileContents != 0);
}

HGLOBAL * CALLBACK CCabObj::ShouldExtract(LPCSTR pszFile, DWORD dwSize, UINT date,
UINT time, UINT attribs, LPARAM lParam)
{
CCabObj *pThis = (CCabObj*)lParam;

int iItem = pThis->m_lSel.FindInList(pszFile, dwSize, date, time, attribs);
if (iItem < 0)
{
return(EXTRACT_FALSE);
}

// Copy nothing for now
return(&(pThis->m_lContents[iItem]));
}

HRESULT CCabObj::InitContents()
{
if (m_lContents)
{
return(NOERROR);
}

int iCount = m_lSel.GetCount();
if (iCount < 1)
{
return(E_UNEXPECTED);
}

m_lContents = (HGLOBAL *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,
sizeof(HGLOBAL)*m_lSel.GetCount());
if (!m_lContents)
{
return(E_OUTOFMEMORY);
}

char szHere[MAX_PATH];
if (!m_pcfHere->GetPath(szHere))
{
return(E_UNEXPECTED);
}

CCabExtract ceHere(szHere);

ceHere.ExtractItems(m_hwndOwner, DIR_MEM, ShouldExtract, (LPARAM)this);

return(TRUE);
}


DATAOBJ.H

//*******************************************************************************************
//
// Filename : DataObj.h
//
//Definition of CCabObj
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _DATAOBJ_H_
#define _DATAOBJ_H_

class CCabObj : public IDataObject
{
public:
CCabObj(HWND hwndOwner, CCabFolder *pcf, LPCABITEM *apit, UINT cpit);
~CCabObj();

STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();

STDMETHODIMP GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium);
STDMETHODIMP QueryGetData(FORMATETC *pformatetc);
STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut);
STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease);
STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc);
STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink,
DWORD *pdwConnection);
STDMETHODIMP DUnadvise(DWORD dwConnection);
STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise);

private:
BOOL InitFileGroupDesc();
BOOL InitFileContents();

static HGLOBAL * CALLBACK ShouldExtract(LPCSTR pszFile, DWORD dwSize,
UINT date, UINT time, UINT attribs, LPARAM lParam);

HRESULT InitContents();

private:
static UINT s_uFileGroupDesc;
static UINT s_uFileContents;

CRefCount m_cRef;

CRefDll m_cRefDll;

CCabItemList m_lSel;
HGLOBAL *m_lContents;

CCabFolder *m_pcfHere;
HWND m_hwndOwner;
} ;

#endif // _DATAOBJ_H_


DEBUG.C

//*******************************************************************************************
//
// Filename : debug.c
//
//Debug routines
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "debug.h"

#define DEBUG_BREAK        __try { _asm { int 3 } } __except (EXCEPTION_EXECUTE_HANDLER) {;}

#ifdef DEBUG

UINT g_fDebugMask = 0x00ff;

UINT WINAPI SetDebugMask(UINT mask)
{
    UINT wOld = g_fDebugMask;
    g_fDebugMask = mask;

    return wOld;
}

UINT WINAPI GetDebugMask()
{
    return g_fDebugMask;
}

void WINAPI AssertFailed(LPCSTR pszFile, int line)
{
    LPCSTR psz;
    char ach[256];
    static char szAssertFailed[] = "Assertion failed in %s on line %d\r\n";

    // Strip off path info from filename string, if present.
    //
    if (g_fDebugMask & DM_ASSERT)
    {
        for (psz = pszFile + lstrlen(pszFile); psz != pszFile; psz=AnsiPrev(pszFile, psz))
        {
            if ((AnsiPrev(pszFile, psz)!= (psz-2)) && *(psz - 1) == '\\')
                break;
        }
        wsprintf(ach, szAssertFailed, psz, line);
        OutputDebugString(ach);

DEBUG_BREAK
    }
}

void _cdecl _AssertMsg(BOOL f, LPCSTR pszMsg, ...)
{
    char ach[256];

    if (!f && (g_fDebugMask & DM_ASSERT))
    {
        wvsprintf(ach, pszMsg, (LPVOID)(&pszMsg + 1));
        lstrcat(ach, "\r\n");
        OutputDebugString(ach);
DEBUG_BREAK
    }
}

void _cdecl _DebugMsg(UINT mask, LPCSTR pszMsg, ...)
{
    char ach[2*MAX_PATH+40];  // Handles 2*largest path + slop for message

    if (g_fDebugMask & mask)
    {
        wvsprintf(ach, pszMsg, (LPVOID)(&pszMsg + 1));
        lstrcat(ach, "\r\n");
        OutputDebugString(ach);
    }
}

#endif // DEBUG


DEBUG.H

//*******************************************************************************************
//
// Filename : Debug.h
//
//Definitions of Debug routines
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#define NOSHELLDEBUG// don't take shell versions of this

// NOTE: You can #define your own DM_* values using bits in the HI BYTE

#define DM_TRACE    0x0001      // Trace messages
#define DM_WARNING  0x0002      // Warning
#define DM_ERROR    0x0004      // Error
#define DM_ASSERT   0x0008      // Assertions

// NOTE: Default debug mask is 0x00ff (show everything)
//
// Inside debugger, you can modify wDebugMask variable.
//
// Set debug mask; returning previous.
//
UINT WINAPI SetDebugMask(UINT mask);

// Get debug mask.
//
UINT WINAPI GetDebugMask();

// Use this macro to declare message text that will be placed
// in the CODE segment (useful if DS is getting full)
//
// Ex: DBGTEXT(szMsg, "Invalid whatever: %d");
//
#define DBGTEXT(sz, msg)      static const char sz[] = msg;

#ifdef DEBUG
// Assert(f)  -- Generate "assertion failed in line x of file.c"
//               message if f is NOT true.
//
// AssertMsg(f, msg, args...)  -- Generate wsprintf-formatted msg w/params
//                          if f is NOT true.
//
// DebugMsg(mask, msg, args...) -
//         Generate wsprintf-formatted msg using
//         specified debug mask.  System debug mask
//         governs whether message is output.
//
void WINAPI AssertFailed(LPCSTR szFile, int line);

#define Assert(f)                           \
    {                                       \
        DBGTEXT(szFile, __FILE__);          \
        if (!(f))                           \
            AssertFailed(szFile, __LINE__); \
    }
#define AssertE(f) Assert(f)

void __cdecl _AssertMsg(BOOL f, LPCSTR pszMsg, ...);
#define AssertMsg   _AssertMsg

void __cdecl _DebugMsg(UINT mask, LPCSTR psz, ...);
#define DebugMsg    _DebugMsg

#else

// retail versions to produce no code, no data

#define Assert(f)
#define AssertE(f)      (f)
#define AssertMsg   1 ? (void)0 : (void)
#define DebugMsg    1 ? (void)0 : (void)

#endif


DLG.CPP

//*******************************************************************************************
//
// Filename : Dlg.cpp
//
//Implementation file for CDlg, CFileDlg and CPropPage
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "Pch.H"

#include "ThisDll.H"

#include "Dlg.H"

BOOL CDlg::RealDlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return(FALSE);
}

BOOL CALLBACK CDlg::DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CDlg *pThis = (CDlg *)GetWindowLong(hDlg, DWL_USER);

if (uMsg == WM_INITDIALOG)
{
pThis = (CDlg *)lParam;
pThis->m_hDlg = hDlg;
SetWindowLong(hDlg, DWL_USER, lParam);
}
else if (!pThis)
{
return(FALSE);
}

return(pThis->RealDlgProc(uMsg, wParam, lParam));
}

int CDlg::DoModal(UINT idRes, HWND hParent)
{
return(DialogBoxParam(g_ThisDll.GetInstance(), MAKEINTRESOURCE(idRes), hParent,
DlgProc, (LPARAM)this));
}

HWND CDlg::DoModeless(UINT idRes, HWND hParent)
{
return(CreateDialogParam(g_ThisDll.GetInstance(), MAKEINTRESOURCE(idRes), hParent,
DlgProc, (LPARAM)this));
}

CFileDlg::CFileDlg(HWND hwndParent, LPCSTR szFilter, LPSTR szFile, UINT uFileLen,
LPCSTR szTitle)
: m_dwError(0)
{
memset((LPOPENFILENAME)this, 0, sizeof(OPENFILENAME));

lStructSize = sizeof(OPENFILENAME);
hwndOwner = hwndParent;
hInstance = g_ThisDll.GetInstance();
lpstrFilter = szFilter;
lpstrFile = szFile;
nMaxFile = uFileLen;
lpstrTitle = szTitle;
}

BOOL CFileDlg::RealHookProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return(FALSE);
}

BOOL CFileDlg::HookProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CFileDlg *pThis = (CFileDlg *)GetWindowLong(hDlg, DWL_USER);

if (uMsg == WM_INITDIALOG)
{
LPOPENFILENAME pofn = (LPOPENFILENAME)lParam;

pThis = (CFileDlg *)pofn->lCustData;
pThis->m_hDlg = hDlg;
SetWindowLong(hDlg, DWL_USER, pofn->lCustData);
}
else if (!pThis)
{
return(FALSE);
}

return(pThis->RealHookProc(uMsg, wParam, lParam));
}

BOOL CFileOpenDlg::DoModal()
{
// Caller needs to set OFN_ENABLEHOOK to get a hook
lpfnHook = (LPOFNHOOKPROC)HookProc;
lCustData = (LONG)this;

BOOL bRet = GetOpenFileName(this);
m_dwError = bRet ? 0 : CommDlgExtendedError();

return(bRet);
}

CPropPage::~CPropPage()
{
}

CPropPage::CPropPage(LPCSTR szTmplt)
{
dwSize = sizeof(CPropPage);
dwFlags = PSP_DEFAULT;
hInstance = g_ThisDll.GetInstance();
pszTemplate = szTmplt;
pfnDlgProc = PageProc;
m_pThis = this;
}

HRESULT CPropPage::DoModeless(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
{
dwFlags |= PSP_USECALLBACK;
pfnCallback = PageRelease;

    // add the page; note this whole object has been copied to the page structure
    HPROPSHEETPAGE hPage = CreatePropertySheetPage(this);
if (!hPage)
{
return(E_OUTOFMEMORY);
}

if (!lpfnAddPage(hPage, lParam))
{
return(E_UNEXPECTED);
}

    return(NOERROR);

}

BOOL CALLBACK CPropPage::PageProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CPropPage *pThis = (CPropPage *)GetWindowLong(hDlg, DWL_USER);

if (uMsg == WM_INITDIALOG)
{
CPropPage *pThat = IToClass(CPropPage, dwSize, lParam);
pThis = pThat->m_pThis;

pThis->m_hPage = hDlg;
SetWindowLong(hDlg, DWL_USER, (LONG)pThis);
}
else if (!pThis)
{
return(FALSE);
}

return(pThis->RealPageProc(uMsg, wParam, lParam));
}

UINT CALLBACK CPropPage::PageRelease(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
{
if (uMsg == PSPCB_RELEASE)
{
CPropPage *pThat = IToClass(CPropPage, dwSize, ppsp);
CPropPage *pThis = pThat->m_pThis;

delete pThis;
}

return(TRUE);
}

BOOL CPropPage::RealPageProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return(FALSE);
}


DLG.H

//*******************************************************************************************
//
// Filename : Dlg.h
//
//Definitions of CDlg, CFileDlg and CPropPage
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _Dlg_H_
#define _Dlg_H_

class CDlg
{
public:
CDlg() {}
~CDlg() {}

int DoModal(UINT idRes, HWND hParent);
HWND DoModeless(UINT idRes, HWND hParent);

protected:
static BOOL CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

HWND m_hDlg;

private:
virtual BOOL RealDlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
} ;

class CFileDlg : public tagOFNA
{
public:
CFileDlg(HWND hwndParent, LPCSTR szFilter, LPSTR szFile, UINT uFileLen, LPCSTR szTitle);
~CFileDlg() {}

DWORD GetDlgError() {return(m_dwError);}

protected:
DWORD m_dwError;
HWND m_hDlg;

static BOOL CALLBACK HookProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

private:
virtual BOOL RealHookProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
} ;

class CFileOpenDlg : public CFileDlg
{
public:
CFileOpenDlg(HWND hwndParent, LPCSTR szFilter, LPSTR szFile, UINT uFileLen, LPCSTR szTitle)
: CFileDlg(hwndParent, szFilter, szFile, uFileLen, szTitle) {}
~CFileOpenDlg() {}

BOOL DoModal();
} ;

class CPropPage : public _PROPSHEETPAGEA
{
public:
CPropPage(LPCSTR szTmplt);
virtual ~CPropPage();

HRESULT DoModeless(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam);

protected:
static BOOL CALLBACK PageProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

HWND m_hPage;

private:
static UINT CALLBACK PageRelease(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp);

virtual BOOL RealPageProc(UINT uMsg, WPARAM wParam, LPARAM lParam);

CPropPage *m_pThis;
} ;

#endif // _Dlg_H_


DPDA.H

//*******************************************************************************************
//
// Filename : Dpda.h
//
//Definitions of DPA routines
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _DPDA_H_
#define _DPDA_H_

#ifdef __cplusplus
extern "C" {
#endif

// Dynamic pointer array
typedef struct _DPA * HDPA;

HDPA   DPA_Create(int cItemGrow);
HDPA   DPA_CreateEx(int cpGrow, HANDLE hheap);
BOOL   DPA_Destroy(HDPA hdpa);
HDPA   DPA_Clone(HDPA hdpa, HDPA hdpaNew);
void *  DPA_GetPtr(HDPA hdpa, int i);
int    DPA_GetPtrIndex(HDPA hdpa, LPVOID p);
BOOL   DPA_Grow(HDPA pdpa, int cp);
BOOL   DPA_SetPtr(HDPA hdpa, int i, LPVOID p);
int    DPA_InsertPtr(HDPA hdpa, int i, LPVOID p);
void * DPA_DeletePtr(HDPA hdpa, int i);
BOOL   DPA_DeleteAllPtrs(HDPA hdpa);
#define       DPA_GetPtrCount(hdpa)   (*(int *)(hdpa))
#define       DPA_GetPtrPtr(hdpa)     (*((LPVOID * *)((BYTE *)(hdpa) + sizeof(int))))
#define       DPA_FastGetPtr(hdpa, i) (DPA_GetPtrPtr(hdpa)[i])

typedef int (CALLBACK *PFNDPACOMPARE)(LPVOID p1, LPVOID p2, LPARAM lParam);

BOOL   DPA_Sort(HDPA hdpa, PFNDPACOMPARE pfnCompare, LPARAM lParam);

typedef struct _DSA * HDSA;

// Search array.  If DPAS_SORTED, then array is assumed to be sorted
// according to pfnCompare, and binary search algorithm is used.
// Otherwise, linear search is used.
//
// Searching starts at iStart (-1 to start search at beginning).
//
// DPAS_INSERTBEFORE/AFTER govern what happens if an exact match is not
// found.  If neither are specified, this function returns -1 if no exact
// match is found.  Otherwise, the index of the item before or after the
// closest (including exact) match is returned.
//
// Search option flags
//
#define DPAS_SORTED             0x0001
#define DPAS_INSERTBEFORE       0x0002
#define DPAS_INSERTAFTER        0x0004

int DPA_Search(HDPA hdpa, LPVOID pFind, int iStart,
                      PFNDPACOMPARE pfnCompare,
                      LPARAM lParam, UINT options);
#ifdef __cplusplus
}
#endif

#endif // _DPDA_H_


ENUM.CPP

//*******************************************************************************************
//
// Filename : Enum.cpp
//
//Implementation for CEnumCabObjs
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "thisdll.h"
#include "enum.h"

// *** IUnknown methods ***
STDMETHODIMP CEnumCabObjs::QueryInterface(
   REFIID riid,
   LPVOID FAR* ppvObj)
{
*ppvObj = NULL;

LPUNKNOWN pObj;

if (riid == IID_IUnknown)
{
pObj = (IUnknown*)((IEnumIDList*)this);
}
else if (riid == IID_IEnumIDList)
{
pObj = (IUnknown*)((IEnumIDList*)this);
}
else
{
   return(E_NOINTERFACE);
}

pObj->AddRef();
*ppvObj = pObj;

return(NOERROR);
}

STDMETHODIMP_(ULONG) CEnumCabObjs::AddRef(void)
{
return(m_cRef.AddRef());
}

STDMETHODIMP_(ULONG) CEnumCabObjs::Release(void)
{
if (!m_cRef.Release())
{
   delete this;
return(0);
}

return(m_cRef.GetRef());
}

// *** IEnumIDList methods ***
STDMETHODIMP CEnumCabObjs::Next(ULONG celt,
      LPITEMIDLIST *rgelt,
      ULONG *pceltFetched)
{
*rgelt = NULL;
if (pceltFetched)
{
*pceltFetched = 0;
}

HRESULT hRes = m_pcfThis->InitItems();
if (FAILED(hRes))
{
return(hRes);
}

for ( ; ; ++m_iCount)
{
if (m_iCount >= m_pcfThis->m_lItems.GetCount())
{
return(S_FALSE);
}

LPCABITEM pit = m_pcfThis->m_lItems[m_iCount];

if ((m_uFlags&(SHCONTF_FOLDERS|SHCONTF_NONFOLDERS))
!= (SHCONTF_FOLDERS|SHCONTF_NONFOLDERS))
{
DWORD gfInOut = SFGAO_FOLDER;
if (FAILED(m_pcfThis->GetAttributesOf(1, (LPCITEMIDLIST *)&pit, &gfInOut)))
{
continue;
}
if (!(m_uFlags&SHCONTF_FOLDERS) && (gfInOut&SFGAO_FOLDER))
{
continue;
}
if ((m_uFlags&SHCONTF_FOLDERS) && !(gfInOut&SFGAO_FOLDER))
{
continue;
}
}

if (!(m_uFlags&SHCONTF_INCLUDEHIDDEN)
&& (pit->uFileAttribs&(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)))
{
continue;
}

break;
}

*rgelt = ILClone((LPCITEMIDLIST)m_pcfThis->m_lItems[m_iCount]);

++m_iCount;

if (*rgelt)
{
if (pceltFetched)
{
*pceltFetched = 1;
}

return(S_OK);
}

return(E_OUTOFMEMORY);
}

STDMETHODIMP CEnumCabObjs::Skip(ULONG celt)
{
return(E_NOTIMPL);
}

STDMETHODIMP CEnumCabObjs::Reset()
{
m_iCount = 0;

return(NOERROR);
}

STDMETHODIMP CEnumCabObjs::Clone(IEnumIDList **ppenum)
{
return(E_NOTIMPL);
}


ENUM.H

//*******************************************************************************************
//
// Filename : Enum.h
//
//Definition of CEnumCabObjs
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _ENUM_H_
#define _ENUM_H_

#include "folder.h"

// Enumeration object for the CabFolder
class CEnumCabObjs : public IEnumIDList
{
public:
CEnumCabObjs(CCabFolder *pcf, DWORD uFlags) : m_iCount(0)
{
m_uFlags = uFlags;
m_pcfThis=pcf;
pcf->AddRef();
}
~CEnumCabObjs()
{
m_pcfThis->Release();
}

    // *** IUnknown methods ***
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // *** IEnumIDList methods ***
    STDMETHODIMP Next(ULONG celt,
      LPITEMIDLIST *rgelt,
      ULONG *pceltFetched);
    STDMETHODIMP Skip(ULONG celt);
    STDMETHODIMP Reset();
    STDMETHODIMP Clone(IEnumIDList **ppenum);

private:
CRefDll m_cRefDll;

CRefCount m_cRef;

CCabFolder *m_pcfThis;

UINT m_iCount;
DWORD m_uFlags;
} ;

#endif // _ENUM_H_


FDI.H

//*******************************************************************************************
//
// Filename : Fdi.h
//
//Common defines for FCI/FDI stuff
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef INCLUDED_TYPES_FCI_FDI
#define INCLUDED_TYPES_FCI_FDI 1

#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif  /* __cplusplus */

//** Define away for 32-bit (NT/Windows95) build
#ifndef HUGE
#define HUGE
#endif

#ifndef FAR
#define FAR
#endif

#ifndef DIAMONDAPI
#define DIAMONDAPI __cdecl
#endif

//** Specify structure packing explicitly for clients of FDI
#pragma pack(4)

//** Don't redefine types defined in Win16 WINDOWS.H (_INC_WINDOWS)
//   or Win32 WINDOWS.H (_WINDOWS_)
//
#if !defined(_INC_WINDOWS) && !defined(_WINDOWS_)
typedef int            BOOL;     /* f */
typedef unsigned char  BYTE;     /* b */
typedef unsigned int   UINT;     /* ui */
typedef unsigned short USHORT;   /* us */
typedef unsigned long  ULONG;    /* ul */
#endif   // _INC_WINDOWS

typedef unsigned long  CHECKSUM; /* csum */

typedef unsigned long  UOFF;     /* uoff - uncompressed offset */
typedef unsigned long  COFF;     /* coff - cabinet file offset */

#ifndef TRUE
#define TRUE    1
#endif

#ifndef FALSE
#define FALSE   0
#endif

#ifndef NULL
#define NULL    0
#endif

/***    ERF - Error structure
 *
 *  This structure returns error information from FCI/FDI.  The caller should
 *  not modify this structure.
 */
typedef struct {
    int     erfOper;            // FCI/FDI error code -- see FDIERROR_XXX
                                //  and FCIERR_XXX equates for details.

    int     erfType;            // Optional error value filled in by FCI/FDI.
                                // For FCI, this is usually the C run-time
                                // *errno* value.

    BOOL    fError;             // TRUE => error present
} ERF;      /* erf */
typedef ERF FAR *PERF;  /* perf */

#ifdef _DEBUG
// don't hide statics from map during debugging
#define STATIC
#else // !DEBUG
#define STATIC static
#endif // !DEBUG

#define CB_MAX_CHUNK            32768U
#define CB_MAX_DISK         0x7ffffffL
#define CB_MAX_FILENAME            256
#define CB_MAX_CABINET_NAME        256
#define CB_MAX_CAB_PATH            256
#define CB_MAX_DISK_NAME           256

/***    FNALLOC - Memory Allocation
 *      FNFREE  - Memory Free
 *
 *  These are modeled after the C run-time routines malloc() and free()
 *  (16-bit clients please note -- the size is a ULONG, so you may need
 *  to write a wrapper routine for halloc!).  FDI expects error
 *  handling to be identical to these C run-time routines.
 *
 *  As long as you faithfully copy the semantics of malloc() and free(),
 *  you can supply any functions you like!
 *
 *  WARNING: You should never assume anything about the sequence of
 *           PFNALLOC and PFNFREE calls -- incremental releases of
 *           Diamond/FDI may have radically different numbers of
 *           PFNALLOC calls and allocation sizes!
 */
typedef void HUGE * (FAR DIAMONDAPI *PFNALLOC)(ULONG cb); /* pfna */
#define FNALLOC(fn) void HUGE * FAR DIAMONDAPI fn(ULONG cb)

typedef void (FAR DIAMONDAPI *PFNFREE)(void HUGE *pv); /* pfnf */
#define FNFREE(fn) void FAR DIAMONDAPI fn(void HUGE *pv)

/***    tcompXXX - Diamond compression types
 *
 *  These are passed to FCIAddFile(), and are also stored in the CFFOLDER
 *  structures in cabinet files.
 *
 *  NOTE: We reserve bits for the TYPE, QUANTUM_LEVEL, and QUANTUM_MEM
 *        to provide room for future expansion.  Since this value is stored
 *        in the CFDATA records in the cabinet file, we don't want to
 *        have to change the format for existing compression configurations
 *        if we add new ones in the future.  This will allows us to read
 *        old cabinet files in the future.
 */

typedef unsigned short TCOMP; /* tcomp */

#define tcompMASK_TYPE          0x000F  // Mask for compression type
#define tcompTYPE_NONE          0x0000  // No compression
#define tcompTYPE_MSZIP         0x0001  // MSZIP
#define tcompTYPE_QUANTUM       0x0002  // Quantum
#define tcompBAD                0x000F  // Unspecified compression type

#define tcompMASK_QUANTUM_LEVEL 0x00F0  // Mask for Quantum Compression Level
#define tcompQUANTUM_LEVEL_LO   0x0010  // Lowest Quantum Level (1)
#define tcompQUANTUM_LEVEL_HI   0x0070  // Highest Quantum Level (7)
#define tcompSHIFT_QUANTUM_LEVEL     4  // Amount to shift over to get int

#define tcompMASK_QUANTUM_MEM   0x1F00  // Mask for Quantum Compression Memory
#define tcompQUANTUM_MEM_LO     0x0A00  // Lowest Quantum Memory (10)
#define tcompQUANTUM_MEM_HI     0x1500  // Highest Quantum Memory (21)
#define tcompSHIFT_QUANTUM_MEM       8  // Amount to shift over to get int

#define tcompMASK_RESERVED      0xE000  // Reserved bits (high 3 bits)

#define CompressionTypeFromTCOMP(tc) \
            ((tc) & tcompMASK_TYPE)

#define CompressionLevelFromTCOMP(tc) \
            (((tc) & tcompMASK_QUANTUM_LEVEL) >> tcompSHIFT_QUANTUM_LEVEL)

#define CompressionMemoryFromTCOMP(tc) \
            (((tc) & tcompMASK_QUANTUM_MEM) >> tcompSHIFT_QUANTUM_MEM)

#define TCOMPfromTypeLevelMemory(t,l,m)           \
            (((m) << tcompSHIFT_QUANTUM_MEM  ) |  \
             ((l) << tcompSHIFT_QUANTUM_LEVEL) |  \
             ( t                             ))

//** Revert to default structure packing
#pragma pack()

#endif // !INCLUDED_TYPES_FCI_FDI
/***     - Diamond File Decompression Interface definitions
 *
 *
 *  ATTENTION:
 *      This is the only documentation on the Diamond File Decompression
 *      Interface (FDI).  Please read it carefully, as there are some subtle
 *      points in FDI that are carefully explained below.
 *
 *  Concepts:
 *      A *cabinet* file contains one or more *folders*.  A folder contains
 *      one or more (pieces of) *files*.  A folder is by definition a
 *      decompression unit, i.e., to extract a file from a folder, all of
 *      the data from the start of the folder up through and including the
 *      desired file must be read and decompressed.
 *
 *      A folder can span one (or more) cabinet boundaries, and by implication
 *      a file can also span one (or more) cabinet boundaries.  Indeed, more
 *      than one file can span a cabinet boundary, since Diamond concatenates
 *      files together into a single data stream before compressing (actually,
 *      at most one file will span any one cabinet boundary, but Diamond does
 *      not know which file this is, since the mapping from uncompressed bytes
 *      to compressed bytes is pretty obscure.  Also, since Diamond compresses
 *      in blocks of 32K (at present), any files with data in a 32K block that
 *      spans a cabinet boundary require Diamond to read both cabinet files
 *      to get the two halves of the compressed block).
 *
 *  Overview:
 *      The File Decompression Interface is used to simplify the reading of
 *      Diamond cabinet files.  A setup program will proceed in a manner very
 *      similar to the pseudo code below.  An FDI context is created, the
 *      setup program calls FDICopy() for each cabinet to be processed.  For
 *      each file in the cabinet, FDICopy() calls a notification callback
 *      routine, asking the setup program if the file should be copied.
 *      This call-back approach is great because it allows the cabinet file
 *      to be read and decompressed in an optimal manner, and also makes FDI
 *      independent of the run-time environment -- FDI makes *no* C run-time
 *      calls whatsoever.  All memory allocation and file I/O functions are
 *      passed into FDI by the client.
 *
 *      main(...)
 *      {
 *          // Read INF file to construct list of desired files.
 *          //  Ideally, these would be sorted in the same order as the
 *          //  files appear in the cabinets, so that you can just walk
 *          //  down the list in response to fdintCOPY_FILE notifications.
 *
 *          // Construct list of required cabinets.
 *
 *          hfdi = FDICreate(...);          // Create FDI context
 *          For (cabinet in List of Cabinets) {
 *              FDICopy(hfdi,cabinet,fdiNotify,...);  // Process each cabinet
 *          }
 *          FDIDestroy(hfdi);
 *          ...
 *      }
 *
 *      // Notification callback function
 *      fdiNotify(fdint,...)
 *      {
 *          If (User Aborted)               // Permit cancellation
 *              if (fdint == fdintCLOSE_FILE_INFO)
 *                  close open file
 *              return -1;
 *          switch (fdint) {
 *              case fdintCOPY_FILE:        // File to copy, maybe
 *                  // Check file against list of desired files
 *                  if want to copy file
 *                      open destination file and return handle
 *                  else
 *                      return NULL;        // Skip file
 *              case fdintCLOSE_FILE_INFO:
 *                  close file
 *                  set date, time, and attributes
 *
 *              case fdintNEXT_CABINET:
 *                  if not an error callback
 *                      Tell FDI to use suggested directory name
 *                  else
 *                      Tell user what the problem was, and prompt
 *                          for a new disk and/or path.
 *                      if user aborts
 *                          Tell FDI to abort
 *                      else
 *                          return to FDI to try another cabinet
 *                  //NOTE: Be sure to see the (sample) code in EXTRACT.C
 *                  //      for an example of how to do this!
 *              ...
 *      }
 *
 *  Error Handling Suggestions:
 *      Since you the client have passed in *all* of the functions that
 *      FDI uses to interact with the "outside" world, you are in prime
 *      position to understand and deal with errors.
 *
 *      The general philosophy of FDI is to pass all errors back up to
 *      the client.  FDI returns fairly generic error codes in the case
 *      where one of the callback functions (PFNOPEN, PFNREAD, etc.) fail,
 *      since it assumes that the callback function will save enough
 *      information in a static/global so that when FDICopy() returns
 *      fail, the client can examine this information and report enough
 *      detail about the problem that the user can take corrective action.
 *
 *      For very specific errors (CORRUPT_CABINET, for example), FDI returns
 *      very specific error codes.
 *
 *      THE BEST POLICY IS FOR YOUR CALLBACK ROUTINES TO AVOID RETURNING
 *      ERRORS TO FDI!
 *
 *      Examples:
 *          (1) If the disk is getting full, instead of returning an error
 *              from your PFNWRITE function, you should -- inside your
 *              PFNWRITE function -- put up a dialog telling the user to free
 *              some disk space.
 *          (2) When you get the fdintNEXT_CABINET notification, you should
 *              verify that the cabinet you return is the correct one (call
 *              FDIIsCabinet(), and make sure the setID matches the one for
 *              the current cabinet specified in the fdintCABINET_INFO, and
 *              that the disk number is one greater.
 *
 *              NOTE: FDI will continue to call fdintNEXT_CABINET until it
 *                    gets the cabinet it wants, or until you return -1
 *                    to abort the FDICopy() call.
 *
 *      The documentation below on the FDI error codes provides explicit
 *      guidance on how to avoid each error.
 *
 *      If you find you must return a failure to FDI from one of your
 *      callback functions, then FDICopy() frees all resources it allocated
 *      and closes all files.  If you can figure out how to overcome the
 *      problem, you can call FDICopy() again on the last cabinet, and
 *      skip any files that you already copied.  But, note that FDI does
 *      *not* maintain any state between FDICopy() calls, other than possibly
 *      memory allocated for the decompressor.
 *
 *      See FDIERROR for details on FDI error codes and recommended actions.
 *
 *
 *  Progress Indicator Suggestions:
 *      As above, all of the file I/O functions are supplied by you.  So,
 *      updating a progress indicator is very simple.  You keep track of
 *      the target files handles you have opened, along with the uncompressed
 *      size of the target file.  When you see writes to the handle of a
 *      target file, you use the write count to update your status!
 *      Since this method is available, there is no separate callback from
 *      FDI just for progess indication.
 */

#ifndef INCLUDED_FDI
#define INCLUDED_FDI    1

//** Specify structure packing explicitly for clients of FDI
#pragma pack(4)

/***    FDIERROR - Error codes returned in erf.erfOper field
 *
 *  In general, FDI will only fail if one of the passed in memory or
 *  file I/O functions fails.  Other errors are pretty unlikely, and are
 *  caused by corrupted cabinet files, passing in a file which is not a
 *  cabinet file, or cabinet files out of order.
 *
 *  Description:    Summary of error.
 *  Cause:          List of possible causes of this error.
 *  Response:       How client might respond to this error, or avoid it in
 *                  the first place.
 */
typedef enum {
    FDIERROR_NONE,
        // Description: No error
        // Cause:       Function was successfull.
        // Response:    Keep going!

    FDIERROR_CABINET_NOT_FOUND,
        // Description: Cabinet not found
        // Cause:       Bad file name or path passed to FDICopy(), or returned
        //              to fdintNEXT_CABINET.
        // Response:    To prevent this error, validate the existence of the
        //              the cabinet *before* passing the path to FDI.

    FDIERROR_NOT_A_CABINET,
        // Description: Cabinet file does not have the correct format
        // Cause:       File passed to to FDICopy(), or returned to
        //              fdintNEXT_CABINET, is too small to be a cabinet file,
        //              or does not have the cabinet signature in its first
        //              four bytes.
        // Response:    To prevent this error, call FDIIsCabinet() to check a
        //              cabinet before calling FDICopy() or returning the
        //              cabinet path to fdintNEXT_CABINET.

    FDIERROR_UNKNOWN_CABINET_VERSION,
        // Description: Cabinet file has an unknown version number.
        // Cause:       File passed to to FDICopy(), or returned to
        //              fdintNEXT_CABINET, has what looks like a cabinet file
        //              header, but the version of the cabinet file format
        //              is not one understood by this version of FDI.  The
        //              erf.erfType field is filled in with the version number
        //              found in the cabinet file.
        // Response:    To prevent this error, call FDIIsCabinet() to check a
        //              cabinet before calling FDICopy() or returning the
        //              cabinet path to fdintNEXT_CABINET.

    FDIERROR_CORRUPT_CABINET,
        // Description: Cabinet file is corrupt
        // Cause:       FDI returns this error any time it finds a problem
        //              with the logical format of a cabinet file, and any
        //              time one of the passed-in file I/O calls fails when
        //              operating on a cabinet (PFNOPEN, PFNSEEK, PFNREAD,
        //              or PFNCLOSE).  The client can distinguish these two
        //              cases based upon whether the last file I/O call
        //              failed or not.
        // Response:    Assuming this is not a real corruption problem in
        //              a cabinet file, the file I/O functions could attempt
        //              to do retries on failure (for example, if there is a
        //              temporary network connection problem).  If this does
        //              not work, and the file I/O call has to fail, then the
        //              FDI client will have to clean up and call the
        //              FDICopy() function again.

    FDIERROR_ALLOC_FAIL,
        // Description: Could not allocate enough memory
        // Cause:       FDI tried to allocate memory with the PFNALLOC
        //              function, but it failed.
        // Response:    If possible, PFNALLOC should take whatever steps
        //              are possible to allocate the memory requested.  If
        //              memory is not immediately available, it might post a
        //              dialog asking the user to free memory, for example.
        //              Note that the bulk of FDI's memory allocations are
        //              made at FDICreate() time and when the first cabinet
        //              file is opened during FDICopy().

    FDIERROR_BAD_COMPR_TYPE,
        // Description: Unknown compression type in a cabinet folder
        // Cause:       [Should never happen.]  A folder in a cabinet has an
        //              unknown compression type.  This is probably caused by
        //              a mismatch between the version of Diamond used to
        //              create the cabinet and the FDI. LIB used to read the
        //              cabinet.
        // Response:    Abort.

    FDIERROR_MDI_FAIL,
        // Description: Failure decompressing data from a cabinet file
        // Cause:       The decompressor found an error in the data coming
        //              from the file cabinet.  The cabinet file was corrupted.
        //              [11-Apr-1994 bens When checksuming is turned on, this
        //              error should never occur.]
        // Response:    Probably should abort; only other choice is to cleanup
        //              and call FDICopy() again, and hope there was some
        //              intermittent data error that will not reoccur.

    FDIERROR_TARGET_FILE,
        // Description: Failure writing to target file
        // Cause:       FDI returns this error any time it gets an error back
        //              from one of the passed-in file I/O calls fails when
        //              writing to a file being extracted from a cabinet.
        // Response:    To avoid or minimize this error, the file I/O functions
        //              could attempt to avoid failing.  A common cause might
        //              be disk full -- in this case, the PFNWRITE function
        //              could have a check for free space, and put up a dialog
        //              asking the user to free some disk space.

    FDIERROR_RESERVE_MISMATCH,
        // Description: Cabinets in a set do not have the same RESERVE sizes
        // Cause:       [Should never happen]. FDI requires that the sizes of
        //              the per-cabinet, per-folder, and per-data block
        //              RESERVE sections be consistent across all the cabinet
        //              in a set.  Diamond will only generate cabinet sets
        //              with these properties.
        // Response:    Abort.

    FDIERROR_WRONG_CABINET,
        // Description: Cabinet returned on fdintNEXT_CABINET is incorrect
        // Cause:       NOTE: THIS ERROR IS NEVER RETURNED BY FDICopy()!
        //              Rather, FDICopy() keeps calling the fdintNEXT_CABINET
        //              callback until either the correct cabinet is specified,
        //              or you return ABORT.
        //              When FDICopy() is extracting a file that crosses a
        //              cabinet boundary, it calls fdintNEXT_CABINET to ask
        //              for the path to the next cabinet.  Not being very
        //              trusting, FDI then checks to make sure that the
        //              correct continuation cabinet was supplied!  It does
        //              this by checking the "setID" and "iCabinet" fields
        //              in the cabinet.  When DIAMOND.EXE creates a set of
        //              cabinets, it constructs the "setID" using the sum
        //              of the bytes of all the destination file names in
        //              the cabinet set.  FDI makes sure that the 16-bit
        //              setID of the continuation cabinet matches the
        //              cabinet file just processed.  FDI then checks that
        //              the cabinet number (iCabinet) is one more than the
        //              cabinet number for the cabinet just processed.
        // Response:    You need code in your fdintNEXT_CABINET (see below)
        //              handler to do retries if you get recalled with this
        //              error.  See the sample code (EXTRACT.C) to see how
        //              this should be handled.

    FDIERROR_USER_ABORT,
        // Description: FDI aborted.
        // Cause:       An FDI callback returnd -1 (usually).
        // Response:    Up to client.

} FDIERROR;

/***    HFDI - Handle to an FDI context
 *
 *  FDICreate() creates this, and it must be passed to all other FDI
 *  functions.
 */
typedef void FAR *HFDI; /* hfdi */

/***    FDICABINETINFO - Information about a cabinet
 *
 */
typedef struct {
    long        cbCabinet;              // Total length of cabinet file
    USHORT      cFolders;               // Count of folders in cabinet
    USHORT      cFiles;                 // Count of files in cabinet
    USHORT      setID;                  // Cabinet set ID
    USHORT      iCabinet;               // Cabinet number in set (0 based)
    BOOL        fReserve;               // TRUE => RESERVE present in cabinet
    BOOL        hasprev;                // TRUE => Cabinet is chained prev
    BOOL        hasnext;                // TRUE => Cabinet is chained next
} FDICABINETINFO; /* fdici */
typedef FDICABINETINFO FAR *PFDICABINETINFO; /* pfdici */

/***    FDIDECRYPTTYPE - PFNFDIDECRYPT command types
 *
 */
typedef enum {
    fdidtNEW_CABINET,                   // New cabinet
    fdidtNEW_FOLDER,                    // New folder
    fdidtDECRYPT,                       // Decrypt a data block
} FDIDECRYPTTYPE; /* fdidt */

/***    FDIDECRYPT - Data for PFNFDIDECRYPT function
 *
 */
typedef struct {
    FDIDECRYPTTYPE    fdidt;            // Command type (selects union below)
    void FAR         *pvUser;           // Decryption context
    union {
        struct {                        // fdidtNEW_CABINET
            void FAR *pHeaderReserve;   // RESERVE section from CFHEADER
            USHORT    cbHeaderReserve;  // Size of pHeaderReserve
            USHORT    setID;            // Cabinet set ID
            int       iCabinet;         // Cabinet number in set (0 based)
        } cabinet;

        struct {                        // fdidtNEW_FOLDER
            void FAR *pFolderReserve;   // RESERVE section from CFFOLDER
            USHORT    cbFolderReserve;  // Size of pFolderReserve
            USHORT    iFolder;          // Folder number in cabinet (0 based)
        } folder;

        struct {                        // fdidtDECRYPT
            void FAR *pDataReserve;     // RESERVE section from CFDATA
            USHORT    cbDataReserve;    // Size of pDataReserve
            void FAR *pbData;           // Data buffer
            USHORT    cbData;           // Size of data buffer
            BOOL      fSplit;           // TRUE if this is a split data block
            USHORT    cbPartial;        // 0 if this is not a split block, or
                                        //  the first piece of a split block;
                                        // Greater than 0 if this is the
                                        //  second piece of a split block.
        } decrypt;
    };
} FDIDECRYPT; /* fdid */
typedef FDIDECRYPT FAR *PFDIDECRYPT; /* pfdid */

/***    PFNFDIDECRYPT - FDI Decryption callback
 *
 *  If this function is passed on the FDICopy() call, then FDI calls it
 *  at various times to update the decryption state and to decrypt FCDATA
 *  blocks.
 *
 *  Common Entry Conditions:
 *      pfdid->fdidt  - Command type
 *      pfdid->pvUser - pvUser value from FDICopy() call
 *
 *  fdidtNEW_CABINET:   //** Notification of a new cabinet
 *      Entry:
 *        pfdid->cabinet.
 *          pHeaderReserve  - RESERVE section from CFHEADER
 *          cbHeaderReserve - Size of pHeaderReserve
 *          setID           - Cabinet set ID
 *          iCabinet        - Cabinet number in set (0 based)
 *      Exit-Success:
 *          returns anything but -1;
 *      Exit-Failure:
 *          returns -1; FDICopy() is aborted.
 *      Notes:
 *      (1) This call allows the decryption code to pick out any information
 *          from the cabinet header reserved area (placed there by DIACRYPT)
 *          needed to perform decryption.  If there is no such information,
 *          this call would presumably be ignored.
 *      (2) This call is made very soon after fdintCABINET_INFO.
 *
 *  fdidtNEW_FOLDER:    //** Notification of a new folder
 *      Entry:
 *        pfdid->folder.
 *          pFolderReserve  - RESERVE section from CFFOLDER
 *          cbFolderReserve - Size of pFolderReserve
 *          iFolder         - Folder number in cabinet (0 based)
 *      Exit-Success:
 *          returns anything but -1;
 *      Exit-Failure:
 *          returns -1; FDICopy() is aborted.
 *      Notes:
 *          This call allows the decryption code to pick out any information
 *          from the folder reserved area (placed there by DIACRYPT) needed
 *          to perform decryption.  If there is no such information, this
 *          call would presumably be ignored.
 *
 *  fdidtDECRYPT:       //** Decrypt a data buffer
 *      Entry:
 *        pfdid->folder.
 *          pDataReserve  - RESERVE section for this CFDATA block
 *          cbDataReserve - Size of pDataReserve
 *          pbData        - Data buffer
 *          cbData        - Size of data buffer
 *          fSplit        - TRUE if this is a split data block
 *          cbPartial     - 0 if this is not a split block, or the first
 *                              piece of a split block; Greater than 0 if
 *                              this is the second piece of a split block.
 *      Exit-Success:
 *          returns TRUE;
 *      Exit-Failure:
 *          returns FALSE; error during decrypt
 *          returns -1; FDICopy() is aborted.
 *      Notes:
 *          Diamond will split CFDATA blocks across cabinet boundaries if
 *          necessary.  To provide maximum flexibility, FDI will call the
 *          fdidtDECRYPT function twice on such split blocks, once when
 *          the first portion is read, and again when the second portion
 *          is read.  And, of course, most data blocks will not be split.
 *          So, there are three cases:
 *
 *           1) fSplit == FALSE
 *              You have the entire data block, so decrypt it.
 *
 *           2) fSplit == TRUE, cbPartial == 0
 *              This is the first portion of a split data block, so cbData
 *              is the size of this portion.  You can either choose to decrypt
 *              this piece, or ignore this call and decrypt the full CFDATA
 *              block on the next (second) fdidtDECRYPT call.
 *
 *           3) fSplit == TRUE, cbPartial > 0
 *              This is the second portion of a split data block (indeed,
 *              cbPartial will have the same value as cbData did on the
 *              immediately preceeding fdidtDECRYPT call!).  If you decrypted
 *              the first portion on the first call, then you can decrypt the
 *              second portion now.  If you ignored the first call, then you
 *              can decrypt the entire buffer.
 *              NOTE: pbData points to the second portion of the split data
 *                    block in this case, *not* the entire data block.  If
 *                    you want to wait until the second piece to decrypt the
 *                    *entire* block, pbData-cbPartial is the address of the
 *                    start of the whole block, and cbData+cbPartial is its
 *                    size.
 */
typedef int (FAR DIAMONDAPI *PFNFDIDECRYPT)(PFDIDECRYPT pfdid); /* pfnfdid */
#define FNFDIDECRYPT(fn) int FAR DIAMONDAPI fn(PFDIDECRYPT pfdid)

/***    FDINOTIFICATION - Notification structure for PFNFDINOTIFY
 *
 *  See the FDINOTIFICATIONTYPE definition for information on usage and
 *  meaning of these fields.
 */
typedef struct {
// long fields
    long      cb;
    char FAR *psz1;
    char FAR *psz2;
    char FAR *psz3;                     // Points to a 256 character buffer
    void FAR *pv;                       // Value for client

// int fields
    int       hf;

// short fields
    USHORT    date;
    USHORT    time;
    USHORT    attribs;

    USHORT    setID;                    // Cabinet set ID
    USHORT    iCabinet;                 // Cabinet number (0-based)

    FDIERROR  fdie;
} FDINOTIFICATION, FAR *PFDINOTIFICATION;  /* fdin, pfdin */

/***    FDINOTIFICATIONTYPE - FDICopy notification types
 *
 *  The notification function for FDICopy can be called with the following
 *  values for the fdint parameter.  In all cases, the pfdin->pv field is
 *  filled in with the value of the pvUser argument passed in to FDICopy().
 *
 *  A typical sequence of calls will be something like this:
 *      fdintCABINET_INFO     // Info about the cabinet
 *      fdintPARTIAL_FILE     // Only if this is not the first cabinet, and
 *                            // one or more files were continued from the
 *                            // previous cabinet.
 *      ...
 *      fdintPARTIAL_FILE
 *      fdintCOPY_FILE        // The first file that starts in this cabinet
 *      ...
 *      fdintCOPY_FILE        // Now let's assume you want this file...
 *      // PFNWRITE called multiple times to write to this file.
 *      fdintCLOSE_FILE_INFO  // File done, set date/time/attributes
 *
 *      fdintCOPY_FILE        // Now let's assume you want this file...
 *      // PFNWRITE called multiple times to write to this file.
 *      fdintNEXT_CABINET     // File was continued to next cabinet!
 *      fdintCABINET_INFO     // Info about the new cabinet
 *      // PFNWRITE called multiple times to write to this file.
 *      fdintCLOSE_FILE_INFO  // File done, set date/time/attributes
 *      ...
 *
 *  fdintCABINET_INFO:
 *        Called exactly once for each cabinet opened by FDICopy(), including
 *        continuation cabinets opened due to file(s) spanning cabinet
 *        boundaries. Primarily intended to permit EXTRACT.EXE to
 *        automatically select the next cabinet in a cabinet sequence even if
 *        not copying files that span cabinet boundaries.
 *      Entry:
 *          pfdin->psz1     = name of next cabinet
 *          pfdin->psz2     = name of next disk
 *          pfdin->psz3     = cabinet path name
 *          pfdin->setID    = cabinet set ID (a random 16-bit number)
 *          pfdin->iCabinet = Cabinet number within cabinet set (0-based)
 *      Exit-Success:
 *          Return anything but -1
 *      Exit-Failure:
 *          Returns -1 => Abort FDICopy() call
 *      Notes:
 *          This call is made *every* time a new cabinet is examined by
 *          FDICopy().  So if "foo2.cab" is examined because a file is
 *          continued from "foo1.cab", and then you call FDICopy() again
 *          on "foo2.cab", you will get *two* fdintCABINET_INFO calls all
 *          told.
 *
 *  fdintCOPY_FILE:
 *        Called for each file that *starts* in the current cabinet, giving
 *        the client the opportunity to request that the file be copied or
 *        skipped.
 *      Entry:
 *          pfdin->psz1    = file name in cabinet
 *          pfdin->cb      = uncompressed size of file
 *          pfdin->date    = file date
 *          pfdin->time    = file time
 *          pfdin->attribs = file attributes
 *      Exit-Success:
 *          Return non-zero file handle for destination file; FDI writes
 *          data to this file use the PFNWRITE function supplied to FDICreate,
 *          and then calls fdintCLOSE_FILE_INFO to close the file and set
 *          the date, time, and attributes.  NOTE: This file handle returned
 *          must also be closeable by the PFNCLOSE function supplied to
 *          FDICreate, since if an error occurs while writing to this handle,
 *          FDI will use the PFNCLOSE function to close the file so that the
 *          client may delete it.
 *      Exit-Failure:
 *          Returns 0  => Skip file, do not copy
 *          Returns -1 => Abort FDICopy() call
 *
 *  fdintCLOSE_FILE_INFO:
 *        Called after all of the data has been written to a target file.
 *        This function must close the file and set the file date, time,
 *        and attributes.
 *      Entry:
 *          pfdin->psz1    = file name in cabinet
 *          pfdin->hf      = file handle
 *          pfdin->date    = file date
 *          pfdin->time    = file time
 *          pfdin->attribs = file attributes
 *      Exit-Success:
 *          Returns TRUE
 *      Exit-Failure:
 *          Returns FALSE, or -1 to abort;
 *              IMPORTANT NOTE:
 *                  FDI assumes that the target file was closed, even if this
 *                  callback returns failure.  FDI will NOT attempt to use
 *                  the PFNCLOSE function supplied on FDICreate() to close
 *                  the file!
 *
 *  fdintPARTIAL_FILE:
 *        Called for files at the front of the cabinet that are CONTINUED
 *        from a previous cabinet.  This callback occurs only when FDICopy is
 *        started on second or subsequent cabinet in a series that has files
 *        continued from a previous cabinet.
 *      Entry:
 *          pfdin->psz1 = file name of file CONTINUED from a PREVIOUS cabinet
 *          pfdin->psz2 = name of cabinet where file starts
 *          pfdin->psz3 = name of disk where file starts
 *      Exit-Success:
 *          Return anything other than -1; enumeration continues
 *      Exit-Failure:
 *          Returns -1 => Abort FDICopy() call
 *
 *  fdintNEXT_CABINET:
 *        This function is *only* called when fdintCOPY_FILE was told to copy
 *        a file in the current cabinet that is continued to a subsequent
 *        cabinet file.  It is important that the cabinet path name (psz3)
 *        be validated before returning!  This function should ensure that
 *        the cabinet exists and is readable before returning.  So, this
 *        is the function that should, for example, issue a disk change
 *        prompt and make sure the cabinet file exists.
 *
 *        When this function returns to FDI, FDI will check that the setID
 *        and iCabinet match the expected values for the next cabinet.
 *        If not, FDI will continue to call this function until the correct
 *        cabinet file is specified, or until this function returns -1 to
 *        abort the FDICopy() function.  pfdin->fdie is set to
 *        FDIERROR_WRONG_CABINET to indicate this case.
 *
 *        If you *haven't* ensured that the cabinet file is present and
 *        readable, or the cabinet file has been damaged, pfdin->fdie will
 *        receive other appropriate error codes:
 *
 *              FDIERROR_CABINET_NOT_FOUND
 *              FDIERROR_NOT_A_CABINET
 *              FDIERROR_UNKNOWN_CABINET_VERSION
 *              FDIERROR_CORRUPT_CABINET
 *              FDIERROR_BAD_COMPR_TYPE
 *              FDIERROR_RESERVE_MISMATCH
 *              FDIERROR_WRONG_CABINET
 *
 *      Entry:
 *          pfdin->psz1 = name of next cabinet where current file is continued
 *          pfdin->psz2 = name of next disk where current file is continued
 *          pfdin->psz3 = cabinet path name; FDI concatenates psz3 with psz1
 *                          to produce the fully-qualified path for the cabinet
 *                          file.  The 256-byte buffer pointed at by psz3 may
 *                          be modified, but psz1 may not!
 *          pfdin->fdie = FDIERROR_WRONG_CABINET if the previous call to
 *                        fdintNEXT_CABINET specified a cabinet file that
 *                        did not match the setID/iCabinet that was expected.
 *      Exit-Success:
 *          Return anything but -1
 *      Exit-Failure:
 *          Returns -1 => Abort FDICopy() call
 *      Notes:
 *          This call is almost always made when a target file is open and
 *          being written to, and the next cabinet is needed to get more
 *          data for the file.
 */
typedef enum {
    fdintCABINET_INFO,              // General information about cabinet
    fdintPARTIAL_FILE,              // First file in cabinet is continuation
    fdintCOPY_FILE,                 // File to be copied
    fdintCLOSE_FILE_INFO,           // close the file, set relevant info
    fdintNEXT_CABINET,              // File continued to next cabinet
} FDINOTIFICATIONTYPE; /* fdint */

typedef int (FAR DIAMONDAPI *PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint,
                                           PFDINOTIFICATION    pfdin); /* pfnfdin */

#define FNFDINOTIFY(fn) int FAR DIAMONDAPI fn(FDINOTIFICATIONTYPE fdint, \
                                              PFDINOTIFICATION    pfdin)

/***    PFNOPEN  - File I/O callbacks for FDI
 *      PFNREAD
 *      PFNWRITE
 *      PFNCLOSE
 *      PFNSEEK
 *
 *  These are modeled after the C run-time routines _open, _read,
 *  _write, _close, and _lseek.  The values for the PFNOPEN oflag
 *  and pmode calls are those defined for _open.  FDI expects error
 *  handling to be identical to these C run-time routines.
 *
 *  As long as you faithfully copy these aspects, you can supply
 *  any functions you like!
 *
 *
 *  SPECIAL NOTE FOR QUANTUM DECOMPRESSION:
 *      When using Quantum compression, at compress time (with Diamond)
 *      you specify how much memory Quantum requires at *decompress* time
 *      to store the decompression history buffer.  This can be as large
 *      as *2Mb*, and in an MS-DOS environment, for example, this much
 *      memory may not be available (certainly not under 640K!).  To permit
 *      large CompressionMemory settings on any machine, the Quantum
 *      decompressor will attempt to create a "spill file" if there is not
 *      sufficient memory available.
 *
 *      For PFNOPEN, a special pszFile parameter is passed to indicate that
 *      a temporary "spill file" is requested.  The name passed is "*", and
 *      you should cast the pszFile parameter to an FDISPILLFILE pointer,
 *      and get the requested file size.  You then need to create a file
 *      of the specified size with read/write access, save the file name and
 *      handle for later use by PFNCLOSE, and then return the handle.  If
 *      you cannot create the file of the specified size, you should return
 *      an error (-1).  This file should be placed on a fast local hard disk,
 *      to maximize the speed of decompression.
 *
 *      For PFNCLOSE, you should check the handle to see if it the spill file
 *      created previously by PFNOPEN (FDI will create at most one spill file
 *      per FDICreate() call).  If it is the spill file handle, you should
 *      close the handle and then delete the file, using the file name you
 *      saved when you created the spill file in PFNOPEN.
 *
 *  WARNING: You should never assume you know what file is being
 *           opened at any one point in time!  FDI will usually
 *           stick to opening cabinet files, but it is possible
 *           that in a future implementation it may open temporary
 *           files or open cabinet files in a different order.
 *
 *  Notes for Memory Mapped File fans:
 *      You can write wrapper routines to allow FDI to work on memory
 *      mapped files.  You'll have to create your own "handle" type so that
 *      you can store the base memory address of the file and the current
 *      seek position, and then you'll allocate and fill in one of these
 *      structures and return a pointer to it in response to the PFNOPEN
 *      call and the fdintCOPY_FILE call.  Your PFNREAD and PFNWRITE
 *      functions will do memcopy(), and update the seek position in your
 *      "handle" structure.  PFNSEEK will just change the seek position
 *      in your "handle" structure.
 */
typedef int  (FAR DIAMONDAPI *PFNOPEN) (char FAR *pszFile, int oflag, int pmode);
typedef UINT (FAR DIAMONDAPI *PFNREAD) (int hf, void FAR *pv, UINT cb);
typedef UINT (FAR DIAMONDAPI *PFNWRITE)(int hf, void FAR *pv, UINT cb);
typedef int  (FAR DIAMONDAPI *PFNCLOSE)(int hf);
typedef long (FAR DIAMONDAPI *PFNSEEK) (int hf, long dist, int seektype);

#pragma pack (1)

/** FDISPILLFILE - Pass as pszFile on PFNOPEN to create spill file
 *
 *  ach    - A two byte string to signal to PFNOPEN that a spill file is
 *           requested.  Value is '*','\0'.
 *  cbFile - Required spill file size, in bytes.
 */
typedef struct {
    char    ach[2];                 // Set to { '*', '\0' }
    long    cbFile;                 // Required spill file size
} FDISPILLFILE; /* fdisf */
typedef FDISPILLFILE *PFDISPILLFILE; /* pfdisf */

#pragma pack ()

/*** cpuType values for FDICreate()
 *
 *  WARNING: For 16-bit Windows applications, the CPU detection may not
 *           correctly detect 286 CPUs.  Instead, use the following code:
 *
 *              DWORD   flags;
 *              int     cpuType;
 *
 *              flags = GetWinFlags();
 *              if (flags & WF_CPU286)
 *                  cpuType = cpu80286;
 *              else
 *                  cpuType = cpu80386;
 *
 *              hfdi = FDICreate(....,cpuType,...);
 */
#define     cpuUNKNOWN         (-1)    /* FDI does detection */
#define     cpu80286           (0)     /* '286 opcodes only */
#define     cpu80386           (1)     /* '386 opcodes used */

/***    FDICreate - Create an FDI context
 *
 *  Entry:
 *      pfnalloc
 *      pfnfree
 *      pfnopen
 *      pfnread
 *      pfnwrite
 *      pfnclose
 *      pfnlseek
 *      cpuType  - Select CPU type (auto-detect, 286, or 386+)
 *                 WARNING: Don't use auto-detect from a 16-bit Windows
 *                          application!  Use GetWinFlags()!
 *                 NOTE: For the 32-bit FDI.LIB, this parameter is ignored!
 *      perf
 *
 *  Exit-Success:
 *      Returns non-NULL FDI context handle.
 *
 *  Exit-Failure:
 *      Returns NULL; perf filled in with error code
 *
 *  Special notes for Quantum Decompression:
 *      If you have used a high setting for CompressionMemory in creating
 *      the cabinet file(s), then FDI will attempt to allocate a lot of
 *      memory (as much as 2Mb, if you specified 21 for CompressionMemory).
 *      Therefore, if you plan to allocate additional memory *after* the
 *      FDICreate() call, you should reserve some memory *prior* to calling
 *      FDICreate(), and then free it up afterwards (or do all your allocation
 *      before calling FDICreate().
 */
HFDI FAR DIAMONDAPI FDICreate(PFNALLOC pfnalloc,
                              PFNFREE  pfnfree,
                              PFNOPEN  pfnopen,
                              PFNREAD  pfnread,
                              PFNWRITE pfnwrite,
                              PFNCLOSE pfnclose,
                              PFNSEEK  pfnseek,
                              int      cpuType,
                              PERF     perf);

/***    FDIIsCabinet - Determines if file is a cabinet, returns info if it is
 *
 *  Entry:
 *      hfdi   - Handle to FDI context (created by FDICreate())
 *      hf     - File handle suitable for PFNREAD/PFNSEEK, positioned
 *               at offset 0 in the file to test.
 *      pfdici - Buffer to receive info about cabinet if it is one.
 *
 *  Exit-Success:
 *      Returns TRUE; file is a cabinet, pfdici filled in.
 *
 *  Exit-Failure:
 *      Returns FALSE, file is not a cabinet;  If an error occurred,
 *          perf (passed on FDICreate call!) filled in with error.
 */
BOOL FAR DIAMONDAPI FDIIsCabinet(HFDI            hfdi,
                                 int             hf,
                                 PFDICABINETINFO pfdici);

/***    FDICopy - extracts files from a cabinet
 *
 *  Entry:
 *      hfdi        - handle to FDI context (created by FDICreate())
 *      pszCabinet  - main name of cabinet file
 *      pszCabPath  - Path to cabinet file(s)
 *      flags       - Flags to modify behavior
 *      pfnfdin     - Notification function
 *      pfnfdid     - Decryption function (pass NULL if not used)
 *      pvUser      - User specified value to pass to notification function
 *
 *  Exit-Success:
 *      Returns TRUE;
 *
 *  Exit-Failure:
 *      Returns FALSE, perf (passed on FDICreate call!) filled in with
 *          error.
 *
 *  Notes:
 *  (1) If FDICopy() fails while a target file is being written out, then
 *      FDI will use the PFNCLOSE function to close the file handle for that
 *      target file that was returned from the fdintCOPY_FILE notification.
 *      The client application is then free to delete the target file, since
 *      it will not be in a valid state (since there was an error while
 *      writing it out).
 */
BOOL FAR DIAMONDAPI FDICopy(HFDI          hfdi,
                            char FAR     *pszCabinet,
                            char FAR     *pszCabPath,
                            int           flags,
                            PFNFDINOTIFY  pfnfdin,
                            PFNFDIDECRYPT pfnfdid,
                            void FAR     *pvUser);

/***    FDIDestroy - Destroy an FDI context
 *
 *  Entry:
 *      hfdi - handle to FDI context (created by FDICreate())
 *
 *  Exit-Success:
 *      Returns TRUE;
 *
 *  Exit-Failure:
 *      Returns FALSE;
 */
BOOL FAR DIAMONDAPI FDIDestroy(HFDI hfdi);

//** Revert to default structure packing
#pragma pack()

#ifdef __cplusplus
}
#endif  /* __cplusplus */

#endif //!INCLUDED_FDI


FOLDER.CPP

//*******************************************************************************************
//
// Filename : folder.cpp
//
//CAB Files Shell Extension
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "thisdll.h"
#include "thisguid.h"

#include "SFView.H"
#include "folder.h"
#include "enum.h"
#include "view.h"
#include "icon.h"
#include "menu.h"
#include "dataobj.h"

#include "cabitms.h"

// *** IUnknown methods ***
STDMETHODIMP CCabFolder::QueryInterface(
   REFIID riid,
   LPVOID FAR* ppvObj)
{
*ppvObj = NULL;

LPVOID pObj;

if (riid == IID_IUnknown)
{
pObj = (IUnknown*)((IShellFolder*)this);
// The (IShellFolder*) ^^^ up there is to disambiguate :) the reference
}
else if (riid == IID_IShellFolder)
{
pObj = (IShellFolder*)this;
}
else if (riid == IID_IPersistFolder)
{
pObj = (IPersistFolder*)this;
}
else
{
   return(E_NOINTERFACE);
}

((LPUNKNOWN)pObj)->AddRef();
*ppvObj = pObj;

return(NOERROR);
}

STDMETHODIMP_(ULONG) CCabFolder::AddRef(void)
{
return(m_cRef.AddRef());
}

STDMETHODIMP_(ULONG) CCabFolder::Release(void)
{
if (!m_cRef.Release())
{
   delete this;
return(0);
}

return(m_cRef.GetRef());
}

// *** IParseDisplayName method ***
STDMETHODIMP CCabFolder::ParseDisplayName(
   HWND hwndOwner,
   LPBC pbc,
   LPOLESTR lpszDisplayName,
   ULONG FAR* pchEaten,
   LPITEMIDLIST * ppidl,
   ULONG *pdwAttributes)
{
return(E_NOTIMPL);
}

// *** IOleContainer methods ***
//**********************************************************************
//
// CCabFolder::EnumObjects
//
// Purpose:
//
//      Creates an item enumeration object
//      (an IEnumIDList interface) that can be used to
//      enumerate the contents of a folder.
//
// Parameters:
//
//       HWND hwndOwner       -    handle to the owner window
//       DWORD grFlags        -    flags about which items to include
//       LPENUMIDLIST *ppenumIDList - address that receives IEnumIDList
//                                    interface pointer
//
//
// Comments:
//
//
//********************************************************************

STDMETHODIMP CCabFolder::EnumObjects(
   HWND hwndOwner,
   DWORD grfFlags,
   LPENUMIDLIST * ppenumIDList) // LPENUMUNKNOWN FAR* ppenumUnknown)
{
CEnumCabObjs *pce = new CEnumCabObjs(this, grfFlags);
if (!pce)
{
return(E_OUTOFMEMORY);
}

pce->AddRef();
HRESULT hRes = pce->QueryInterface(IID_IEnumIDList, (LPVOID*)ppenumIDList);
pce->Release();

return(hRes);
}

// *** IShellFolder methods ***
// subfolders not implemented
STDMETHODIMP CCabFolder::BindToObject(
   LPCITEMIDLIST pidl,
   LPBC pbc,
   REFIID riid,
   LPVOID FAR* ppvObj)
{
return(E_NOTIMPL);
}

STDMETHODIMP CCabFolder::BindToStorage(
   LPCITEMIDLIST pidl,
   LPBC pbc,
   REFIID riid,
   LPVOID FAR* ppvObj)
{
return(E_NOTIMPL);
}

//**********************************************************************
//
// CCabFolder::CompareIDs
//
// Purpose:
//
//      Determines the relative ordering of two file
//      objects or folders, given their item identifier lists
//
// Parameters:
//
//      LPARAM lParam         -    type of comparison
//      LPCITEMIDLIST pidl1   -    address to ITEMIDLIST
//      LPCITEMIDLIST pidl2   -    address to ITEMIDLIST
//
//
// Comments:
//
//
//********************************************************************

STDMETHODIMP CCabFolder::CompareIDs(
   LPARAM lParam,
   LPCITEMIDLIST pidl1,
   LPCITEMIDLIST pidl2)
{
LPCABITEM pit1 = (LPCABITEM)pidl1;
LPCABITEM pit2 = (LPCABITEM)pidl2;

short nCmp = 0;

switch (lParam)
{
case CV_COL_NAME:
break;

case CV_COL_SIZE:
if (pit1->dwFileSize < pit2->dwFileSize)
{
nCmp = -1;
}
else if (pit1->dwFileSize > pit2->dwFileSize)
{
nCmp = 1;
}
break;

case CV_COL_TYPE:
{
STRRET srName1, srName2;

GetTypeOf(pit1, &srName1);
GetTypeOf(pit2, &srName2);

nCmp = (SHORT)lstrcmp(srName1.cStr, srName2.cStr);
break;
}

case CV_COL_MODIFIED:
FILETIME ft1, ft2;

DosDateTimeToFileTime(pit1->uFileDate, pit1->uFileTime, &ft1);
DosDateTimeToFileTime(pit2->uFileDate, pit2->uFileTime, &ft2);

nCmp = (SHORT)CompareFileTime(&ft1, &ft2);
break;

default:
break;
}

if (nCmp != 0)
{
return(ResultFromShort(nCmp));
}

return(ResultFromShort(lstrcmpi(pit1->szName, pit2->szName)));
}

//**********************************************************************
//
// CCabFolder::CreateViewObject
//
// Purpose:
//
//      IShellbrowser calls this to create a ShellView
//      object
//
// Parameters:
//
//      HWND   hwndOwner     -
//
//      REFIID riid          -  interface ID
//
//      LPVOID * ppvObj      -  pointer to the Shellview object
//
// Return Value:
//
//      NOERROR
//      E_OUTOFMEMORY
//      E_NOINTERFACE
//
//
// Comments:
//
//      ShellBrowser interface calls this to request the ShellFolder
//      to create a ShellView object
//
//********************************************************************

STDMETHODIMP CCabFolder::CreateViewObject(
   HWND hwndOwner,
   REFIID riid,
   LPVOID FAR* ppvObj)
{
IUnknown *pObj = NULL;

if (riid == IID_IShellView)
{

// Create a call back for the ShellView

IShellFolderViewCallback *pcb;
HRESULT hRes = CabView_CreateCallback(&pcb);

hRes = CreateShellFolderView(this, pcb, (LPSHELLVIEW FAR*)&pObj);
if (pcb)
{
// The ShellFolderView should have AddRef'ed if it needed it.
pcb->Release();
}

if (FAILED(hRes))
{
return(hRes);
}
}
else
{
return(E_NOINTERFACE);
}

if (!pObj)
{
return(E_OUTOFMEMORY);
}

// The ref count is already 1
HRESULT hRes = pObj->QueryInterface(riid, ppvObj);
pObj->Release();

return(NOERROR);
}

// **************************************************************************************
//
// CCabFolder::GetAttributesOf
//
// Purpose
//
//Retrieves attributes of one of more file objects
//
// Parameters:
//
//    UINT cidl                -    number of file objects
//    LPCITEMIDLIST  *apidl    -    pointer to array of ITEMIDLIST
//    ULONG *rgfInOut          -    array of values that specify file object
//                                  attributes
//
//
// Return Value:
//
//     NOERROR
//
//Comments
//
// ***************************************************************************************

STDMETHODIMP CCabFolder::GetAttributesOf(
   UINT cidl,
   LPCITEMIDLIST FAR* apidl,
   ULONG FAR* rgfInOut)
{
*rgfInOut &= SFGAO_CANCOPY;

return(NOERROR);
}

// **************************************************************************************
//
// CCabFolder::GetUIObjectOf
//
// Purpose
//
//Returns an interface that can be used to carry out actions on
//          the specified file objects or folders
//
// Parameters:
//
//        HWND hwndOwner        -    handle of the Owner window
//
//        UINT cidl             -    Number of file objects
//
//        LPCITEMIDLIST *apidl  -    array of file object pidls
//
//        REFIID                -    Identifier of interface to return
//
//        UINT * prgfInOut      -    reserved
//
//        LPVOID *ppvObj        -    address that receives interface pointer
//
// Return Value:
//
//         E_INVALIDARG
//         E_NOINTERFACE
//         E_OUTOFMEMORY
//
//Comments
// ***************************************************************************************

STDMETHODIMP CCabFolder::GetUIObjectOf(
   HWND hwndOwner,
   UINT cidl,
   LPCITEMIDLIST FAR* apidl,
   REFIID riid,
   UINT FAR* prgfInOut,
   LPVOID FAR* ppvObj)
{
LPUNKNOWN pObj = NULL;

if (riid == IID_IExtractIcon)
{
if (cidl != 1)
{
return(E_INVALIDARG);
}

LPCABITEM pci = (LPCABITEM)*apidl;
pObj = (LPUNKNOWN)(IExtractIcon *)(new CCabItemIcon(pci->szName));
}
else if (riid == IID_IContextMenu)
{
if (cidl < 1)
{
return(E_INVALIDARG);
}

pObj = (LPUNKNOWN)(IContextMenu *)(new CCabItemMenu(hwndOwner, this,
(LPCABITEM *)apidl, cidl));
}
else if (riid == IID_IDataObject)
{
if (cidl < 1)
{
return(E_INVALIDARG);
}

pObj = (LPUNKNOWN)(IDataObject *)(new CCabObj(hwndOwner, this,
(LPCABITEM *)apidl, cidl));
}
else
{
return(E_NOINTERFACE);
}

if (!pObj)
{
return(E_OUTOFMEMORY);
}

pObj->AddRef();
HRESULT hRes = pObj->QueryInterface(riid, ppvObj);
pObj->Release();

return(hRes);
}

//*****************************************************************************
//
// CCabFolder::GetDisplayNameOf
//
// Purpose:
//        Retrieves the display name for the specified file object or
//        subfolder.
//
//
// Parameters:
//
//        LPCITEMIDLIST    pidl    -    pidl of the file object
//        DWORD  dwReserved        -    Value of the type of display name to
//                                      return
//        LPSTRRET  lpName         -    address holding the name returned
//
//
// Comments:
//
//*****************************************************************************

STDMETHODIMP CCabFolder::GetDisplayNameOf(
   LPCITEMIDLIST pidl,
   DWORD dwReserved,
   LPSTRRET lpName)
{
LPCABITEM pit = (LPCABITEM)pidl;

GetNameOf(pit, lpName);

return(NOERROR);
}

STDMETHODIMP CCabFolder::SetNameOf(
   HWND hwndOwner,
   LPCITEMIDLIST pidl,
   LPCOLESTR lpszName,
   DWORD dwReserved,
   LPITEMIDLIST FAR* ppidlOut)
{
return(E_NOTIMPL);
}

  // *** IPersist methods ***
//**********************************************************************
//
// CCabFolder::GetClassID
//
// Purpose:
//
//      Return the class id
//
// Parameters:
//
//      LPCLSID lpClassID       -   pointer to the ClassID member
//
// Return Value:
//
//      NOERROR
//
//
// Comments:
//
//      This routine returns the Class ID for the DLL
//
//********************************************************************

STDMETHODIMP CCabFolder::GetClassID(
   LPCLSID lpClassID)
{
*lpClassID = CLSID_ThisDll;
return NOERROR;
}

// *** IPersistFolder methods ***
//**********************************************************************
//
// CCabFolder::Initialize folder
//
// Purpose:
//
//      Explorer calls this while initializing the ShellFolder
//      object
//
// Parameters:
//
//      LPCITEMIDLIST pidl-   pidl passed by IShellBrowser
//
// Return Value:
//
//      S_OK
//
//
// Comments:
//
//      This routine is called by Explorer during initialization
//
//********************************************************************

STDMETHODIMP CCabFolder::Initialize(
   LPCITEMIDLIST pidl)
{
if (m_pidlHere)
{
ILFree(m_pidlHere);
}

// Clone the pidl passed by the explorer

m_pidlHere = ILClone(pidl);
if (!m_pidlHere)
{
return(E_OUTOFMEMORY);
}

return(S_OK);
}

//*****************************************************************************
//
// CCabFolder::CreateIDList
//
// Purpose:
//
//    Creates an item identifier list for the objects in the namespace
//
//
//*****************************************************************************

LPITEMIDLIST CCabFolder::CreateIDList(LPCSTR pszName, DWORD dwFileSize,
UINT uFileDate, UINT uFileTime, UINT uFileAttribs)
{
// We'll assume no name is longer than MAX_PATH
// Note the terminating NULL is already in the sizeof(CABITEM)
BYTE bBuf[sizeof(CABITEM) + MAX_PATH + sizeof(WORD)];
LPCABITEM pci = (LPCABITEM)bBuf;

UINT uNameLen = lstrlen(pszName);
if (uNameLen >= MAX_PATH)
{
uNameLen = MAX_PATH;
}

pci->wSize = (WORD)(sizeof(CABITEM) + uNameLen);
pci->dwFileSize = dwFileSize;
pci->uFileDate = (USHORT)uFileDate;
pci->uFileTime = (USHORT)uFileTime;
pci->uFileAttribs = (USHORT)uFileAttribs;
lstrcpyn(pci->szName, pszName, uNameLen+1);

// Terminate the IDList
*(WORD *)(((LPSTR)pci)+pci->wSize) = 0;

return(ILClone((LPCITEMIDLIST)pci));
}

//*****************************************************************************
//
// CCabFolder::GetPath
//
// Purpose:
//
//        Get the Path for the current pidl
//
// Parameters:
//
//        LPSTR szPath        -    return pointer for path string
//
// Comments:
//
//*****************************************************************************

BOOL CCabFolder::GetPath(LPSTR szPath)
{
if (!m_pidlHere || !SHGetPathFromIDList(m_pidlHere, szPath))
{
*szPath = '\0';
return(FALSE);
}

return(TRUE);
}

void CCabFolder::GetNameOf(LPCABITEM pit, LPSTRRET lpName)
{
lpName->uType = STRRET_OFFSET;
lpName->uOffset = FIELDOFFSET(CABITEM, szName);

SHFILEINFO sfi;

if (SHGetFileInfo(pit->szName, 0, &sfi, sizeof(sfi),
SHGFI_USEFILEATTRIBUTES | SHGFI_DISPLAYNAME)
&& lstrcmp(sfi.szDisplayName, pit->szName) != 0)
{
lpName->uType = STRRET_CSTR;
lstrcpy(lpName->cStr, sfi.szDisplayName);
}
}

void CCabFolder::GetTypeOf(LPCABITEM pit, LPSTRRET lpName)
{
lpName->uType = STRRET_CSTR;
lpName->cStr[0] = '\0';

SHFILEINFO sfi;

if (SHGetFileInfo(pit->szName, 0, &sfi, sizeof(sfi),
SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME))
{
lstrcpy(lpName->cStr, sfi.szTypeName);
}
}
//*****************************************************************************
//
// CCabFolder::EnumToList
//
// Purpose:
//
//       This notify callback is called by the FDI routines. It adds the
//       file object from the cab file to the list.
//
// Parameters:
//
//
//
// Comments:
//
//*****************************************************************************

void CALLBACK CCabFolder::EnumToList(LPCSTR pszFile, DWORD dwSize, UINT date,
UINT time, UINT attribs, LPARAM lParam)
{
CCabFolder *pThis = (CCabFolder *)lParam;

pThis->m_lItems.AddItem(pszFile, dwSize, date, time, attribs);
}

HRESULT CCabFolder::InitItems()
{
switch (m_lItems.GetState())
{
case CCabItemList::State_Init:
return(NOERROR);

case CCabItemList::State_OutOfMem:
return(E_OUTOFMEMORY);

case CCabItemList::State_UnInit:
default:
break;
}

// Force the list to initialize
m_lItems.InitList();

char szHere[MAX_PATH];

// the m_pidl has been set to current dir
// get the path to the current directory
if (!GetPath(szHere))
{
return(E_UNEXPECTED);
}

CCabItems ciHere(szHere);

if (!ciHere.EnumItems(EnumToList, (LPARAM)this))
{
return(E_UNEXPECTED);
}

return(NOERROR);
}

//*******************************************************************************************
//
// CreateInstance
//
// Purpose:
//
//Create a CCabFolder object and returns it
//
// Parameters:
//REFIID riid-a reference to the interface that is
//being queried
//
//LPVOID *ppvObj-an out parameter to return a pointer to
//interface being queried
//
//*******************************************************************************************

HRESULT CreateInstance(REFIID riid, LPVOID *ppvObj)
{
IUnknown *pObj = NULL;
*ppvObj = NULL;

if(riid == IID_IPersistFolder)
{
pObj = (IUnknown *)(IPersistFolder *)(new CCabFolder);
}
else if(riid == IID_IShellFolder)
{
pObj = (IUnknown *)(IShellFolder *)(new CCabFolder);
}
else
{
return(E_NOINTERFACE);
}

if (!pObj)
{
return(E_OUTOFMEMORY);
}

pObj->AddRef();
HRESULT hRes = pObj->QueryInterface(riid, ppvObj);
pObj->Release();

return(hRes);
}

UINT CCabItemList::GetState()
{
if (m_uStep == 0)
{
if (m_dpaList)
{
return(State_Init);
}

return(State_OutOfMem);
}

return(State_UnInit);
}

BOOL CCabItemList::StoreItem(LPITEMIDLIST pidl)
{
if (pidl)
{
if (InitList() && DPA_InsertPtr(m_dpaList, 0x7fff, (LPSTR)pidl)>=0)
{
return(TRUE);
}

ILFree(pidl);
}

CleanList();
return(FALSE);
}

BOOL CCabItemList::AddItems(LPCABITEM *apit, UINT cpit)
{
for (UINT i=0; i<cpit; ++i)
{
if (!StoreItem(ILClone((LPCITEMIDLIST)apit[i])))
{
return(FALSE);
}
}

return(TRUE);
}

BOOL CCabItemList::AddItem(LPCSTR pszName, DWORD dwFileSize,
UINT uFileDate, UINT uFileTime, UINT uFileAttribs)
{
return(StoreItem(CCabFolder::CreateIDList(pszName, dwFileSize, uFileDate, uFileTime,
uFileAttribs)));
}

int CCabItemList::FindInList(LPCSTR pszName, DWORD dwFileSize,
UINT uFileDate, UINT uFileTime, UINT uFileAttribs)
{
// TODO: Linear search for now; binary later
for (int i=DPA_GetPtrCount(m_dpaList)-1; i>=0; --i)
{
if (lstrcmpi(pszName, (*this)[i]->szName) == 0)
{
break;
}
}

return(i);
}

BOOL CCabItemList::InitList()
{
switch (GetState())
{
case State_Init:
return(TRUE);

case State_OutOfMem:
return(FALSE);

case State_UnInit:
default:
m_dpaList = DPA_Create(m_uStep);
m_uStep = 0;

return(InitList());
}
}

void CCabItemList::CleanList()
{
if (m_uStep != 0)
{
m_dpaList = NULL;
m_uStep = 0;
return;
}

if (!m_dpaList)
{
return;
}

for (int i=DPA_GetPtrCount(m_dpaList)-1; i>=0; --i)
{
ILFree((LPITEMIDLIST)((*this)[i]));
}

DPA_Destroy(m_dpaList);
m_dpaList = NULL;
}


FOLDER.H

//*******************************************************************************************
//
// Filename : Folder.h
//
//Definitions of CCabFolder and CCabItemList
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _CABFOLD_H_
#define _CABFOLD_H_

enum _CV_COLS
{
CV_COL_NAME = 0,
CV_COL_SIZE,
CV_COL_TYPE,
CV_COL_MODIFIED,
CV_COL_MAX,
} ;

typedef struct _CABITEM
{
WORD wSize;
DWORD dwFileSize;
USHORT uFileDate;
USHORT uFileTime;
USHORT uFileAttribs;
char szName[1];
} CABITEM, *LPCABITEM;

class CCabItemList
{
public:
CCabItemList(UINT uStep) {m_uStep=uStep;}
CCabItemList() {CCabItemList(8);}
~CCabItemList() {CleanList();}

enum
{
State_UnInit,
State_Init,
State_OutOfMem,
};

UINT GetState();

LPCABITEM operator[](UINT nIndex)
{
return((LPCABITEM)DPA_GetPtr(m_dpaList, nIndex));
}
UINT GetCount() {return(GetState()==State_Init ? DPA_GetPtrCount(m_dpaList) : 0);}

BOOL InitList();

BOOL AddItems(LPCABITEM *apit, UINT cpit);
BOOL AddItem(LPCSTR pszName, DWORD dwFileSize,
UINT uFileDate, UINT uFileTime, UINT uFileAttribs);

int FindInList(LPCSTR pszName, DWORD dwFileSize,
UINT uFileDate, UINT uFileTime, UINT uFileAttribs);
BOOL IsInList(LPCSTR pszName, DWORD dwFileSize,
UINT uFileDate, UINT uFileTime, UINT uFileAttribs)
{
return(FindInList(pszName, dwFileSize, uFileDate, uFileTime, uFileAttribs) >= 0);
}

private:
BOOL StoreItem(LPITEMIDLIST pidl);
void CleanList();

private:
UINT m_uStep;
HDPA m_dpaList;
} ;

class CCabFolder : public IPersistFolder, public IShellFolder
{
public:
CCabFolder() : m_pidlHere(0), m_lItems(1024/sizeof(LPVOID)) {}
~CCabFolder()
{
if (m_pidlHere)
{
ILFree(m_pidlHere);
}
}

// *** IUnknown methods ***
STDMETHODIMP QueryInterface(
REFIID riid,
LPVOID FAR* ppvObj);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);

// *** IParseDisplayName method ***
STDMETHODIMP ParseDisplayName(
HWND hwndOwner,
LPBC pbc,
LPOLESTR lpszDisplayName,
ULONG FAR* pchEaten,
LPITEMIDLIST * ppidl,
ULONG *pdwAttributes);

// *** IOleContainer methods ***
STDMETHODIMP EnumObjects(
HWND hwndOwner,
DWORD grfFlags,
LPENUMIDLIST * ppenumIDList);

// *** IShellFolder methods ***
STDMETHODIMP BindToObject(
LPCITEMIDLIST pidl,
LPBC pbc,
REFIID riid,
LPVOID FAR* ppvObj);
STDMETHODIMP BindToStorage(
LPCITEMIDLIST pidl,
LPBC pbc,
REFIID riid,
LPVOID FAR* ppvObj);
STDMETHODIMP CompareIDs(
LPARAM lParam,
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2);
STDMETHODIMP CreateViewObject(
HWND hwndOwner,
REFIID riid,
LPVOID FAR* ppvObj);
STDMETHODIMP GetAttributesOf(
UINT cidl,
LPCITEMIDLIST FAR* apidl,
ULONG FAR* rgfInOut);
STDMETHODIMP GetUIObjectOf(
HWND hwndOwner,
UINT cidl,
LPCITEMIDLIST FAR* apidl,
REFIID riid,
UINT FAR* prgfInOut,
LPVOID FAR* ppvObj);
STDMETHODIMP GetDisplayNameOf(
LPCITEMIDLIST pidl,
DWORD dwReserved,
LPSTRRET lpName);
STDMETHODIMP SetNameOf(
HWND hwndOwner,
LPCITEMIDLIST pidl,
LPCOLESTR lpszName,
DWORD dwReserved,
LPITEMIDLIST FAR* ppidlOut);

// *** IPersist methods ***
STDMETHODIMP GetClassID(
LPCLSID lpClassID);
// *** IPersistFolder methods ***
STDMETHODIMP Initialize(
LPCITEMIDLIST pidl);

public:
static LPITEMIDLIST CreateIDList(LPCSTR pszName, DWORD dwFileSize,
UINT uFileDate, UINT uFileTime, UINT uFileAttribs);
static void GetNameOf(LPCABITEM pit, LPSTRRET lpName);
static void GetTypeOf(LPCABITEM pit, LPSTRRET lpName);

BOOL GetPath(LPSTR szPath);

private:
static void CALLBACK EnumToList(LPCSTR pszFile, DWORD dwSize, UINT date,
UINT time, UINT attribs, LPARAM lParam);

HRESULT InitItems();

private:
CRefDll m_cRefDll;

CRefCount m_cRef;

LPITEMIDLIST m_pidlHere;// maintains the current pidl

CCabItemList m_lItems;

friend class CEnumCabObjs;
} ;

#endif // _CABFOLD_H_


ICON.CPP

//*******************************************************************************************
//
// Filename : Icon.cpp
//
//Implementation file for CCabItemIcon
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "thisdll.h"

#include "path.h"
#include "icon.h"

// *** IUnknown methods ***
STDMETHODIMP CCabItemIcon::QueryInterface(
   REFIID riid,
   LPVOID FAR* ppvObj)
{
*ppvObj = NULL;

LPUNKNOWN pObj;

if (riid == IID_IUnknown)
{
pObj = (LPUNKNOWN)((IExtractIcon*)this);
}
else if (riid == IID_IExtractIcon)
{
pObj = (LPUNKNOWN)((IExtractIcon*)this);
}
else
{
   return(E_NOINTERFACE);
}

pObj->AddRef();
*ppvObj = (LPVOID)pObj;

return(NOERROR);
}

STDMETHODIMP_(ULONG) CCabItemIcon::AddRef(void)
{
return(m_cRef.AddRef());
}

STDMETHODIMP_(ULONG) CCabItemIcon::Release(void)
{
if (!m_cRef.Release())
{
   delete this;
return(0);
}

return(m_cRef.GetRef());
}

const char c_szCabViewLoc[] = "CABVIEW::";

// *** IExtractIcon methods ***
STDMETHODIMP CCabItemIcon::GetIconLocation(
UINT   uFlags,
LPSTR  szIconFile,
UINT   cchMax,
int   * piIndex,
UINT  * pwFlags)
{
LPCSTR pszExt = PathFindExtension(m_szName);
if (!pszExt || !pszExt[0])
{
return(E_UNEXPECTED);
}

char szClass[80];
LONG lLen = sizeof(szClass);

if (RegQueryValue(HKEY_CLASSES_ROOT, pszExt, szClass, &lLen) != ERROR_SUCCESS)
{
// This extension has no icon
return(E_UNEXPECTED);
}

if (cchMax < sizeof(c_szCabViewLoc) + lstrlen(pszExt))
{
return(E_INVALIDARG);
}

lstrcpy(szIconFile, c_szCabViewLoc);
lstrcpy(szIconFile+(sizeof(c_szCabViewLoc)-1), pszExt);
CharUpper(szIconFile);

*piIndex = uFlags&GIL_OPENICON ? 1 : 0;
*pwFlags = GIL_PERCLASS | GIL_NOTFILENAME;

return(NOERROR);
}

STDMETHODIMP CCabItemIcon::Extract(
LPCSTR   pszFile,
UINT nIconIndex,
HICON   *phiconLarge,
HICON   *phiconSmall,
UINT     nIconSize)
{
UINT uFlags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES;

switch (nIconIndex)
{
case 0:
break;

case 1:
uFlags |= SHGFI_OPENICON;
break;

default:
return(E_INVALIDARG);
}

char szExt[sizeof(c_szCabViewLoc) + 5];

lstrcpyn(szExt, pszFile, sizeof(szExt));
szExt[sizeof(c_szCabViewLoc)-1] = '\0';
if (lstrcmp(szExt, c_szCabViewLoc) != 0)
{
return(E_INVALIDARG);
}

lstrcpyn(szExt, pszFile+(sizeof(c_szCabViewLoc)-1), sizeof(szExt));
LPSTR pszExt = PathFindExtension(m_szName);
if (lstrcmpi(szExt, pszExt) != 0)
{
return(E_INVALIDARG);
}

SHFILEINFO sfi;

if (!SHGetFileInfo(m_szName, 0, &sfi, sizeof(sfi), uFlags | SHGFI_LARGEICON))
{
return(E_UNEXPECTED);
}

*phiconLarge = sfi.hIcon;

if (SHGetFileInfo(m_szName, 0, &sfi, sizeof(sfi), uFlags | SHGFI_SMALLICON))
{
*phiconSmall = sfi.hIcon;
}
else
{
*phiconSmall = NULL;
}

return(S_OK);
}


ICON.H

//*******************************************************************************************
//
// Filename : Icon.h
//
//Definition of CCabItemIcon
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _ICON_H_
#define _ICON_H_

class CCabItemIcon : public IExtractIcon
{
public:
CCabItemIcon(LPCSTR pszName) {lstrcpyn(m_szName, pszName, sizeof(m_szName));}
~CCabItemIcon() {}

// *** IUnknown methods ***
STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();

    // *** IExtractIcon methods ***
    STDMETHODIMP GetIconLocation(
UINT   uFlags,
LPSTR  szIconFile,
UINT   cchMax,
int   * piIndex,
UINT  * pwFlags);

    STDMETHODIMP Extract(
LPCSTR   pszFile,
UINT nIconIndex,
HICON   *phiconLarge,
HICON   *phiconSmall,
UINT     nIconSize);

private:
CRefDll m_cRefDll;

CRefCount m_cRef;

char m_szName[MAX_PATH];
} ;

#endif // _ICON_H_


ISHELL2.H

//*******************************************************************************************
//
// Filename : IShell2.h
//
//Shell_MessageBox and Shell_MergeMenu
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _ISHELL2_H_
#define _ISHELL2_H_

#ifdef __cplusplus
extern "C" {
#endif
//----------------------------------------------------------------------------
// **** helper macro
//----------------------------------------------------------------------------

#define _IOffset(class, itf)         ((UINT)&(((class *)0)->itf))
#define IToClass(class, itf, pitf)   ((class  *)(((LPSTR)pitf)-_IOffset(class, itf)))
#define IToClassN(class, itf, pitf)  IToClass(class, itf, pitf)

//===========================================================================
// ITEMIDLIST
//===========================================================================

// unsafe macros from shesimp.h & idlist.c
#define _ILSkip(pidl, cb)       ((LPITEMIDLIST)(((BYTE*)(pidl))+cb))
#define _ILNext(pidl)           _ILSkip(pidl, (pidl)->mkid.cb)
#ifdef DEBUG
// Dugging aids for making sure we dont use free pidls
#define VALIDATE_PIDL(pidl) Assert((pidl)->mkid.cb != 0xC5C5)
#else
#define VALIDATE_PIDL(pidl)
#endif

void        ILFree(LPITEMIDLIST pidl);
LPITEMIDLIST ILClone(LPCITEMIDLIST pidl);
UINT ILGetSize(LPCITEMIDLIST pidl);

typedef void (WINAPI FAR* RUNDLLPROC)(HWND hwndStub,
        HINSTANCE hAppInstance,
        LPSTR lpszCmdLine, int nCmdShow);

UINT  Shell_MergeMenus(HMENU hmDst, HMENU hmSrc, UINT uInsert, UINT uIDAdjust, UINT uIDAdjustMax, ULONG uFlags);

//===================================================================
// Shell_MergeMenu parameter
//
#define MM_ADDSEPARATOR0x00000001L
#define MM_SUBMENUSHAVEIDS0x00000002L

#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))

//====== ShellMessageBox ================================================

// If lpcTitle is NULL, the title is taken from hWnd
// If lpcText is NULL, this is assumed to be an Out Of Memory message
// If the selector of lpcTitle or lpcText is NULL, the offset should be a
//     string resource ID
// The variable arguments must all be 32-bit values (even if fewer bits
//     are actually used)
// lpcText (or whatever string resource it causes to be loaded) should
//     be a formatting string similar to wsprintf except that only the
//     following formats are available:
//         %%              formats to a single '%'
//         %nn%s           the nn-th arg is a string which is inserted
//         %nn%ld          the nn-th arg is a DWORD, and formatted decimal
//         %nn%lx          the nn-th arg is a DWORD, and formatted hex
//     note that lengths are allowed on the %s, %ld, and %lx, just
//                         like wsprintf /* ;Internal */
//
//int _cdecl ShellMessageBox(HINSTANCE hAppInst, HWND hWnd, LPCSTR
//        lpcText, LPCSTR lpcTitle, UINT fuStyle, ...);

#define STRRET_OLESTR   0x0000

#ifdef __cplusplus
}
#endif

#endif // _ISHELL2_H_


IUTIL.C

//*******************************************************************************************
//
// Filename : IUtil.c
//
//Implementation of Cab_MergeMenus and some other useful
//              routines
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"
#include "Cabvw2.h"

// Copy a menu onto the beginning or end of another menu
// Adds uIDAdjust to each menu ID (pass in 0 for no adjustment)
// Will not add any item whose adjusted ID is greater than uMaxIDAdjust
// (pass in 0xffff to allow everything)
// Returns one more than the maximum adjusted ID that is used
//

BOOL _SHIsMenuSeparator(HMENU hm, int i)
{
        MENUITEMINFO mii;

        mii.cbSize = sizeof(MENUITEMINFO);
        mii.fMask = MIIM_TYPE;
        mii.cch = 0;    // WARNING: We MUST initialize it to 0!!!
        if (!GetMenuItemInfo(hm, i, TRUE, &mii))
        {
                return(FALSE);
        }

        if (mii.fType & MFT_SEPARATOR)
        {
                return(TRUE);
        }

        return(FALSE);
}

UINT Cab_MergeMenus(HMENU hmDst, HMENU hmSrc, UINT uInsert, UINT uIDAdjust, UINT uIDAdjustMax, ULONG uFlags)
{
        int nItem;
        HMENU hmSubMenu;
        BOOL bAlreadySeparated;
        MENUITEMINFO miiSrc;
        char szName[256];
        UINT uTemp, uIDMax = uIDAdjust;

        if (!hmDst || !hmSrc)
        {
                goto MM_Exit;
        }

        nItem = GetMenuItemCount(hmDst);
        if (uInsert >= (UINT)nItem)
        {
                uInsert = (UINT)nItem;
                bAlreadySeparated = TRUE;
        }
        else
        {
                bAlreadySeparated = _SHIsMenuSeparator(hmDst, uInsert);;
        }

        if ((uFlags & MM_ADDSEPARATOR) && !bAlreadySeparated)
        {
                // Add a separator between the menus
                InsertMenu(hmDst, uInsert, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
                bAlreadySeparated = TRUE;
        }

        // Go through the menu items and clone them
        for (nItem = GetMenuItemCount(hmSrc) - 1; nItem >= 0; nItem--)
        {
        miiSrc.cbSize = sizeof(MENUITEMINFO);
        miiSrc.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_TYPE | MIIM_DATA;
                // We need to reset this every time through the loop in case
                // menus DON'T have IDs
                miiSrc.fType = MFT_STRING;
                miiSrc.dwTypeData = szName;
                miiSrc.dwItemData = 0;
                miiSrc.cch        = sizeof(szName);

                if (!GetMenuItemInfo(hmSrc, nItem, TRUE, &miiSrc))
                {
                        continue;
                }

                if (miiSrc.fType & MFT_SEPARATOR)
                {
                        // This is a separator; don't put two of them in a row
                        if (bAlreadySeparated)
                        {
                                continue;
                        }

                        bAlreadySeparated = TRUE;
                }
                else if (miiSrc.hSubMenu)
                {
                        if (uFlags & MM_SUBMENUSHAVEIDS)
                        {
                                // Adjust the ID and check it
                                miiSrc.wID += uIDAdjust;
                                if (miiSrc.wID > uIDAdjustMax)
                                {
                                        continue;
                                }

                                if (uIDMax <= miiSrc.wID)
                                {
                                        uIDMax = miiSrc.wID + 1;
                                }
                        }
                        else
                        {
                                // Don't set IDs for submenus that didn't have
                                // them already
                                miiSrc.fMask &= ~MIIM_ID;
                        }

                        hmSubMenu = miiSrc.hSubMenu;
                        miiSrc.hSubMenu = CreatePopupMenu();
                        if (!miiSrc.hSubMenu)
                        {
                                goto MM_Exit;
                        }

                        uTemp = Cab_MergeMenus(miiSrc.hSubMenu, hmSubMenu, 0, uIDAdjust,
                                uIDAdjustMax, uFlags&MM_SUBMENUSHAVEIDS);
                        if (uIDMax <= uTemp)
                        {
                                uIDMax = uTemp;
                        }

                        bAlreadySeparated = FALSE;
                }
                else
                {
                        // Adjust the ID and check it
                        miiSrc.wID += uIDAdjust;
                        if (miiSrc.wID > uIDAdjustMax)
                        {
                                continue;
                        }

                        if (uIDMax <= miiSrc.wID)
                        {
                                uIDMax = miiSrc.wID + 1;
                        }

                        bAlreadySeparated = FALSE;
                }

                if (!InsertMenuItem(hmDst, uInsert, TRUE, &miiSrc))
                {
                        goto MM_Exit;
                }
        }

        // Ensure the correct number of separators at the beginning of the
        // inserted menu items
        if (uInsert == 0)
        {
                if (bAlreadySeparated)
                {
                        DeleteMenu(hmDst, uInsert, MF_BYPOSITION);
                }
        }
        else
        {
                if (_SHIsMenuSeparator(hmDst, uInsert-1))
                {
                        if (bAlreadySeparated)
                        {
                                DeleteMenu(hmDst, uInsert, MF_BYPOSITION);
                        }
                }
                else
                {
                        if ((uFlags & MM_ADDSEPARATOR) && !bAlreadySeparated)
                        {
                                // Add a separator between the menus
                                InsertMenu(hmDst, uInsert, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
                        }
                }
        }

MM_Exit:
        return(uIDMax);
}

int OleStrToStrN(LPSTR psz, int cchMultiByte, LPCOLESTR pwsz, int cchWideChar)
{
    return WideCharToMultiByte(CP_ACP, 0, pwsz, cchWideChar, psz, cchMultiByte, NULL, NULL);
}

int OleStrToStr(LPSTR psz, LPCOLESTR pwsz)
{
    return WideCharToMultiByte(CP_ACP, 0, pwsz, -1, psz, MAX_PATH, NULL, NULL);
}

int StrToOleStrN(LPOLESTR pwsz, int cchWideChar, LPCSTR psz, int cchMultiByte)
{
    return MultiByteToWideChar(CP_ACP, 0, psz, cchMultiByte, pwsz, cchWideChar);
}

int StrToOleStr(LPOLESTR pwsz, LPCSTR psz)
{
    return MultiByteToWideChar(CP_ACP, 0, psz, -1, pwsz, MAX_PATH);
}

/*
 *
 */

UINT ILGetSize(LPCITEMIDLIST pidl)
{
    UINT cbTotal = 0;
    if (pidl)
    {
        VALIDATE_PIDL(pidl);
cbTotal += sizeof(pidl->mkid.cb);// Null terminator
while (pidl->mkid.cb)
{
    cbTotal += pidl->mkid.cb;
    pidl = _ILNext(pidl);
}
    }

    return cbTotal;
}

LPITEMIDLIST ILClone(LPCITEMIDLIST pidl)
{
    UINT cb = ILGetSize(pidl);
    LPMALLOC pMalloc;
LPITEMIDLIST pidlRet;

SHGetMalloc(&pMalloc);
pidlRet = (LPITEMIDLIST)pMalloc->lpVtbl->Alloc(pMalloc, cb);
pMalloc->lpVtbl->Release(pMalloc);

    if (pidlRet)
    {
// Notes: no need to zero-init.
hmemcpy(pidlRet, pidl, cb);
    }
    return pidlRet;
}

void ILFree(LPITEMIDLIST pidl)
{
LPMALLOC pMalloc;

    if (pidl)
    {
#ifdef DEBUG
        UINT cbSize = ILGetSize(pidl);
        VALIDATE_PIDL(pidl);

        // Fill the memory with some bad value...
        _fmemset(pidl, 0xE5, cbSize);

        // If large enough try to save the call return address of who
        // freed us in the 3-6 byte of the structure.
        if (cbSize >= 6)
            *((UINT*)((LPSTR)pidl + 2)) =  *(((UINT*)&pidl) - 1);

#endif
SHGetMalloc(&pMalloc);
pMalloc->lpVtbl->Free(pMalloc, pidl);
pMalloc->lpVtbl->Release(pMalloc);

    }
}


MENU.CPP

//*******************************************************************************************
//
// Filename : Menu.cpp
//
//Implementations for CCabItemMenu methods
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "thisdll.h"

#include "resource.h"

#include "folder.h"
#include "menu.h"

#include "cabitms.h"

CCabItemMenu::CCabItemMenu(HWND hwndOwner, CCabFolder*pcf, LPCABITEM *apit, UINT cpit)
: m_lSel(8)
{
m_hwndOwner = hwndOwner;
m_pcfHere = pcf;
pcf->AddRef();

// No need to check return value here; check in QueryInterface
m_lSel.AddItems(apit, cpit);
}

CCabItemMenu::~CCabItemMenu()
{
m_pcfHere->Release();
}

// *** IUnknown methods ***
STDMETHODIMP CCabItemMenu::QueryInterface(
   REFIID riid,
   LPVOID FAR* ppvObj)
{
*ppvObj = NULL;

if (m_lSel.GetState() == CCabItemList::State_OutOfMem)
{
return(E_OUTOFMEMORY);
}

LPUNKNOWN pObj;

if (riid == IID_IUnknown)
{
pObj = (LPUNKNOWN)(IUnknown*)((IContextMenu*)this);
// The (IShellFolder*) ^^^ up there is to disambiguate :) the reference
}
else if (riid == IID_IContextMenu)
{
pObj = (LPUNKNOWN)(IContextMenu*)this;
}
else
{
   return(E_NOINTERFACE);
}

pObj->AddRef();
*ppvObj = pObj;

return(NOERROR);
}

STDMETHODIMP_(ULONG) CCabItemMenu::AddRef(void)
{
return(m_cRef.AddRef());
}

STDMETHODIMP_(ULONG) CCabItemMenu::Release(void)
{
if (!m_cRef.Release())
{
   delete this;
return(0);
}

return(m_cRef.GetRef());
}

// *** IContextMenu methods ***
STDMETHODIMP CCabItemMenu::QueryContextMenu(
                                HMENU hmenu,
                                UINT indexMenu,
                                UINT idCmdFirst,
                                UINT idCmdLast,
                                UINT uFlags)
{
    HMENU hmMerge = LoadPopupMenu(MENU_ITEMCONTEXT, 0);

if (!hmMerge)
{
return(E_OUTOFMEMORY);
}

UINT idMax = Cab_MergeMenus(hmenu, hmMerge, indexMenu, idCmdFirst, idCmdLast,
MM_ADDSEPARATOR);

DestroyMenu(hmMerge);

SetMenuDefaultItem(hmenu, IDC_ITEM_EXTRACT+idCmdFirst, FALSE);

return(ResultFromShort(idMax - idCmdFirst));
}

STDMETHODIMP CCabItemMenu::InvokeCommand(
                             LPCMINVOKECOMMANDINFO lpici)
{
if (HIWORD(lpici->lpVerb))
{
// Deal with string commands
return(E_INVALIDARG);
}

switch ((UINT)LOWORD((DWORD)lpici->lpVerb))
{
case IDC_ITEM_EXTRACT:
{
char szHere[MAX_PATH];
if (!m_pcfHere->GetPath(szHere))
{
return(E_UNEXPECTED);
}

CCabExtract ceHere(szHere);

ceHere.ExtractItems(m_hwndOwner, NULL, ShouldExtract, (LPARAM)this);

break;
}

default:
return(E_INVALIDARG);
}

return(NOERROR);
}

STDMETHODIMP CCabItemMenu::GetCommandString(
                                UINT        idCmd,
                                UINT        uType,
                                UINT      * pwReserved,
                                LPSTR       pszName,
                                UINT        cchMax)
{
return(E_NOTIMPL);
}

HGLOBAL * CALLBACK CCabItemMenu::ShouldExtract(LPCSTR pszFile, DWORD dwSize, UINT date,
UINT time, UINT attribs, LPARAM lParam)
{
CCabItemMenu *pThis = (CCabItemMenu*)lParam;

if (pThis->m_lSel.IsInList(pszFile, dwSize, date, time, attribs))
{
return(EXTRACT_TRUE);
}

// Copy nothing for now
return(EXTRACT_FALSE);
}

HMENU CCabItemMenu::LoadPopupMenu(UINT id, UINT uSubMenu)
{
    HMENU hmParent = LoadMenu(g_ThisDll.GetInstance(), MAKEINTRESOURCE(id));
    if (!hmParent)
    {
return(NULL);
}

    HMENU hmPopup = GetSubMenu(hmParent, 0);
    RemoveMenu(hmParent, uSubMenu, MF_BYPOSITION);
    DestroyMenu(hmParent);

    return(hmPopup);
}


MENU.H

//*******************************************************************************************
//
// Filename : Menu.h
//
//CCabItemMenu
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _MENU_H_
#define _MENU_H_

#include "folder.h"

//*****************************************************************************
//
// CCabItemMenu
//
// Purpose:
//
//        IContextMenu for the Shellfolder
//
//*****************************************************************************
class CCabItemMenu : public IContextMenu
{
public:
CCabItemMenu(HWND hwndOwner, CCabFolder*pcf, LPCABITEM *apit, UINT cpit);
~CCabItemMenu();

    // *** IUnknown methods ***
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // *** IContextMenu methods ***
    STDMETHODIMP QueryContextMenu(
                                HMENU hmenu,
                                UINT indexMenu,
                                UINT idCmdFirst,
                                UINT idCmdLast,
                                UINT uFlags);

    STDMETHODIMP InvokeCommand(
                             LPCMINVOKECOMMANDINFO lpici);

    STDMETHODIMP GetCommandString(
                                UINT        idCmd,
                                UINT        uType,
                                UINT      * pwReserved,
                                LPSTR       pszName,
                                UINT        cchMax);

private:
static HMENU LoadPopupMenu(UINT id, UINT uSubMenu);

static HGLOBAL * CALLBACK CCabItemMenu::ShouldExtract(LPCSTR pszFile, DWORD dwSize,
UINT date, UINT time, UINT attribs, LPARAM lParam);

private:
CRefCount m_cRef;

CRefDll m_cRefDll;

HWND m_hwndOwner;
CCabFolder *m_pcfHere;

CCabItemList m_lSel;
} ;

#endif // _MENU_H_


OS.CPP

//*******************************************************************************************
//
// Filename : OS.cpp
//
//Implementation file for CFileTime
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "os.h"

void CFileTime::DateTimeToString(WORD wDate, WORD wTime, LPSTR pszText)
{
    FILETIME ft;

    // Netware directories do not have dates...
    if (wDate == 0)
    {
        *pszText='\0';
        return;
    }

    DosDateTimeToFileTime(wDate, wTime, &ft);
    FileTimeToDateTimeString(&ft, pszText);
}

void CFileTime::FileTimeToDateTimeString(LPFILETIME lpft, LPSTR pszText)
{
    SYSTEMTIME st;

    FileTimeToLocalFileTime(lpft, lpft);
    FileTimeToSystemTime(lpft, &st);
    GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, pszText, 64);
    pszText += lstrlen(pszText);
    *pszText++ = ' ';
    GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, pszText, 64);
}


OS.H

//*******************************************************************************************
//
// Filename : Os.h
//
//CFileTime
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _OS_H_
#define _OS_H_

class CFileTime
{
public:
CFileTime() {}
~CFileTime() {}

static void DateTimeToString(WORD wDate, WORD wTime, LPSTR pszText);

private:
static void FileTimeToDateTimeString(LPFILETIME lpft, LPSTR pszText);
} ;

#endif // _OS_H_


PATH.C

//*******************************************************************************************
//
// Filename : Path.c
//
//Useful Path manipulation routines
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"
#include "path.h"
#include "debug.h"
#include "strings.h"

//
// Inline function to check for a double-backslash at the
// beginning of a string
//

__inline BOOL DBL_BSLASH(LPCTSTR psz)
{
    return (psz[0] == TEXT('\\') && psz[1] == TEXT('\\'));
}

// returns a pointer to the extension of a file.
//
// in:
//      qualified or unqualfied file name
//
// returns:
//      pointer to the extension of this file.  if there is no extension
//      as in "foo" we return a pointer to the NULL at the end
//      of the file
//
//      foo.txt     ==> ".txt"
//      foo         ==> ""
//      foo.        ==> "."
//

LPTSTR PathFindExtension(LPCTSTR pszPath)
{
    LPCTSTR pszDot;

    for (pszDot = NULL; *pszPath; pszPath = CharNext(pszPath))
    {
        switch (*pszPath) {
        case TEXT('.'):
            pszDot = pszPath;         // remember the last dot
            break;
        case TEXT('\\'):
        case TEXT(' '):         // extensions can't have spaces
            pszDot = NULL;       // forget last dot, it was in a directory
            break;
        }
    }

    // if we found the extension, return ptr to the dot, else
    // ptr to end of the string (NULL extension) (cast->non const)
    return pszDot ? (LPTSTR)pszDot : (LPTSTR)pszPath;
}

//--------------------------------------------------------------------------
// Return a pointer to the end of the next path componenent in the string.
// ie return a pointer to the next backslash or terminating NULL.
LPCTSTR GetPCEnd(LPCTSTR lpszStart)
{
        LPCTSTR lpszEnd;

        lpszEnd = StrChr(lpszStart, TEXT('\\'));
        if (!lpszEnd)
        {
                lpszEnd = lpszStart + lstrlen(lpszStart);
        }

        return lpszEnd;
}
//--------------------------------------------------------------------------
// Given a pointer to the end of a path component, return a pointer to
// its begining.
// ie return a pointer to the previous backslash (or start of the string).
LPCTSTR PCStart(LPCTSTR lpszStart, LPCTSTR lpszEnd)
{
    LPCTSTR lpszBegin = StrRChr(lpszStart, lpszEnd, TEXT('\\'));
    if (!lpszBegin)
    {
            lpszBegin = lpszStart;
    }
    return lpszBegin;
}

//--------------------------------------------------------------------------
// Fix up a few special cases so that things roughly make sense.
void NearRootFixups(LPTSTR lpszPath, BOOL fUNC)
    {
    // Check for empty path.
    if (lpszPath[0] == TEXT('\0'))
        {
        // Fix up.
        lpszPath[0] = TEXT('\\');
        lpszPath[1] = TEXT('\0');
        }
    // Check for missing slash.
    if (!IsDBCSLeadByte(lpszPath[0]) && lpszPath[1] == TEXT(':') && lpszPath[2] == TEXT('\0'))
        {
        // Fix up.
        lpszPath[2] = TEXT('\\');
        lpszPath[3] = TEXT('\0');
        }
    // Check for UNC root.
    if (fUNC && lpszPath[0] == TEXT('\\') && lpszPath[1] == TEXT('\0'))
        {
        // Fix up.
        lpszPath[0] = TEXT('\\');
        lpszPath[1] = TEXT('\\');
        lpszPath[2] = TEXT('\0');
        }
    }

//--------------------------------------------------------------------------
// Canonicalizes a path.
BOOL PathCanonicalize(LPTSTR lpszDst, LPCTSTR lpszSrc)
    {
    LPCTSTR lpchSrc;
    LPCTSTR lpchPCEnd;           // Pointer to end of path component.
    LPTSTR lpchDst;
    BOOL fUNC;
    int cbPC;

    fUNC = PathIsUNC(lpszSrc);    // Check for UNCness.

    // Init.
    lpchSrc = lpszSrc;
    lpchDst = lpszDst;

    while (*lpchSrc)
        {
        // this should just return the count
        lpchPCEnd = GetPCEnd(lpchSrc);
        cbPC = (lpchPCEnd - lpchSrc)+1;

        // Check for slashes.
        if (cbPC == 1 && *lpchSrc == TEXT('\\'))
            {
            // Just copy them.
            *lpchDst = TEXT('\\');
            lpchDst++;
            lpchSrc++;
            }
        // Check for dots.
        else if (cbPC == 2 && *lpchSrc == TEXT('.'))
            {
            // Skip it...
            // Are we at the end?
            if (*(lpchSrc+1) == TEXT('\0'))
                {
                lpchDst--;
                lpchSrc++;
                }
            else
                lpchSrc += 2;
            }
        // Check for dot dot.
        else if (cbPC == 3 && *lpchSrc == TEXT('.') && *(lpchSrc + 1) == TEXT('.'))
            {
            // make sure we aren't already at the root
            if (!PathIsRoot(lpszDst))
                {
                // Go up... Remove the previous path component.
                lpchDst = (LPTSTR)PCStart(lpszDst, lpchDst - 1);
                }
            else
                {
                // When we can't back up, remove the trailing backslash
                // so we don't copy one again. (C:\..\FOO would otherwise
                // turn into C:\\FOO).
                if (*(lpchSrc + 2) == TEXT('\\'))
                    {
                    lpchSrc++;
                    }
                }
            lpchSrc += 2;       // skip ".."
            }
        // Everything else
        else
            {
            // Just copy it.
            lstrcpyn(lpchDst, lpchSrc, cbPC);
            lpchDst += cbPC - 1;
            lpchSrc += cbPC - 1;
            }
        // Keep everything nice and tidy.
        *lpchDst = TEXT('\0');
        }

    // Check for weirdo root directory stuff.
    NearRootFixups(lpszDst, fUNC);

    return TRUE;
    }

// Modifies:
//      szRoot
//
// Returns:
//      TRUE if a drive root was found
//      FALSE otherwise
//
BOOL PathStripToRoot(LPTSTR pszRoot)
{
    while(!PathIsRoot(pszRoot))
    {
        if (!PathRemoveFileSpec(pszRoot))
        {
            // If we didn't strip anything off,
            // must be current drive
            return(FALSE);
        }
    }

    return(TRUE);
}

// concatinate lpszDir and lpszFile into a properly formed path
// and canonicalizes any relative path pieces
//
// returns:
//  pointer to destination buffer
//
// lpszDest and lpszFile can be the same buffer
// lpszDest and lpszDir can be the same buffer
//
// assumes:
//      lpszDest is MAX_PATH bytes
//
//

LPTSTR PathCombine(LPTSTR lpszDest, LPCTSTR lpszDir, LPCTSTR lpszFile)
{
    TCHAR szTemp[MAX_PATH];
    LPTSTR pszT;

    if (!lpszFile || *lpszFile==TEXT('\0')) {

        lstrcpyn(szTemp, lpszDir, ARRAYSIZE(szTemp));       // lpszFile is empty

    } else if (lpszDir && *lpszDir && PathIsRelative(lpszFile)) {

        lstrcpyn(szTemp, lpszDir, ARRAYSIZE(szTemp));
        pszT = PathAddBackslash(szTemp);
        if (pszT) {
            int iLen = lstrlen(szTemp);
            if ((iLen + lstrlen(lpszFile)) < ARRAYSIZE(szTemp)) {
                lstrcpy(pszT, lpszFile);
            } else
                return NULL;
        } else
            return NULL;

    } else if (lpszDir && *lpszDir &&
        *lpszFile == TEXT('\\') && !PathIsUNC(lpszFile)) {

        lstrcpyn(szTemp, lpszDir, ARRAYSIZE(szTemp));
        // Note that we do not check that an actual root is returned;
        // it is assumed that we are given valid parameters
        PathStripToRoot(szTemp);

        pszT = PathAddBackslash(szTemp);
        if (pszT)
        {
            // Skip the backslash when copying
            lstrcpyn(pszT, lpszFile+1, ARRAYSIZE(szTemp) - 1 - (pszT-szTemp));
        } else
            return NULL;

    } else {

        lstrcpyn(szTemp, lpszFile, ARRAYSIZE(szTemp));     // already fully qualified file part

    }

    PathCanonicalize(lpszDest, szTemp); // this deals with .. and . stuff

    return lpszDest;
}

// rips the last part of the path off including the backslash
//      C:\foo      -> C:\      ;
//      C:\foo\bar  -> C:\foo
//      C:\foo\     -> C:\foo
//      \\x\y\x     -> \\x\y
//      \\x\y       -> \\x
//      \\x         -> ?? (test this)
//      \foo        -> \  (Just the slash!)
//
// in/out:
//      pFile   fully qualified path name
// returns:
//      TRUE    we stripped something
//      FALSE   didn't strip anything (root directory case)
//

BOOL PathRemoveFileSpec(LPTSTR pFile)
{
    LPTSTR pT;
    LPTSTR pT2 = pFile;

    for (pT = pT2; *pT2; pT2 = CharNext(pT2)) {
        if (*pT2 == TEXT('\\'))
            pT = pT2;             // last "\" found, (we will strip here)
        else if (*pT2 == TEXT(':')) {   // skip ":\" so we don't
            if (pT2[1] ==TEXT('\\'))    // strip the "\" from "C:\"
                pT2++;
            pT = pT2 + 1;
        }
    }
    if (*pT == 0)
        return FALSE;   // didn't strip anything

    //
    // handle the \foo case
    //
    else if ((pT == pFile) && (*pT == TEXT('\\'))) {
        // Is it just a '\'?
        if (*(pT+1) != TEXT('\0')) {
            // Nope.
            *(pT+1) = TEXT('\0');
            return TRUE;        // stripped something
        }
        else        {
            // Yep.
            return FALSE;
        }
    }
    else {
        *pT = 0;
        return TRUE;    // stripped something
    }
}

// add a backslash to a qualified path
//
// in:
//  lpszPath    path (A:, C:\foo, etc)
//
// out:
//  lpszPath    A:\, C:\foo\    ;
//
// returns:
//  pointer to the NULL that terminates the path

LPTSTR PathAddBackslash(LPTSTR lpszPath)
{
    LPTSTR lpszEnd;

    // try to keep us from tromping over MAX_PATH in size.
    // if we find these cases, return NULL.  Note: We need to
    // check those places that call us to handle their GP fault
    // if they try to use the NULL!
    int ichPath = lstrlen(lpszPath);
    if (ichPath >= (MAX_PATH - 1))
    {
        Assert(FALSE);      // Let the caller know!
        return(NULL);
    }

    lpszEnd = lpszPath + ichPath;

    // this is really an error, caller shouldn't pass
    // an empty string
    if (!*lpszPath)
        return lpszEnd;

    /* Get the end of the source directory
    */
    switch(*CharPrev(lpszPath, lpszEnd)) {
    case TEXT('\\'):
        break;

    default:
        *lpszEnd++ = TEXT('\\');
        *lpszEnd = TEXT('\0');
    }
    return lpszEnd;
}

// Returns a pointer to the last component of a path string.
//
// in:
//      path name, either fully qualified or not
//
// returns:
//      pointer into the path where the path is.  if none is found
//      returns a poiter to the start of the path
//
//  c:\foo\bar  -> bar
//  c:\foo      -> foo
//  c:\foo\     -> c:\foo\      ( is this case busted?)
//  c:\         -> c:\          ( this case is strange)
//  c:          -> c:
//  foo         -> foo

LPTSTR PathFindFileName(LPCTSTR pPath)
{
    LPCTSTR pT;

    for (pT = pPath; *pPath; pPath = CharNext(pPath)) {
        if ((pPath[0] == TEXT('\\') || pPath[0] == TEXT(':')) && pPath[1] && (pPath[1] != TEXT('\\')))
            pT = pPath + 1;
    }

    return (LPTSTR)pT;   // const -> non const
}

//---------------------------------------------------------------------------
// Returns TRUE if the given string is a UNC path.
//
// TRUE
//      "\\foo\bar"
//      "\\foo"         <- careful
//      "\\"
// FALSE
//      "\foo"
//      "foo"
//      "c:\foo"

BOOL PathIsUNC(LPCTSTR pszPath)
{
    return DBL_BSLASH(pszPath);
}

//---------------------------------------------------------------------------
// Return TRUE if the path isn't absoulte.
//
// TRUE
//      "foo.exe"
//      ".\foo.exe"
//      "..\boo\foo.exe"
//
// FALSE
//      "\foo"
//      "c:bar"     <- be careful
//      "c:\bar"
//      "\\foo\bar"

BOOL PathIsRelative(LPCTSTR lpszPath)
{
    // The NULL path is assumed relative
    if (*lpszPath == 0)
        return TRUE;

    // Does it begin with a slash ?
    if (lpszPath[0] == TEXT('\\'))
        return FALSE;
    // Does it begin with a drive and a colon ?
    else if (!IsDBCSLeadByte(lpszPath[0]) && lpszPath[1] == TEXT(':'))
        return FALSE;
    // Probably relative.
    else
        return TRUE;
}

#pragma data_seg(".text", "CODE")

const TCHAR c_szColonSlash[] = TEXT(":\\");

#pragma data_seg()

// check if a path is a root
//
// returns:
//  TRUE for "\" "X:\" "\\foo\asdf" "\\foo\"
//  FALSE for others

BOOL  PathIsRoot(LPCTSTR pPath)
{
    if (!IsDBCSLeadByte(*pPath))
    {
        if (!lstrcmpi(pPath + 1, c_szColonSlash))                  // "X:\" case
            return TRUE;
    }

    if ((*pPath == TEXT('\\')) && (*(pPath + 1) == 0))        // "\" case
        return TRUE;

    if (DBL_BSLASH(pPath))      // smells like UNC name
    {
        LPCTSTR p;
        int cBackslashes = 0;

        for (p = pPath + 2; *p; p = CharNext(p)) {
            if (*p == TEXT('\\') && (++cBackslashes > 1))
               return FALSE;   /* not a bare UNC name, therefore not a root dir */
        }
        return TRUE;    /* end of string with only 1 more backslash */
                        /* must be a bare UNC, which looks like a root dir */
    }
    return FALSE;
}

BOOL OnExtList(LPCTSTR pszExtList, LPCTSTR pszExt)
{
    for (; *pszExtList; pszExtList += lstrlen(pszExtList) + 1)
    {
        if (!lstrcmpi(pszExt, pszExtList))
        {
            return TRUE;        // yes
        }
    }

    return FALSE;
}

#pragma data_seg(".text", "CODE")
// what about .cmd?
const TCHAR achExes[] = TEXT(".bat\0.pif\0.exe\0.com\0");
#pragma data_seg()

// determine if a path is a program by looking at the extension
//
BOOL PathIsExe(LPCTSTR szFile)
{
    LPCTSTR temp = PathFindExtension(szFile);
    return OnExtList((LPCTSTR) achExes, temp);
}


PATH.H

//*******************************************************************************************
//
// Filename : Path.h
//
//Path APIs
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _PATH_H_
#define _PATH_H_

#ifdef __cplusplus
extern "C" {
#endif

extern const TCHAR c_szStarDotStar[];
extern const TCHAR c_szStar[];
extern const TCHAR c_szSlash[];
extern const TCHAR c_szNULL[];

#define DRIVEID(path)   ((path[0] - 'A') & 31)
#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))
#define SIZEOF(v)       sizeof(v)

BOOL   PathIsDotOrDotDot(LPTSTR pszDir);
BOOL   PathStripToRoot(LPTSTR pszRoot);
LPTSTR PathAddBackslash(LPTSTR lpszPath);
LPTSTR PathRemoveBackslash(LPTSTR lpszPath);
void   PathRemoveBlanks(LPTSTR lpszString);
BOOL   PathRemoveFileSpec(LPTSTR lpszPath);
LPTSTR PathFindFileName(LPCTSTR pPath);
BOOL   PathIsRoot(LPCTSTR lpszPath);
BOOL   PathIsRelative(LPCTSTR lpszPath);
BOOL   PathIsUNC(LPCTSTR lpsz);
BOOL   PathIsDirectory(LPCTSTR lpszPath);
BOOL   PathIsExe(LPCTSTR lpszPath);
int    PathGetDriveNumber(LPCTSTR lpszPath);
LPTSTR PathCombine(LPTSTR szDest, LPCTSTR lpszDir, LPCTSTR lpszFile);
BOOL   PathAppend(LPTSTR pPath, LPCTSTR pMore);
LPTSTR PathBuildRoot(LPTSTR szRoot, int iDrive);
int    PathCommonPrefix(LPCTSTR pszFile1, LPCTSTR pszFile2, LPTSTR achPath);
LPTSTR PathFindExtension(LPCTSTR pszPath);
BOOL   PathFileExists(LPCTSTR lpszPath);
BOOL   PathMatchSpec(LPCTSTR pszFile, LPCTSTR pszSpec);
BOOL   PathMakeUniqueName(LPTSTR pszUniqueName, UINT cchMax, LPCTSTR pszTemplate, LPCTSTR pszLongPlate, LPCTSTR pszDir);
LPTSTR PathGetArgs(LPCTSTR pszPath);
BOOL   PathGetShortName(LPCTSTR lpszLongName, LPTSTR lpszShortName, UINT cbShortName);
void   PathQuoteSpaces(LPTSTR lpsz);
void   PathUnquoteSpaces(LPTSTR lpsz);
BOOL   PathDirectoryExists(LPCTSTR lpszDir);

#define PQD_NOSTRIPDOTS 0x00000001
void   PathQualifyDef(LPTSTR psz, LPCTSTR szDefDir, DWORD dwFlags);
void   PathQualify(LPTSTR lpsz);

#define PRF_VERIFYEXISTS            0x0001
#define PRF_TRYPROGRAMEXTENSIONS    (0x0002 | PRF_VERIFYEXISTS)
#define PRF_FIRSTDIRDEF             0x0004
#define PRF_DONTFINDLNK             0x0008      // if PRF_TRYPROGRAMEXTENSIONS is specified

int    PathResolve(LPTSTR lpszPath, LPCTSTR FAR dirs[], UINT fFlags);

LPTSTR PathGetNextComponent(LPCTSTR lpszPath, LPTSTR lpszComponent);
LPTSTR PathFindNextComponent(LPCTSTR lpszPath);
BOOL   PathIsSameRoot(LPCTSTR pszPath1, LPCTSTR pszPath2);

//
//  Return codes from PathCleanupSpec.  Negative return values are
//  unrecoverable errors
//
#define PCS_FATAL           0x80000000
#define PCS_REPLACEDCHAR    0x00000001
#define PCS_REMOVEDCHAR     0x00000002
#define PCS_TRUNCATED       0x00000004
#define PCS_PATHTOOLONG     0x00000008  // Always combined with FATAL

int    PathCleanupSpec(LPCTSTR pszDir, LPTSTR pszSpec);

BOOL   PathCompactPath(HDC hDC, LPTSTR lpszPath, UINT dx);
void   PathSetDlgItemPath(HWND hDlg, int id, LPCTSTR pszPath);

#define GCT_INVALID             0x0000
#define GCT_LFNCHAR             0x0001
#define GCT_SHORTCHAR           0x0002
#define GCT_WILD                0x0004
#define GCT_SEPERATOR           0x0008
UINT PathGetCharType(TCHAR ch);

void PathRemoveArgs(LPTSTR pszPath);
BOOL PathMakePretty(LPTSTR lpPath);

BOOL PathIsFileSpec(LPCTSTR lpszPath);
BOOL PathIsLink(LPCTSTR szFile);

BOOL PathRenameExtension(LPTSTR pszPath, LPCTSTR pszExt);

int DriveType(int iDrive);

//-------- drive type identification --------------
// iDrive      drive index (0=A, 1=B, ...)
//
#define DRIVE_CDROM     5           // extended DriveType() types
#define DRIVE_RAMDRIVE  6

#define DRIVE_TYPE      0x000F      // type masek
#define DRIVE_SLOW      0x0010      // drive is on a slow link
#define DRIVE_LFN       0x0020      // drive supports LFNs
#define DRIVE_AUTORUN   0x0040      // drive has AutoRun.inf in root.
#define DRIVE_AUDIOCD   0x0080      // drive is a AudioCD
#define DRIVE_AUTOOPEN  0x0100      // should *always* auto open on insert
#define DRIVE_NETUNAVAIL 0x0200     // Network drive that is not available
#define DRIVE_SHELLOPEN  0x0400     // should auto open on insert, if shell has focus

#define DriveTypeFlags(iDrive)      DriveType('A' + (iDrive))
#define DriveIsSlow(iDrive)         (DriveTypeFlags(iDrive) & DRIVE_SLOW)
#define DriveIsLFN(iDrive)          (DriveTypeFlags(iDrive) & DRIVE_LFN)
#define DriveIsAutoRun(iDrive)      (DriveTypeFlags(iDrive) & DRIVE_AUTORUN)
#define DriveIsAutoOpen(iDrive)     (DriveTypeFlags(iDrive) & DRIVE_AUTOOPEN)
#define DriveIsShellOpen(iDrive)    (DriveTypeFlags(iDrive) & DRIVE_SHELLOPEN)
#define DriveIsAudioCD(iDrive)      (DriveTypeFlags(iDrive) & DRIVE_AUDIOCD)
#define DriveIsNetUnAvail(iDrive)   (DriveTypeFlags(iDrive) & DRIVE_NETUNAVAIL)

#define IsCDRomDrive(iDrive)        (DriveType(iDrive) == DRIVE_CDROM)
#define IsRamDrive(iDrive)          (DriveType(iDrive) == DRIVE_RAMDRIVE)
#define IsRemovableDrive(iDrive)    (DriveType(iDrive) == DRIVE_REMOVABLE)
#define IsRemoteDrive(iDrive)       (DriveType(iDrive) == DRIVE_REMOTE)

#ifdef __cplusplus
}
#endif

#endif // _PATH_H_


PCH.H

//*******************************************************************************************
//
// Filename : Pch.h
//
//Common header file
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#pragma warning(disable: 4001 4100 4201 4204 4209 4214 4226)
#pragma warning(disable: 4514 4699 4705)

#define STRICT
#include <windows.h>

#pragma warning(disable: 4001 4100 4201 4204 4209 4214 4226)
#pragma warning(disable: 4514 4699 4705)
// Need to turn off warnings again after windows.h

#include <windowsx.h>
#include <shlobj.h>
#include <shlguid.h>

#include "dpda.h"
#include "Cabobj.h"
#include "Cabvw2.h"
#include "Cabp.h"

#define FIELDOFFSET(type, field)    ((int)(&((type NEAR*)1)->field)-1)


CABITMS.CPP

//*******************************************************************************************
//
// Filename : CabItms.cpp
//
//Implementation file for CMemFile, CCabEnum and CCabExtract
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "thisdll.h"

#include "resource.h"

#include "path.h"
#include "fdi.h"
#include "cabitms.h"

class CMemFile
{
public:
CMemFile(HGLOBAL *phMem, DWORD dwSize);
~CMemFile() {}

BOOL Create(LPCSTR pszFile, int fnAttribute);
BOOL Open(LPCSTR pszFile, int oflag);
LONG Seek(LONG dist, int seektype);
UINT Read(LPVOID pv, UINT cb);
UINT Write(LPVOID pv, UINT cb);
HFILE Close();

private:
HFILE m_hf;

HGLOBAL *m_phMem;
DWORD m_dwSize;
LONG m_lLoc;
} ;

CMemFile::CMemFile(HGLOBAL *phMem, DWORD dwSize) : m_hf(HFILE_ERROR), m_lLoc(0)
{
m_phMem = phMem;
m_dwSize = dwSize;

if (phMem)
{
*phMem = NULL;
}
}

BOOL CMemFile::Create(LPCSTR pszFile, int fnAttribute)
{
if (m_phMem)
{
if (*m_phMem)
{
return(FALSE);
}

*m_phMem = GlobalAlloc(LMEM_FIXED, m_dwSize);
return(*m_phMem != NULL);
}
else
{
if (m_hf != HFILE_ERROR)
{
return(FALSE);
}

m_hf = _lcreat(pszFile, fnAttribute);
return(m_hf != HFILE_ERROR);
}
}

BOOL CMemFile::Open(LPCSTR pszFile, int oflag)
{
if (m_phMem)
{
return(FALSE);
}
else
{
if (m_hf != HFILE_ERROR)
{
return(FALSE);
}

m_hf = _lopen(pszFile, oflag);
return(m_hf != HFILE_ERROR);
}
}

LONG CMemFile::Seek(LONG dist, int seektype)
{
if (m_phMem)
{
if (!*m_phMem)
{
return(HFILE_ERROR);
}

switch (seektype)
{
case FILE_BEGIN:
break;

case FILE_CURRENT:
dist += m_lLoc;
break;

case FILE_END:
dist = m_dwSize - dist;
break;

default:
return(HFILE_ERROR);
}

if (dist<0 || dist>(LONG)m_dwSize)
{
return(HFILE_ERROR);
}

m_lLoc = dist;
return(dist);
}
else
{
return(_llseek(m_hf, dist, seektype));
}
}

UINT CMemFile::Read(LPVOID pv, UINT cb)
{
if (m_phMem)
{
if (!*m_phMem)
{
return((UINT)HFILE_ERROR);
}

if (cb > m_dwSize - m_lLoc)
{
cb = m_dwSize - m_lLoc;
}

hmemcpy(pv, (LPSTR)(*m_phMem)+m_lLoc, cb);
m_lLoc += cb;
return(cb);
}
else
{
return(_lread(m_hf, pv, cb));
}
}

UINT CMemFile::Write(LPVOID pv, UINT cb)
{
if (m_phMem)
{
if (!*m_phMem)
{
return((UINT)HFILE_ERROR);
}

if (cb > m_dwSize - m_lLoc)
{
cb = m_dwSize - m_lLoc;
}

hmemcpy((LPSTR)(*m_phMem)+m_lLoc, pv, cb);
m_lLoc += cb;
return(cb);
}
else
{
return(_lwrite(m_hf, (LPCSTR)pv, cb));
}
}

HFILE CMemFile::Close()
{
HFILE hRet;

if (m_phMem)
{
hRet = *m_phMem ? 0 : HFILE_ERROR;
}
else
{
hRet = _lclose(m_hf);
}

delete this;

return(hRet);
}

//*****************************************************************************
//
// CCabEnum
//
// Purpose:
//
//        Class encapsulating all the FDI operations
//
// Comments:
//
//*****************************************************************************

class CCabEnum
{
public:
CCabEnum() : m_hfdi(0) {}
~CCabEnum() {}

protected:
static void HUGE * FAR DIAMONDAPI CabAlloc(ULONG cb);
static void FAR DIAMONDAPI CabFree(void HUGE *pv);
static int  FAR DIAMONDAPI CabOpen(char FAR *pszFile, int oflag, int pmode);
static UINT FAR DIAMONDAPI CabRead(int hf, void FAR *pv, UINT cb);
static UINT FAR DIAMONDAPI CabWrite(int hf, void FAR *pv, UINT cb);
static int  FAR DIAMONDAPI CabClose(int hf);
static long FAR DIAMONDAPI CabSeek(int hf, long dist, int seektype);

BOOL StartEnum();
BOOL SimpleEnum(LPCSTR szCabFile, PFNFDINOTIFY pfnCallBack, LPVOID pv);
void EndEnum();

HFDI m_hfdi;

private:
static CMemFile * s_hSpill;
} ;

CMemFile * CCabEnum::s_hSpill = NULL;

void HUGE * FAR DIAMONDAPI CCabEnum::CabAlloc(ULONG cb)
{
    return(GlobalAllocPtr(GHND, cb));
}

void FAR DIAMONDAPI CCabEnum::CabFree(void HUGE *pv)
{
    GlobalFreePtr(pv);
}

int  FAR DIAMONDAPI CCabEnum::CabOpen(char FAR *pszFile, int oflag, int pmode)
{
    if(!pszFile)
    {
      return -1;
    }

    // See if we are opening the spill file.
    if( *pszFile=='*' )
    {
char szSpillFile[MAX_PATH];
char szTempPath[MAX_PATH];

        if(s_hSpill != NULL)
            return -1;

GetTempPath(sizeof(szTempPath), szTempPath);
GetTempFileName(szTempPath, "fdi", 0, szSpillFile);

        s_hSpill = new CMemFile(NULL, 0);
if (!s_hSpill)
{
return(-1);
}
        if (!s_hSpill->Create(szSpillFile, 0))
{
delete s_hSpill;
s_hSpill = NULL;
return(-1);
}

        // Set its extent.
        if( s_hSpill->Seek( ((FDISPILLFILE FAR *)pszFile)->cbFile-1, 0) == HFILE_ERROR)
        {
s_hSpill->Close();
s_hSpill = NULL;
return -1;
        }
        s_hSpill->Write(szSpillFile, 1);

        return (int)s_hSpill;
    }

    CMemFile *hFile = new CMemFile(NULL, 0);
if (!hFile)
{
return(-1);
}

    while (!hFile->Open(pszFile, oflag))
    {
#if 1// TODO: No UI for inserting a disk at this point
delete hFile;
return(-1);
#else
       // Failed to open the source.
       if (!LoadString (g_hInst, IDS_DISKPROMPT, szText, MAX_STRTABLE_LEN))
           return -1;

   char szText[MAX_PATH];

       wsprintf (g_pErrorBuffer, (LPSTR)szText, (LPSTR)g_pCabName);

       // Use hwndIniting to have a parent window
       if ( MyMessageBox(g_hwndIniting, g_pErrorBuffer,
                    MAKEINTRESOURCE(IDS_DISKPROMPT_TIT),
                    MB_OKCANCEL|MB_ICONSTOP, 0) == IDOK )
          continue;
       else
          return -1;
#endif
    }

    return((int)hFile);
}

UINT FAR DIAMONDAPI CCabEnum::CabRead(int hf, void FAR *pv, UINT cb)
{
CMemFile *hFile = (CMemFile *)hf;

    if (hFile->Read(pv, cb) != cb)
    {
        cb = (UINT)-1;
}

    return(cb);
}

UINT FAR DIAMONDAPI CCabEnum::CabWrite(int hf, void FAR *pv, UINT cb)
{
CMemFile *hFile = (CMemFile *)hf;

    if (hFile->Write(pv, cb) != cb)
{
cb = (UINT)-1;
}

return(cb);
}

int  FAR DIAMONDAPI CCabEnum::CabClose(int hf)
{
CMemFile *hFile = (CMemFile *)hf;

    // Special case for the deletion of the spill file.
    if(hFile == s_hSpill)
{
        s_hSpill = NULL;
}

    return (hFile->Close());
}

long FAR DIAMONDAPI CCabEnum::CabSeek(int hf, long dist, int seektype)
{
CMemFile *hFile = (CMemFile *)hf;

    return(hFile->Seek(dist, seektype));
}

BOOL CCabEnum::StartEnum()
{
if (m_hfdi)
{
// We seem to already be enumerating
return(FALSE);
}

ERF erf;

m_hfdi = FDICreate(
CabAlloc,
        CabFree,
        CabOpen,
        CabRead,
        CabWrite,
        CabClose,
        CabSeek,
        cpu80386,
        &erf);

return(m_hfdi != NULL);
}

BOOL CCabEnum::SimpleEnum(LPCSTR szCabFile, PFNFDINOTIFY pfnCallBack, LPVOID pv)
{
char szCabPath[MAX_PATH];
char szCabName[MAX_PATH];

// Path should be fully qualified
lstrcpyn(szCabPath, szCabFile, sizeof(szCabPath));
LPSTR pszName = PathFindFileName(szCabPath);
if (!pszName)
{
return(FALSE);
}

lstrcpy(szCabName, pszName);
*pszName = '\0';

if (!StartEnum())
{
return(FALSE);
}

BOOL bRet = FDICopy(
m_hfdi,
szCabName,
szCabPath,// path to cabinet files
0,// flags
pfnCallBack,
NULL,
pv);

EndEnum();

return(bRet);
}

void CCabEnum::EndEnum()
{
if (!m_hfdi)
{
return;
}

FDIDestroy(m_hfdi);
m_hfdi = NULL;
}

class CCabItemsCB : private CCabEnum
{
public:
CCabItemsCB(CCabItems::PFNCABITEM pfnCallBack, LPARAM lParam)
{
m_pfnCallBack = pfnCallBack;
m_lParam = lParam;
}
~CCabItemsCB() {}

BOOL DoEnum(LPCSTR szCabFile)
{
return(SimpleEnum(szCabFile, CabItemsNotify, this));
}

private:
static int  FAR DIAMONDAPI CabItemsNotify(FDINOTIFICATIONTYPE fdint,
PFDINOTIFICATION pfdin);

CCabItems::PFNCABITEM m_pfnCallBack;
LPARAM m_lParam;
} ;

int FAR DIAMONDAPI CCabItemsCB::CabItemsNotify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
{
CCabItemsCB *pThis = (CCabItemsCB *)pfdin->pv;

    // uiYield( g_hwndSetup );

    switch (fdint)
    {
    case fdintCOPY_FILE:
pThis->m_pfnCallBack(pfdin->psz1, pfdin->cb, pfdin->date, pfdin->time, pfdin->attribs,
pThis->m_lParam);
break;

default:
break;
    } // end switch

    return 0;
}

//*****************************************************************************
//
// CCabItems::EnumItems
//
// Purpose:
//
//        Enumerate the items in the cab file
//
//
// Comments:
//
//         lParam contains pointer to CCabFolder
//
//*****************************************************************************

BOOL CCabItems::EnumItems(PFNCABITEM pfnCallBack, LPARAM lParam)
{
CCabItemsCB cItems(pfnCallBack, lParam);

return(cItems.DoEnum(m_szCabFile));
}

//*****************************************************************************
//
// CCabExtractCB
//
// Purpose:
//
//        handles the call back while extracting Cab files
//
//
//*****************************************************************************

class CCabExtractCB : private CCabEnum
{
public:
CCabExtractCB(LPCSTR szDir, HWND hwndOwner, CCabExtract::PFNCABEXTRACT pfnCallBack,
LPARAM lParam)
{
m_szDir = szDir;
m_hwndOwner = hwndOwner;
m_pfnCallBack = pfnCallBack;
m_lParam = lParam;
m_bTryNextCab = FALSE;
}
~CCabExtractCB() {}

BOOL DoEnum(LPCSTR szCabFile)
{
return(SimpleEnum(szCabFile, CabExtractNotify, this));
}

private:
static int  FAR DIAMONDAPI CabExtractNotify(FDINOTIFICATIONTYPE fdint,
PFDINOTIFICATION pfdin);
static int CALLBACK CCabExtractCB::BrowseNotify(HWND hwnd, UINT uMsg, LPARAM lParam,
LPARAM lpData);

LPCSTR m_szDir;
HWND m_hwndOwner;
CCabExtract::PFNCABEXTRACT m_pfnCallBack;
LPARAM m_lParam;
BOOL m_bTryNextCab;
PFDINOTIFICATION m_pfdin;
} ;

int CALLBACK CCabExtractCB::BrowseNotify(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
CCabExtractCB *pThis = (CCabExtractCB *)lpData;

switch (uMsg)
{
case BFFM_INITIALIZED:
{
// Set initial folder
LPSTR pszEnd = PathAddBackslash(pThis->m_pfdin->psz3);
if (pszEnd - pThis->m_pfdin->psz3 > 3)
{
// No problems if not drive root
*(pszEnd - 1) = '\0';
}
SendMessage(hwnd, BFFM_SETSELECTION, 1, (LPARAM)pThis->m_pfdin->psz3);
break;
}

default:
return(0);
}

return(1);
}

int FAR DIAMONDAPI CCabExtractCB::CabExtractNotify(FDINOTIFICATIONTYPE fdint,
PFDINOTIFICATION pfdin)
{
CCabExtractCB *pThis = (CCabExtractCB *)pfdin->pv;

    // uiYield( g_hwndSetup );

    switch (fdint)
    {
case fdintCABINET_INFO:
pThis->m_bTryNextCab = TRUE;
break;

case fdintNEXT_CABINET:
{
if (pThis->m_bTryNextCab)
{
// Automatically open next cab if already in default dir
pThis->m_bTryNextCab = FALSE;
return(1);
}

pThis->m_pfdin = pfdin;

char szTitle[80];
LoadString(g_ThisDll.GetInstance(), IDS_NEXTCABBROWSE, szTitle, sizeof(szTitle));

BROWSEINFO bi;
bi.hwndOwner = pThis->m_hwndOwner;
bi.pidlRoot = NULL;
bi.pszDisplayName = NULL;
bi.lpszTitle = szTitle;
bi.ulFlags = BIF_RETURNONLYFSDIRS;
bi.lpfn = BrowseNotify;
bi.lParam = (LPARAM)pThis;

LPITEMIDLIST pidl = SHBrowseForFolder(&bi);

if (bi.pidlRoot)
{
ILFree((LPITEMIDLIST)bi.pidlRoot);
}

if (!pidl)
{
return(-1);
}

BOOL bSuccess = SHGetPathFromIDList(pidl, pfdin->psz3);
ILFree(pidl);

if (bSuccess)
{
PathAddBackslash(pfdin->psz3);
return(1);
}

return(-1);
}

    case fdintCOPY_FILE:
{
HGLOBAL *phMem = pThis->m_pfnCallBack(pfdin->psz1, pfdin->cb, pfdin->date,
pfdin->time, pfdin->attribs, pThis->m_lParam);
if (!phMem)
{
break;
}

char szTemp[MAX_PATH];

CMemFile *hFile;

if (pThis->m_szDir == DIR_MEM)
{
*szTemp = '\0';
hFile = new CMemFile(phMem, pfdin->cb);
}
else
{
PathCombine(szTemp, pThis->m_szDir, pfdin->psz1);
hFile = new CMemFile(NULL, 0);
}

if (!hFile)
{
return(-1);
}

if (hFile->Create(szTemp, OF_WRITE))
{
return((int)hFile);
}

delete hFile;
return(-1);
}

    case fdintCLOSE_FILE_INFO:
{
CMemFile *hFile = (CMemFile *)pfdin->hf;

return(hFile->Close() == 0);
}

default:
break;
    } // end switch

    return 0;
}

BOOL CCabExtract::ExtractItems(HWND hwndOwner, LPCSTR szDir, PFNCABEXTRACT pfnCallBack, LPARAM lParam)
{
char szTempDir[MAX_PATH];

if (!szDir)
{
szDir = szTempDir;

char szTitle[80];
LoadString(g_ThisDll.GetInstance(), IDS_EXTRACTBROWSE, szTitle, sizeof(szTitle));

BROWSEINFO bi;
bi.hwndOwner = hwndOwner;
bi.pidlRoot = NULL;
bi.pszDisplayName = NULL;
bi.lpszTitle = szTitle;
bi.ulFlags = BIF_RETURNONLYFSDIRS;
bi.lpfn = NULL;

LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
if (!pidl)
{
return(FALSE);
}

BOOL bSuccess = SHGetPathFromIDList(pidl, szTempDir);

ILFree(pidl);
if (!bSuccess)
{
return(FALSE);
}
}

CCabExtractCB cExtract(szDir, hwndOwner, pfnCallBack, lParam);

// Display Wait cursor until done copying
CWaitCursor cWait;

return(cExtract.DoEnum(m_szCabFile));
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by cabview.rc
//
#define IDS_CV_COL_NAME                 1
#define IDC_ITEM_EXTRACT                1
#define IDI_CABVIEW                     1
#define IDC_SELECT                      1
#define IDS_CV_COL_SIZE                 2
#define IDC_VIEW_ICON                   2
#define IDS_CV_COL_TYPE                 3
#define IDC_VIEW_SMALLICON              3
#define IDS_CV_COL_MODIFIED             4
#define IDC_VIEW_LIST                   4
#define IDC_VIEW_DETAILS                5
#define IDC_ARRANGE_AUTO                6
#define IDC_ARRANGE_GRID                7
#define IDS_ORDERKB                     9
#define IDC_EDIT_COPY                   10
#define IDS_EXTRACTBROWSE               12
#define IDS_NEXTCABBROWSE               13
#define IDS_SUREUNINST                  14
#define IDS_THISDLL                     15
#define IDC_EDIT_SELALL                 15
#define IDC_EDIT_INVSEL                 16
#define IDS_NAME                        16
#define IDS_BYCOL_FMT                   17
#define IDS_VIEW_ICON                   32
#define IDS_VIEW_SMALLICON              33
#define IDS_VIEW_LIST                   34
#define IDS_VIEW_DETAILS                35
#define IDS_EDIT_COPY                   36
#define IDS_EDIT_SELALL                 37
#define IDS_EDIT_INVSEL                 38
#define IDS_BYCOL_HELP_FMT              39
#define IDS_ARRANGE_GRID                40
#define IDS_ARRANGE_AUTO                41
#define IDS_TT_VIEW_ICON                64
#define IDS_TT_VIEW_SMALLICON           65
#define IDS_TT_VIEW_LIST                66
#define IDS_TT_VIEW_DETAILS             67
#define IDS_TT_EDIT_COPY                68
#define MENU_ITEMCONTEXT                101
#define IDD_VIEW                        104
#define IDI_GENERIC                     105
#define MENU_SFV                        106
#define MENU_SFV_MAINMERGE              107
#define MENU_SFV_MAINMERGENF            108
#define IDA_MAIN                        108
#define IDC_LISTVIEW                    1000
#define IDC_LISTBOX                     1001

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        109
#define _APS_NEXT_COMMAND_VALUE         40011
#define _APS_NEXT_CONTROL_VALUE         1002
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SFVIEW.CPP

//*******************************************************************************************
//
// Filename : Sfview.cpp
//
//Implementation file for CSFView
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "Pch.H"

#include "SFView.H"
#include "SFVWnd.H"

#include "Resource.H"

#include "ThisGuid.H"

// Define the ShellFolderView IID's
#pragma data_seg(".text")
#define INITGUID
#include <initguid.h>
#include "SFView.H"
//#undef INITGUID
#pragma data_seg()

struct SFSTATE_HDR
{
CLSID clsThis;
SFSTATE sfState;
UINT nCols;
} ;

CSFView::CSFView(LPSHELLFOLDER psf, IShellFolderViewCallback *psfvcb) :
m_psf(psf), m_erFolder(psf), m_erCB(psfvcb), m_pCDB(NULL), m_cView(NULL),
m_uState(SVUIA_DEACTIVATE), m_pcmSel(NULL), m_cAccel(IDA_MAIN)
{
m_psfvcb = psfvcb;
if (psfvcb)
{
psfvcb->AddRef();
}

psf->AddRef();

m_aParamSort = DPA_Create(4);
m_sfState.lParamSort = 0;
}

CSFView::~CSFView()
{
ReleaseSelContextMenu();
}

STDMETHODIMP CSFView::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
static const IID *apiid[] = { &IID_IShellView, NULL };
LPUNKNOWN aobj[] = { (IShellView *)this };

return(QIHelper(riid, ppvObj, apiid, aobj));
}

STDMETHODIMP_(ULONG) CSFView::AddRef()
{
return(AddRefHelper());
}

STDMETHODIMP_(ULONG) CSFView::Release()
{
return(ReleaseHelper());
}

STDMETHODIMP CSFView::GetWindow(HWND * lphwnd)
{
return(E_NOTIMPL);
}

STDMETHODIMP CSFView::ContextSensitiveHelp(BOOL fEnterMode)
{
return(E_NOTIMPL);
}

//*****************************************************************************
//
// CSFView::TranslateAccelerator
//
// Purpose:
//         Handle the accelerator keystrokes
//
//
// Parameters:
//        LPMSG lpmsg    -    message structure
//
//
// Comments:
//
//*****************************************************************************

STDMETHODIMP CSFView::TranslateAccelerator(LPMSG lpmsg)
{
return(m_cAccel.TranslateAccelerator(m_cView, lpmsg) ? S_OK : S_FALSE);
}

STDMETHODIMP CSFView::EnableModeless(BOOL fEnable)
{
return(E_NOTIMPL);
}

//*****************************************************************************
//
// CSFView:UIActivate
//
// Purpose:
//        The explorer calls this member function whenever the activation
//  state of the view window is changed by a certain event that is
//  NOT caused by the shell view itself.
//
//
// Parameters:
//
//        UINT uState    -    UI activate flag
//
// Comments:
//
//*****************************************************************************

STDMETHODIMP CSFView::UIActivate(UINT uState)
{
if (uState)
{
OnActivate(uState);
}
else
{
OnDeactivate();
}

return S_OK;
}

STDMETHODIMP CSFView::Refresh()
{
return(E_NOTIMPL);
}

//*****************************************************************************
//
// CSFView::CreateViewWindow
//
// Purpose:
//
//        called by IShellBrowser to create a contents pane window
//
// Parameters:
//
//    IShellView  *lpPrevView    -    previous view
//    LPCFOLDERSETTINGS lpfs     -    folder settings for the view
//    IShellBrowser *psb         -    pointer to the shell browser
//    RECT * prcView             -    view Rectangle
//    HWND * phWnd               -    pointer to Window handle
//
//
// Comments:
//
//*****************************************************************************

STDMETHODIMP CSFView::CreateViewWindow(IShellView  *lpPrevView,
                LPCFOLDERSETTINGS lpfs, IShellBrowser  * psb,
                RECT * prcView, HWND  *phWnd)
{
*phWnd = NULL;

if ((HWND)m_cView)
{
return(E_UNEXPECTED);
}

m_fs = *lpfs;
m_psb = psb;

// get the main window handle from shell browser

psb->GetWindow(&m_hwndMain);

// bring up the contents pane

if (!m_cView.DoModeless(IDD_VIEW, m_hwndMain))
{
return(E_OUTOFMEMORY);
}

*phWnd = m_cView;

// map the current view mode into menu id and set the contents pane
// view mode accordingly

OnCommand(NULL, GET_WM_COMMAND_MPS(GetMenuIDFromViewMode(), 0, 0));

AddColumns();

RestoreViewState();

// size the contents pane
SetWindowPos(m_cView, NULL, prcView->left, prcView->top,
prcView->right-prcView->left, prcView->bottom-prcView->top,
SWP_NOZORDER|SWP_SHOWWINDOW);

FillList(TRUE);

return(NOERROR);
}

STDMETHODIMP CSFView::DestroyViewWindow()
{
if (!(HWND)m_cView)
{
return(E_UNEXPECTED);
}

m_cView.DestroyWindow();

return(NOERROR);
}

STDMETHODIMP CSFView::GetCurrentInfo(LPFOLDERSETTINGS lpfs)
{
*lpfs = m_fs;

return(NOERROR);
}

STDMETHODIMP CSFView::AddPropertySheetPages(DWORD dwReserved,
                LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam)
{
return(E_NOTIMPL);
}

STDMETHODIMP CSFView::SaveViewState()
{
SFSTATE_HDR hdr;
LPSTREAM pstm;

HRESULT hres = m_psb->GetViewStateStream(STGM_WRITE, &pstm);
if (FAILED(hres))
{
return(hres);
}
CEnsureRelease erStr(pstm);

pstm->Write(&hdr, sizeof(hdr), NULL);

hdr.clsThis = CLSID_ThisDll;
hdr.sfState = m_sfState;
hdr.nCols = SaveColumns(pstm);

ULARGE_INTEGER libCurPosition;
LARGE_INTEGER dlibMove;
dlibMove.HighPart = 0;
dlibMove.LowPart = 0;
pstm->Seek(dlibMove, STREAM_SEEK_SET, &libCurPosition);

hres = pstm->Write(&hdr, sizeof(hdr), NULL);

return(hres);
}

STDMETHODIMP CSFView::SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags)
{
return(E_NOTIMPL);
}

STDMETHODIMP CSFView::GetItemObject(UINT uItem, REFIID riid,
                LPVOID *ppv)
{
return(E_NOTIMPL);
}

int CSFView::AddObject(LPCITEMIDLIST pidl)
{
// Check the commdlg hook to see if we should include this
// object.
if (IncludeObject(pidl) != S_OK)
{
return(-1);
}

return(m_cView.AddObject(pidl));
}

int CALLBACK CSFView::CompareIDs(LPVOID p1, LPVOID p2, LPARAM lParam)
{
PFNDPACOMPARE pfnCheckAPI = CompareIDs;

CSFView *pThis = (CSFView *)lParam;

HRESULT hres = pThis->m_psf->CompareIDs(pThis->m_sfState.lParamSort,
(LPITEMIDLIST)p1, (LPITEMIDLIST)p2);

return (hres);
}

//*****************************************************************************
//
// CSFView::FillList
//
// Purpose:
//
//        Enumerates the objects in the namespace and fills up the
//        data structures
//
//
// Comments:
//
//*****************************************************************************

HRESULT CSFView::FillList(BOOL bInteractive)
{
m_cView.DeleteAllItems();

// Setup the enum flags.
DWORD dwEnumFlags = SHCONTF_NONFOLDERS;
if (ShowAllObjects())
{
dwEnumFlags |= SHCONTF_INCLUDEHIDDEN ;
}

if (!(m_fs.fFlags & FWF_NOSUBFOLDERS))
{
dwEnumFlags |= SHCONTF_FOLDERS;
}

// Create an enum object and get the IEnumIDList ptr
LPENUMIDLIST peIDL;
HRESULT hres = m_psf->EnumObjects(bInteractive ? m_hwndMain : NULL,
dwEnumFlags, &peIDL);

// Note the return may be S_FALSE which indicates no enumerator.
// That's why we shouldn't use if (FAILED(hres))
if (hres != S_OK)
{
if (hres == S_FALSE)
{
return(NOERROR);
}

return(hres);
}
CEnsureRelease erEnum(peIDL);

HDPA hdpaNew = DPA_Create(16);
if (!hdpaNew)
{
return(E_OUTOFMEMORY);
}

LPITEMIDLIST pidl;
ULONG celt;

// Enumerate the idlist and insert into the DPA

while (peIDL->Next(1, &pidl, &celt) == S_OK)
{
if (DPA_InsertPtr(hdpaNew, 0x7fff, pidl) == -1)
{
m_cMalloc.Free(pidl);
}
}

DPA_Sort(hdpaNew, CompareIDs, (LPARAM)this);

int cNew = DPA_GetPtrCount(hdpaNew);
for (int i=0; i<cNew; ++i)
{
LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_GetPtr(hdpaNew, i);
if (AddObject(pidl) < 0)
{
m_cMalloc.Free(pidl);
}
}

return(NOERROR);
}

//*****************************************************************************
//
// CSFView::AddColumns
//
// Purpose:
//
//        Adds columns to the contents pane listview
//
// Comments:
//
//*****************************************************************************
void CSFView::AddColumns()
{
UINT cxChar = m_cView.CharWidth();

// add columns to the listview in the contents pane
for (int i=0; ; ++i)
{
SFVCB_GETDETAILSOF_DATA gdo;
gdo.pidl = NULL;

// get the first column

HRESULT hres = CallCB(SFVCB_GETDETAILSOF, i, (LPARAM)&gdo);
if (hres != S_OK)
{
if (i != 0)
{
break;
}

// If there is no first column, fake one up
gdo.fmt = LVCFMT_LEFT;
gdo.cChar = 40;
gdo.lParamSort = 0;
gdo.str.uType = STRRET_CSTR;
LoadString(g_ThisDll.GetInstance(), IDS_NAME, gdo.str.cStr, sizeof(gdo.str.cStr));
}

char szText[MAX_PATH];
// init the column info for the details view ...
LV_COLUMN col;
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
col.fmt = gdo.fmt;
col.cx = gdo.cChar * cxChar;
col.pszText = szText;
col.cchTextMax = sizeof(szText);
col.iSubItem = i;

StrRetToStr(szText, sizeof(szText), &gdo.str, NULL);

// insert the column into the list view
if (m_cView.InsertColumn(i, &col)>=0 && m_aParamSort)
{
DPA_InsertPtr(m_aParamSort, 0x7fff, (LPVOID)gdo.lParamSort);
}

if (hres != S_OK)
{
break;
}
}
}

//
// Save (and check) column header information
// Returns TRUE if the columns are the default width, FALSE otherwise
// Side effect: the stream pointer is left right after the last column
//
BOOL CSFView::SaveColumns(LPSTREAM pstm)
{
UINT cxChar = m_cView.CharWidth();
BOOL bDefaultCols = TRUE;

for (int i=0; ; ++i)
{
SFVCB_GETDETAILSOF_DATA gdo;
gdo.pidl = NULL;

if (CallCB(SFVCB_GETDETAILSOF, i, (LPARAM)&gdo) != S_OK)
{
break;
}

LV_COLUMN col;
col.mask = LVCF_WIDTH;

if (!m_cView.GetColumn(i, &col))
{
// There is some problem, so just assume
// default column widths
bDefaultCols = TRUE;
break;
}

if (col.cx != (int)(gdo.cChar * cxChar))
{
bDefaultCols = FALSE;
}

// HACK: I don't really care about column widths larger
// than 64K
if (FAILED(pstm->Write(&col.cx, sizeof(USHORT), NULL)))
{
// There is some problem, so just assume
// default column widths
bDefaultCols = TRUE;
break;
}
}

return(bDefaultCols ? 0 : i);
}

void CSFView::RestoreColumns(LPSTREAM pstm, int nCols)
{
for (int i=0; i<nCols; ++i)
{
LV_COLUMN col;
col.mask = LVCF_WIDTH;

if (FAILED(pstm->Read(&col.cx, sizeof(USHORT), NULL)))
{
break;
}

m_cView.SetColumn(i, &col);
}
}

void CSFView::RestoreViewState()
{
SFSTATE_HDR hdr;

LPSTREAM pstm;
// get the stream for storing view specific info
if (FAILED(m_psb->GetViewStateStream(STGM_READ, &pstm)))
{
return;
}
CEnsureRelease erStr(pstm);

if (FAILED(pstm->Read(&hdr, sizeof(hdr), NULL)))
{
return;
}

// Validate the header
if (hdr.clsThis != CLSID_ThisDll)
{
return;
}

 m_sfState = hdr.sfState;
RestoreColumns(pstm, hdr.nCols);

MergeToolBar();
}

void CSFView::CheckToolbar()
{
UINT idCmdCurView = GetMenuIDFromViewMode();

for (UINT idCmd=IDC_VIEW_ICON; idCmd<=IDC_VIEW_DETAILS; ++idCmd)
{
m_psb->SendControlMsg(FCW_TOOLBAR, TB_CHECKBUTTON, idCmd,
(LPARAM)(idCmd == idCmdCurView), NULL);
}
}

void CSFView::MergeToolBar()
{
enum
{
IN_STD_BMP = 0x4000,
IN_VIEW_BMP = 0x8000,
} ;
static const TBBUTTON c_tbDefault[] =
{
{ STD_COPY | IN_STD_BMP, IDC_EDIT_COPY, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 0,    0,    TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1 },
// the bitmap indexes here are relative to the view bitmap
{ VIEW_LARGEICONS | IN_VIEW_BMP, IDC_VIEW_ICON,        TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
{ VIEW_SMALLICONS | IN_VIEW_BMP, IDC_VIEW_SMALLICON, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
{ VIEW_LIST       | IN_VIEW_BMP, IDC_VIEW_LIST,         TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
{ VIEW_DETAILS    | IN_VIEW_BMP, IDC_VIEW_DETAILS,      TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
} ;

LRESULT iStdBMOffset;
LRESULT iViewBMOffset;
TBADDBITMAP ab;
ab.hInst = HINST_COMMCTRL;// hinstCommctrl
ab.nID   = IDB_STD_SMALL_COLOR;// std bitmaps
m_psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &iStdBMOffset);

ab.nID   = IDB_VIEW_SMALL_COLOR;// std view bitmaps
m_psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &iViewBMOffset);

TBBUTTON tbActual[ARRAYSIZE(c_tbDefault)];

for (int i=0; i<ARRAYSIZE(c_tbDefault); ++i)
{
tbActual[i] = c_tbDefault[i];
if (!(tbActual[i].fsStyle & TBSTYLE_SEP))
{
if (tbActual[i].iBitmap & IN_VIEW_BMP)
{
tbActual[i].iBitmap = (tbActual[i].iBitmap & ~IN_VIEW_BMP) + iViewBMOffset;
}
else if (tbActual[i].iBitmap & IN_STD_BMP)
{
tbActual[i].iBitmap = (tbActual[i].iBitmap & ~IN_STD_BMP) + iStdBMOffset;
}
}
}

m_psb->SetToolbarItems(tbActual, ARRAYSIZE(c_tbDefault), FCT_MERGE);

CheckToolbar();
}

HRESULT CreateShellFolderView(LPSHELLFOLDER psf, IShellFolderViewCallback *psfvcb,
LPSHELLVIEW * ppsv)
{
CSFView *pSFView = new CSFView(psf, psfvcb);
if (!pSFView)
{
return(E_OUTOFMEMORY);
}

pSFView->AddRef();
HRESULT hRes = pSFView->QueryInterface(IID_IShellView, (LPVOID *)ppsv);
pSFView->Release();

return(hRes);
}


SFVIEW.H

//*******************************************************************************************
//
// Filename : SFView.h
//
//Definition of IShellFolderViewCallback
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

// 1C503180-E9F5-11CE-AE65-08002B2E1262
DEFINE_GUID(IID_IShellFolderViewCallback, 0x1C503180L, 0xE9F5, 0x11CE, 0xAE, 0x65, 0x08, 0x00, 0x2B, 0x2E, 0x12, 0x62);

#ifndef _SFView_H_
#define _SFView_H_

#undef  INTERFACE
#define INTERFACE IShellFolderViewCallback

DECLARE_INTERFACE_(IShellFolderViewCallback, IUnknown)
{
// *** IUnknown methods ***
STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE;
STDMETHOD_(ULONG, AddRef) (THIS)  PURE;
STDMETHOD_(ULONG, Release) (THIS) PURE;

// *** IShellFolderViewCallback methods ***
STDMETHOD(Message)(THIS_ UINT uMsg, WPARAM wParam, LPARAM lParam) PURE;
};

struct SFVCB_GETDETAILSOF_DATA
{
// In:
LPCITEMIDLIST pidl;// NULL if column header

// Out:
int fmt;// A LVCFMT_* value if pidl == NULL
UINT cChar;// Default width in chars if pidl == NULL
LPARAM lParamSort;// The lParam for IShellFolder::CompareIDs if pidl == NULL
STRRET str;// The string to display
} ;

#define HANDLE_SFVCB_MSG(message, fn)    \
    case (message): return HANDLE_##message((wParam), (lParam), (fn))

#define SFVCB_GETDETAILSOF 1
// wParam is the index of the column
// lParam is a pointer to SFVCB_GETDETAILSOF_DATA
// return is an error code if the column is out of range
// HRESULT OnGetDetailsOf(UINT uColumn, SFVCB_GETDETAILSOF_DATA *pData)
#define HANDLE_SFVCB_GETDETAILSOF(_wp, _lp, _fn) ((_fn)((UINT)(_wp), \
(SFVCB_GETDETAILSOF_DATA*)(_lp)))

HRESULT CreateShellFolderView(LPSHELLFOLDER psf,
IShellFolderViewCallback *psfvcb, LPSHELLVIEW * ppsv);

#define SFVID_FIRST (FCIDM_SHVIEWFIRST + 0x2000)
#define SFVID_LAST  (FCIDM_SHVIEWFIRST + 0x3000)

#define SFVID_MENU_ARRANGE (SFVID_FIRST + 0x000)

#endif // _SFView_H_


SFVMENU.CPP

//*******************************************************************************************
//
// Filename : SFVMenu.cpp
//
//Implementation file for CSFView menu related methods
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "Pch.H"

#include "SFView.H"
#include "SFVWnd.H"

#include "Resource.H"

enum
{
SUBMENU_EDIT,
SUBMENU_VIEW,
} ;

enum
{
SUBMENUNF_EDIT,
SUBMENUNF_VIEW,
} ;

static HMENU _GetMenuFromID(HMENU hmMain, UINT uID)
{
MENUITEMINFO miiSubMenu;

miiSubMenu.cbSize = sizeof(MENUITEMINFO);
miiSubMenu.fMask  = MIIM_SUBMENU;
miiSubMenu.cch = 0;     // just in case

if (!GetMenuItemInfo(hmMain, uID, FALSE, &miiSubMenu))
{
return NULL;
}

return(miiSubMenu.hSubMenu);
}

static int _GetOffsetFromID(HMENU hmMain, UINT uID)
{
int index;

for (index = GetMenuItemCount(hmMain)-1; index>=0; index--)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_ID;
mii.cch = 0;// just in case

if (GetMenuItemInfo(hmMain, (UINT)index, TRUE, &mii)
&& (mii.wID == FCIDM_MENU_VIEW_SEP_OPTIONS))
{
// merge it right above the separator.
break;
}
}

return(index);
}

int CSFView::GetMenuIDFromViewMode()
{
switch (m_fs.ViewMode)
{
case FVM_SMALLICON:
return(IDC_VIEW_SMALLICON);
case FVM_LIST:
return(IDC_VIEW_LIST);
case FVM_DETAILS:
return(IDC_VIEW_DETAILS);
default:
return(IDC_VIEW_ICON);
}
}

BOOL CSFView::GetArrangeText(int iCol, UINT idFmt, LPSTR pszText, UINT cText)
{
char szFormat[200];
LoadString(g_ThisDll.GetInstance(), idFmt, szFormat, sizeof(szFormat));

LV_COLUMN col;
char szText[80];

col.mask = LVCF_TEXT;
col.pszText = szText;
col.cchTextMax = sizeof(szText);

if (!m_cView.GetColumn(iCol, &col))
{
return(FALSE);
}

char szCommand[sizeof(szFormat)+sizeof(szText)];
wsprintf(szCommand, szFormat, szText);

lstrcpyn(pszText, szCommand, cText);

return(TRUE);
}

void CSFView::MergeArrangeMenu(HMENU hmView)
{
// Now add the sorting menu items
for (int i=0; i<MAX_COL; ++i)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);

char szCommand[100];
if (!GetArrangeText(i, IDS_BYCOL_FMT, szCommand, sizeof(szCommand)))
{
mii.fMask = MIIM_TYPE;
mii.fType = MFT_SEPARATOR;
InsertMenuItem(hmView, IDC_ARRANGE_AUTO, FALSE, &mii);

break;
}

mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
mii.fType = MFT_STRING;
mii.fState = MF_ENABLED;
mii.wID = IDC_ARRANGE_BY + i;
mii.dwTypeData = szCommand;

InsertMenuItem(hmView, IDC_ARRANGE_AUTO, FALSE, &mii);
}
}

void CSFView::MergeViewMenu(HMENU hmenu, HMENU hmMerge)
{
HMENU hmView = _GetMenuFromID(hmenu, FCIDM_MENU_VIEW);
if (!hmView)
{
return;
}

int iOptions = _GetOffsetFromID(hmView, FCIDM_MENU_VIEW_SEP_OPTIONS);

Cab_MergeMenus(hmView, hmMerge, (UINT)iOptions, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST,
MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR);

MergeArrangeMenu(hmView);
}

//*****************************************************************************
//
// CSFView::OnActivate
//
// Purpose:
//
//        handles the UIActivate call by ShellBrowser
//
// Parameters:
//
//    UINT  uState    -    UIActivate flag
//
// Comments:
//
//*****************************************************************************

BOOL CSFView::OnActivate(UINT uState)
{
if (m_uState == uState)
{
return(TRUE);
}

OnDeactivate();

{
// Scope cMenu
CMenuTemp cMenu(CreateMenu());

if (!((HMENU)cMenu))
{
return(TRUE);
}

OLEMENUGROUPWIDTHS mwidth = { { 0, 0, 0, 0, 0, 0 } };

// insert menus into the shell browser
// Explorer returns the wrong value so don't check
m_psb->InsertMenusSB(cMenu, &mwidth);

// store this current menu
m_cmCur.Attach(cMenu.Detach());
}

// Get the edit menu in the browser
HMENU hmEdit = _GetMenuFromID(m_cmCur, FCIDM_MENU_EDIT);

if (uState == SVUIA_ACTIVATE_FOCUS)
{
// load the menu resource
CMenuTemp cmMerge(LoadMenu(g_ThisDll.GetInstance(),
MAKEINTRESOURCE(MENU_SFV_MAINMERGE)));
if ((HMENU)cmMerge)
{
// merge it with Edit submenu
Cab_MergeMenus(hmEdit, GetSubMenu(cmMerge, SUBMENU_EDIT), (UINT)-1,
FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);

// add the view menu
MergeViewMenu(m_cmCur, GetSubMenu(cmMerge, SUBMENU_VIEW));
}
}
else
{
CMenuTemp cmMerge(LoadMenu(g_ThisDll.GetInstance(),
MAKEINTRESOURCE(MENU_SFV_MAINMERGENF)));
if ((HMENU)cmMerge)
{
Cab_MergeMenus(hmEdit, GetSubMenu(cmMerge, SUBMENUNF_EDIT), (UINT)-1,
FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);

// view menu
MergeViewMenu(m_cmCur, GetSubMenu(cmMerge, SUBMENUNF_VIEW));
}
}

// install the composite menu into the shell browser
m_psb->SetMenuSB(m_cmCur, NULL, m_cView);
m_uState = uState;

return TRUE;
}

BOOL CSFView::OnDeactivate()
{
if (m_uState != SVUIA_DEACTIVATE)
{
m_psb->SetMenuSB(NULL, NULL, NULL);
m_psb->RemoveMenusSB(m_cmCur);
DestroyMenu(m_cmCur.Detach());
m_uState = SVUIA_DEACTIVATE;
}

return TRUE;
}

//*****************************************************************************
//
// CSFView::OnCommand
//
// Purpose:
//
//       handle the WM_COMMAND sent by the explorer to the view
//
// Comments:
//
//*****************************************************************************

void CSFView::OnCommand(IContextMenu *pcm, WPARAM wParam, LPARAM lParam)
{
if (!pcm)
{
pcm = m_pcmSel;
}

if (pcm)
{
pcm->AddRef();
}
CEnsureRelease erContext(pcm);

int idCmd = GET_WM_COMMAND_ID(wParam, lParam);
DWORD dwStyle;

switch (idCmd)
{

    // Set the FOLDERSETTINGS for this view

case IDC_VIEW_ICON + FCIDM_SHVIEWFIRST:
dwStyle = LVS_ICON;
m_fs.ViewMode = FVM_ICON;
goto SetStyle;

case IDC_VIEW_SMALLICON + FCIDM_SHVIEWFIRST:
dwStyle = LVS_SMALLICON;
m_fs.ViewMode = FVM_SMALLICON;
goto SetStyle;

case IDC_VIEW_LIST + FCIDM_SHVIEWFIRST:
dwStyle = LVS_LIST;
m_fs.ViewMode = FVM_LIST;
goto SetStyle;

case IDC_VIEW_DETAILS + FCIDM_SHVIEWFIRST:
dwStyle = LVS_REPORT;
m_fs.ViewMode = FVM_DETAILS;
goto SetStyle;

// set the style of the Listview accordingly
SetStyle:
m_cView.SetStyle(dwStyle, LVS_TYPEMASK);
CheckToolbar();
break;

// handle the Copy operation
case IDC_EDIT_COPY + FCIDM_SHVIEWFIRST:
{
LPDATAOBJECT pdtobj;
if (!m_cView.OleInited()
|| FAILED(m_cView.GetUIObjectFromItem(IID_IDataObject, (LPVOID*)&pdtobj,
SVGIO_SELECTION)))
{
MessageBeep(0);
break;
}
CEnsureRelease erData(pdtobj);

OleSetClipboard(pdtobj);
break;
}

    // handle selection of items
case IDC_EDIT_SELALL + FCIDM_SHVIEWFIRST:
SetFocus(m_cView);
m_cView.SelAll();
break;

case IDC_EDIT_INVSEL + FCIDM_SHVIEWFIRST:
SetFocus(m_cView);
m_cView.InvSel();
break;

default:
if (idCmd>=IDC_ARRANGE_BY && idCmd<IDC_ARRANGE_BY+MAX_COL)
{
ColumnClick(idCmd - IDC_ARRANGE_BY);
}
else if (pcm && idCmd>=SFV_CONTEXT_FIRST && idCmd<=SFV_CONTEXT_LAST)
{

// invoke the context menu
CMINVOKECOMMANDINFO ici;

ici.cbSize = sizeof(ici);
ici.fMask = 0;
ici.hwnd = m_hwndMain;
ici.lpVerb = MAKEINTRESOURCE(idCmd - SFV_CONTEXT_FIRST);
ici.lpParameters = NULL;
ici.lpDirectory = NULL;
ici.nShow = SW_SHOWNORMAL;

pcm->InvokeCommand(&ici);
}
break;
}
}

IContextMenu * CSFView::GetSelContextMenu()
{
if (!m_pcmSel)
{
if (FAILED(m_cView.GetUIObjectFromItem(IID_IContextMenu, (LPVOID *)&m_pcmSel,
SVGIO_SELECTION)))
{
m_pcmSel = NULL;
return(m_pcmSel);
}
}

m_pcmSel->AddRef();
return(m_pcmSel);
}

void CSFView::ReleaseSelContextMenu()
{
if (m_pcmSel)
{
m_pcmSel->Release();
m_pcmSel = NULL;
}
}

void CSFView::InitFileMenu(HMENU hmInit)
{
//
// Don't touch the file menu unless we have the focus.
//
if (m_uState != SVUIA_ACTIVATE_FOCUS)
{
return;
}

BOOL bDeleteItems = FALSE;
int i;

// Remove all the menu items we've added.
for (i = GetMenuItemCount(hmInit) - 1; i >= 0; --i)
{
if (!bDeleteItems)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_ID;
mii.cch = 0;     // just in case

if (GetMenuItemInfo(hmInit, i, TRUE, &mii))
{
if (mii.wID>=SFV_CONTEXT_FIRST && mii.wID<=SFV_CONTEXT_LAST)
{
bDeleteItems = TRUE;
}
}
}

if (bDeleteItems)
{
DeleteMenu(hmInit, i, MF_BYPOSITION);
}
}

// Let the object add the separator.
if (CSFViewDlg::IsMenuSeparator(hmInit, 0))
{
DeleteMenu(hmInit, 0, MF_BYPOSITION);
}

//
// Now add item specific commands to the menu
// This is done by seeing if we already have a context menu
// object for our selection.  If not we generate it now.
//
IContextMenu *pcmSel = GetSelContextMenu();
if (pcmSel)
{
pcmSel->QueryContextMenu(hmInit, 0, SFV_CONTEXT_FIRST,
SFV_CONTEXT_LAST, CMF_DVFILE);
pcmSel->Release();
}

// Note that the SelContextMenu stays around until the selection changes or we
// close the window, but it doesn't really matter that much
}

void CSFView::InitEditMenu(HMENU hmInit)
{
ULONG dwAttr = SFGAO_CANCOPY;
UINT uFlags = (m_cView.OleInited()
&& SUCCEEDED(m_cView.GetAttributesFromItem(&dwAttr, SVGIO_SELECTION))
&& (dwAttr & SFGAO_CANCOPY)) ? MF_ENABLED : MF_GRAYED;

EnableMenuItem(hmInit, IDC_EDIT_COPY + FCIDM_SHVIEWFIRST, uFlags | MF_BYCOMMAND);
}

void CSFView::InitViewMenu(HMENU hmInit)
{
int iCurViewMenuItem = GetMenuIDFromViewMode() + FCIDM_SHVIEWFIRST;
UINT uEnable;

CheckMenuRadioItem(hmInit, IDC_VIEW_ICON, IDC_VIEW_DETAILS,
iCurViewMenuItem, MF_BYCOMMAND | MF_CHECKED);

uEnable = (iCurViewMenuItem==IDC_VIEW_LIST+FCIDM_SHVIEWFIRST
|| iCurViewMenuItem==IDC_VIEW_DETAILS+FCIDM_SHVIEWFIRST) ?
(MF_GRAYED | MF_BYCOMMAND)  :  (MF_ENABLED | MF_BYCOMMAND);
uEnable = MF_GRAYED | MF_BYCOMMAND;

EnableMenuItem(hmInit, IDC_ARRANGE_GRID + FCIDM_SHVIEWFIRST, uEnable);
EnableMenuItem(hmInit, IDC_ARRANGE_AUTO + FCIDM_SHVIEWFIRST, uEnable);
CheckMenuItem(hmInit, IDC_ARRANGE_AUTO + FCIDM_SHVIEWFIRST,
((uEnable == (MF_ENABLED | MF_BYCOMMAND)) && (m_fs.fFlags & FWF_AUTOARRANGE))
? MF_CHECKED : MF_UNCHECKED);
}

//*****************************************************************************
//
// CSFView::OnInitMenuPopup
//
// Purpose:
//
//    handle the WM_INITMENUPOPUP message received by CSFViewDlg
//
//
//*****************************************************************************
BOOL CSFView::OnInitMenuPopup(HMENU hmInit, int nIndex, BOOL fSystemMenu)
{
if (!(HMENU)m_cmCur)
{
return(TRUE);
}

MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_SUBMENU|MIIM_ID;
mii.cch = 0;     // just in case

if (!GetMenuItemInfo(m_cmCur, nIndex, TRUE, &mii) || mii.hSubMenu!=hmInit)
{
return(TRUE);
}

switch (mii.wID)
{
case FCIDM_MENU_FILE:
InitFileMenu(hmInit);
break;

case FCIDM_MENU_EDIT:
InitEditMenu(hmInit);
break;

case FCIDM_MENU_VIEW:
InitViewMenu(hmInit);
break;

default:
return 1L;
}

return 0L;
}

int _FindIt(const UINT *puFirst, UINT uFind, UINT uStep, int cCount)
{
LPBYTE pbFirst = (LPBYTE)puFirst;

for (--cCount, pbFirst+=cCount*uStep; cCount>=0; --cCount, pbFirst-=uStep)
{
if (*(UINT *)pbFirst == uFind)
{
break;
}
}

return(cCount);
}

const struct
{
UINT idCmd;
UINT idStr;
UINT idTT;
} c_idTbl[] =
{
IDC_VIEW_ICON, IDS_VIEW_ICON, IDS_TT_VIEW_ICON,
IDC_VIEW_SMALLICON, IDS_VIEW_SMALLICON, IDS_TT_VIEW_SMALLICON,
IDC_VIEW_LIST, IDS_VIEW_LIST, IDS_TT_VIEW_LIST,
IDC_VIEW_DETAILS, IDS_VIEW_DETAILS, IDS_TT_VIEW_DETAILS,
IDC_EDIT_COPY, IDS_EDIT_COPY, IDS_TT_EDIT_COPY,
IDC_EDIT_SELALL, IDS_EDIT_SELALL, 0,
IDC_EDIT_INVSEL, IDS_EDIT_INVSEL, 0,
IDC_ARRANGE_GRID, IDS_ARRANGE_GRID, 0,
IDC_ARRANGE_AUTO, IDS_ARRANGE_AUTO, 0,
} ;

void CSFView::GetCommandHelpText(UINT idCmd, LPSTR pszText, UINT cchText, BOOL bToolTip)
{
*pszText = 0;

if (idCmd>=SFV_CONTEXT_FIRST && idCmd<=SFV_CONTEXT_LAST && m_pcmSel)
{
if (bToolTip)
{
return;
}

m_pcmSel->GetCommandString(idCmd - SFV_CONTEXT_FIRST, GCS_HELPTEXT, NULL,
pszText, cchText);
}
else if (idCmd>=IDC_ARRANGE_BY && idCmd<IDC_ARRANGE_BY+MAX_COL)
{
if (bToolTip)
{
return;
}

GetArrangeText(idCmd-IDC_ARRANGE_BY, IDS_BYCOL_HELP_FMT, pszText, cchText);
}
else
{
int iid = _FindIt(&c_idTbl[0].idCmd, idCmd-FCIDM_SHVIEWFIRST, sizeof(c_idTbl[0]),
ARRAYSIZE(c_idTbl));
if (iid >= 0)
{
LoadString(g_ThisDll.GetInstance(),
bToolTip ? c_idTbl[iid].idTT : c_idTbl[iid].idStr, pszText, cchText);
}
}
}

LRESULT CSFView::OnMenuSelect(UINT idCmd, UINT uFlags, HMENU hmenu)
{
// If we dismissed the menu restore our status bar...
if (!hmenu && LOWORD(uFlags)==0xffff)
{
m_psb->SendControlMsg(FCW_STATUS, SB_SIMPLE, 0, 0L, NULL);
return 0L;
}

if (uFlags & (MF_SYSMENU | MF_SEPARATOR))
{
return 0L;
}

char szHelpText[80 + 2*MAX_PATH];
szHelpText[0] = 0;   // in case of failures below

if (uFlags & MF_POPUP)
{
MENUITEMINFO miiSubMenu;

miiSubMenu.cbSize = sizeof(MENUITEMINFO);
miiSubMenu.fMask = MIIM_ID;
miiSubMenu.cch = 0;     // just in case

if (!GetMenuItemInfo(hmenu, idCmd, TRUE, &miiSubMenu))
{
return 0;
}

// Change the parameters to simulate a "normal" menu item
idCmd = miiSubMenu.wID;
uFlags &= ~MF_POPUP;
}

GetCommandHelpText(idCmd, szHelpText, sizeof(szHelpText), FALSE);
m_psb->SendControlMsg(FCW_STATUS, SB_SETTEXT, SBT_NOBORDERS | 255,
(LPARAM)szHelpText, NULL);

m_psb->SendControlMsg(FCW_STATUS, SB_SIMPLE, 1, 0L, NULL);

return 0;
}


SFVWND.CPP

//*******************************************************************************************
//
// Filename : SFVWnd.cpp
//
//Implementation file for CSFVDropSource and CSFViewDlg
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "Pch.H"

#include "SFVWnd.H"

#include "ThisDll.h"

#include "Resource.H"

class CSFVDropSource : public CUnknown, public IDropSource
{
public:
CSFVDropSource() : m_grfInitialKeyState(0) {}
virtual ~CSFVDropSource();

STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();

STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);
STDMETHODIMP GiveFeedback(DWORD dwEffect);

private:
DWORD m_grfInitialKeyState;
} ;

CSFVDropSource::~CSFVDropSource()
{
}

STDMETHODIMP CSFVDropSource::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
static const IID *apiid[] = { &IID_IDropSource, NULL };
LPUNKNOWN aobj[] = { (IDropSource *)this };

return(QIHelper(riid, ppvObj, apiid, aobj));
}

STDMETHODIMP_(ULONG) CSFVDropSource::AddRef()
{
return(AddRefHelper());
}

STDMETHODIMP_(ULONG) CSFVDropSource::Release()
{
return(ReleaseHelper());
}

STDMETHODIMP CSFVDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
if (fEscapePressed)
{
return(DRAGDROP_S_CANCEL);
}

// initialize ourself with the drag begin button
if (m_grfInitialKeyState == 0)
{
m_grfInitialKeyState = (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
}

if (!(grfKeyState & m_grfInitialKeyState))
{
return(DRAGDROP_S_DROP);
}

return(S_OK);
}

STDMETHODIMP CSFVDropSource::GiveFeedback(DWORD dwEffect)
{
return(DRAGDROP_S_USEDEFAULTCURSORS);
}

// Note that the OLESTR gets freed, so don't try to use it later
BOOL StrRetToStr(LPSTR szOut, UINT uszOut, LPSTRRET pStrRet, LPCITEMIDLIST pidl)
{
    switch (pStrRet->uType)
    {
    case STRRET_OLESTR:
    {
        CSafeMalloc sm;

    OleStrToStrN(szOut, uszOut, pStrRet->pOleStr, -1);
sm.Free(pStrRet->pOleStr);

    break;
    }

    case STRRET_CSTR:
    lstrcpyn(szOut, pStrRet->cStr, uszOut);
    break;

    case STRRET_OFFSET:
    if (pidl)
    {
        lstrcpyn(szOut, ((LPCSTR)&pidl->mkid)+pStrRet->uOffset, uszOut);
        break;
    }

    // Fall through
    default:
    if (uszOut)
    {
        *szOut = '\0';
    }
    return(FALSE);
    }

    return(TRUE);
}

static void _PrettyMenu(HMENU hm)
{
BOOL bSeparated = TRUE;
int i;

for (i=GetMenuItemCount(hm)-1; i>0; --i)
{
if (CSFViewDlg::IsMenuSeparator(hm, i))
{
if (bSeparated)
{
DeleteMenu(hm, i, MF_BYPOSITION);
}

bSeparated = TRUE;
}
else
{
bSeparated = FALSE;
}
}

// The above loop does not handle the case of many separators at
// the beginning of the menu
while (CSFViewDlg::IsMenuSeparator(hm, 0))
{
DeleteMenu(hm, 0, MF_BYPOSITION);
}
}

static HMENU _LoadPopupMenu(UINT id, UINT uSubMenu)
{
    HMENU hmParent = LoadMenu(g_ThisDll.GetInstance(), MAKEINTRESOURCE(id));
    if (!hmParent)
    {
return(NULL);
}

    HMENU hmPopup = GetSubMenu(hmParent, 0);
    RemoveMenu(hmParent, uSubMenu, MF_BYPOSITION);
    DestroyMenu(hmParent);

    return(hmPopup);
}

void CSFViewDlg::InitDialog()
{
m_cList.Init(GetDlgItem(m_hDlg, IDC_LISTVIEW), GetDlgItem(m_hDlg, IDC_LISTBOX),
IDI_GENERIC);

SetWindowLong(m_cList, GWL_EXSTYLE,
GetWindowLong(m_cList, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);

m_hrOLE = OleInitialize(NULL);
}

LRESULT CSFViewDlg::BeginDrag()
{
if (!OleInited())
{
return(0);
}

// Get the dwEffect from the selection.
ULONG dwEffect = DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY;
GetAttributesFromItem(&dwEffect, SVGIO_SELECTION);

// Just in case
dwEffect &= DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY;
if (!dwEffect)
{
return(0);
}

LPDATAOBJECT pdtobj;
if (FAILED(GetUIObjectFromItem(IID_IDataObject, (LPVOID*)&pdtobj, SVGIO_SELECTION)))
{
return(0);
}
CEnsureRelease erData(pdtobj);

CSFVDropSource *pcsrc = new CSFVDropSource;
if (!pcsrc)
{
return(0);
}
pcsrc->AddRef();
CEnsureRelease erCDSrc((IDropSource*)pcsrc);

IDropSource *pdsrc;
HRESULT hres = pcsrc->QueryInterface(IID_IDropSource, (LPVOID*)&pdsrc);
if (FAILED(hres))
{
return(0);
}
CEnsureRelease erPDSrc(pdsrc);

DoDragDrop(pdtobj, pdsrc, dwEffect, &dwEffect);

return(0);
}

BOOL CSFViewDlg::Notify(LPNMHDR pNotify)
{
LPNM_LISTVIEW pNotifyLV = (LPNM_LISTVIEW)pNotify;
LV_DISPINFO *pNotifyDI = (LV_DISPINFO *)pNotify;
LPTBNOTIFY pNotifyTB = (LPTBNOTIFY)pNotify;
LPTOOLTIPTEXT pNotifyTT = (LPTOOLTIPTEXT)pNotify;

switch(pNotify->code)
{
case TTN_NEEDTEXT:
m_psfv->GetCommandHelpText(pNotifyTT->hdr.idFrom, pNotifyTT->szText,
sizeof(pNotifyTT->szText), TRUE);
break;

case TBN_BEGINDRAG:
m_psfv->OnMenuSelect(pNotifyTB->iItem, 0, 0);
break;

case LVN_DELETEITEM:
m_psfv->m_cMalloc.Free((LPITEMIDLIST)pNotifyLV->lParam);
break;

case LVN_GETDISPINFO:
if (pNotifyDI->item.iSubItem == 0)
{
LPCITEMIDLIST pidl = (LPCITEMIDLIST)pNotifyDI->item.lParam;
if (pNotifyDI->item.mask & LVIF_TEXT)
{
STRRET strret;

if (FAILED(m_psfv->m_psf->GetDisplayNameOf(pidl, 0, &strret)))
{
lstrcpyn(pNotifyDI->item.pszText, "", pNotifyDI->item.cchTextMax);
}
else
{
StrRetToStr(pNotifyDI->item.pszText, pNotifyDI->item.cchTextMax,
&strret, pidl);
}
}

if (pNotifyDI->item.mask & LVIF_IMAGE)
{
// Get the image
pNotifyDI->item.iImage = m_cList.GetIcon(m_psfv->m_psf, pidl);
}

pNotifyDI->item.mask |= LVIF_DI_SETITEM;
}
else if (pNotifyDI->item.mask & LVIF_TEXT)
{
SFVCB_GETDETAILSOF_DATA gdo;
gdo.pidl = (LPCITEMIDLIST)pNotifyDI->item.lParam;

if (m_psfv->CallCB(SFVCB_GETDETAILSOF, pNotifyDI->item.iSubItem, (LPARAM)&gdo)
== S_OK)
{
StrRetToStr(pNotifyDI->item.pszText, pNotifyDI->item.cchTextMax,
&gdo.str, gdo.pidl);
}
}
break;

case LVN_COLUMNCLICK:
m_psfv->ColumnClick(pNotifyLV->iSubItem);
break;

case LVN_ITEMCHANGED:
// We only care about STATECHANGE messages
if (!(pNotifyLV->uChanged & LVIF_STATE))
{
// If the text is changed, we need to flush the cached
// context menu.
if (pNotifyLV->uChanged & LVIF_TEXT)
{
m_psfv->ReleaseSelContextMenu();
}
break;
}

// tell commdlg that selection may have changed
m_psfv->OnStateChange(CDBOSC_SELCHANGE);

// The rest only cares about SELCHANGE messages
if ((pNotifyLV->uNewState^pNotifyLV->uOldState) & LVIS_SELECTED)
{
m_psfv->ReleaseSelContextMenu();
}

break;

case LVN_BEGINDRAG:
case LVN_BEGINRDRAG:
return(BeginDrag());

    case NM_RETURN:
    case NM_DBLCLK:
        ContextMenu((DWORD)-1, TRUE);
        break;

default:
return(FALSE);
}

return(TRUE);
}

void CSFViewDlg::ContextMenu(DWORD dwPos, BOOL bDoDefault)
{
int idDefault = -1;
int nInsert;
UINT fFlags = 0;
POINT pt;

// Find the selected item
int iItem = ListView_GetNextItem(m_cList, -1, LVNI_SELECTED);

if (dwPos == (DWORD) -1)
{
if (iItem != -1)
{
RECT rc;
int iItemFocus = ListView_GetNextItem(m_cList, -1, LVNI_FOCUSED|LVNI_SELECTED);
if (iItemFocus == -1)
{
iItemFocus = iItem;
}

// Note that ListView_GetItemRect returns client coordinates
ListView_GetItemRect(m_cList, iItemFocus, &rc, LVIR_ICON);
pt.x = (rc.left+rc.right)/2;
pt.y = (rc.top+rc.bottom)/2;
}
else
{
pt.x = pt.y = 0;
}

MapWindowPoints(m_cList, HWND_DESKTOP, &pt, 1);
}
else
{
pt.x = LOWORD(dwPos);
pt.y = HIWORD(dwPos);
}

CMenuTemp cmContext(CreatePopupMenu());
if (!(HMENU)cmContext)
{
// There should be an error message here
return;
}

LPCONTEXTMENU pcm = NULL;

if (iItem == -1)
{
// No selected item; use the background context menu
nInsert = -1;

CMenuTemp cmMerge(_LoadPopupMenu(MENU_SFV, 0));
if (!(HMENU)cmMerge)
{
//  There should be an error message here
return;
}

Cab_MergeMenus(cmContext, cmMerge, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, 0);
m_psfv->MergeArrangeMenu(cmContext);
m_psfv->InitViewMenu(cmContext);

if (FAILED(m_psfv->m_psf->CreateViewObject(m_psfv->m_hwndMain, IID_IContextMenu,
(LPVOID *)&pcm)))
{
pcm = NULL;
}
}
else
{
nInsert = 0;

pcm = m_psfv->GetSelContextMenu();
}
CEnsureRelease erPCM(pcm);

if (pcm)
{
if (m_psfv->m_psb)
{
// Determine whether we are in Explorer mode
HWND hwnd = NULL;
m_psfv->m_psb->GetControlWindow(FCW_TREE, &hwnd);
if (hwnd)
{
fFlags |= CMF_EXPLORE;
}
}

pcm->QueryContextMenu(cmContext, nInsert,
SFV_CONTEXT_FIRST, SFV_CONTEXT_LAST, fFlags);

// If this is the common dialog browser, we need to make the
// default command "Select" so that double-clicking (which is
// open in common dialog) makes sense.
if (m_psfv->IsInCommDlg() && iItem!=-1)
{
// make sure this is an item
CMenuTemp cmSelect(_LoadPopupMenu(MENU_SFV, 1));

Cab_MergeMenus(cmContext, cmSelect, 0, 0, (UINT)-1, MM_ADDSEPARATOR);

SetMenuDefaultItem(cmContext, 0, MF_BYPOSITION);
}

idDefault = GetMenuDefaultItem(cmContext, MF_BYCOMMAND, 0);
}

_PrettyMenu(cmContext);

    int idCmd;
    if (bDoDefault)
    {
        if (idDefault < 0)
        {
            return;
        }

        idCmd = idDefault;
    }
    else
    {
    idCmd = TrackPopupMenu(cmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
    pt.x, pt.y, 0, m_psfv->m_cView, NULL);
    }

if ((idCmd==idDefault) && m_psfv->OnDefaultCommand()==S_OK)
{
// commdlg browser ate the default command
}
else if (idCmd == 0)
{
// No item selected
}
else
{
m_psfv->OnCommand(pcm, GET_WM_COMMAND_MPS(idCmd, 0, 0));
}
}

BOOL CSFViewDlg::RealDlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
InitDialog();
break;

case WM_DESTROY:
m_cList.DeleteAllItems();
if (OleInited())
{
OleUninitialize();
m_hrOLE = E_UNEXPECTED;
}
break;

case WM_NOTIFY:
SetWindowLong(m_hDlg, DWL_MSGRESULT, Notify((LPNMHDR)lParam));
break;

case WM_INITMENUPOPUP:
        m_psfv->OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam));
break;

case WM_COMMAND:
m_psfv->OnCommand(NULL, wParam, lParam);
break;

case WM_CONTEXTMENU:
ContextMenu(lParam);
break;

case WM_SIZE:
SetWindowPos(m_cList, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam),
SWP_NOZORDER|SWP_SHOWWINDOW);
break;

case WM_MENUSELECT:
    m_psfv->OnMenuSelect(GET_WM_MENUSELECT_CMD(wParam, lParam),
    GET_WM_MENUSELECT_FLAGS(wParam, lParam),
    GET_WM_MENUSELECT_HMENU(wParam, lParam));
    break;

default:
return(FALSE);
}

return(TRUE);
}

UINT CSFViewDlg::CharWidth()
{
HDC hdc = GetDC(m_cList);
SelectFont(hdc, FORWARD_WM_GETFONT(m_cList, SendMessage));

SIZE siz;
GetTextExtentPoint(hdc, "0", 1, &siz);
ReleaseDC(m_cList, hdc);

return(siz.cx);
}

int CSFViewDlg::AddObject(LPCITEMIDLIST pidl)
{
LV_ITEM item;

item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
item.iItem = 0x7fff;     // add at end
item.iSubItem = 0;

item.iImage = I_IMAGECALLBACK;
item.pszText = LPSTR_TEXTCALLBACK;
item.lParam = (LPARAM)pidl;

return(m_cList.InsertItem(&item));
}

LPCITEMIDLIST CSFViewDlg::GetPIDL(int iItem)
{
LV_ITEM item;

item.mask = LVIF_PARAM;
item.iItem = iItem;
item.iSubItem = 0;
item.lParam = 0;
if (iItem != -1)
{
ListView_GetItem(m_cList, &item);
}

return((LPCITEMIDLIST)item.lParam);
}

UINT CSFViewDlg::GetItemPIDLS(LPCITEMIDLIST apidl[], UINT cItemMax, UINT uItem)
{
// We should put the focused one at the top of the list.
int iItem = -1;
int iItemFocus = -1;
UINT cItem = 0;
UINT uType;

switch (uItem)
{
case SVGIO_SELECTION:
// special case for faster search
if (!cItemMax)
{
return ListView_GetSelectedCount(m_cList);
}
iItemFocus = ListView_GetNextItem(m_cList, -1, LVNI_FOCUSED);
uType = LVNI_SELECTED;
break;

case SVGIO_ALLVIEW:
// special case for faster search
if (!cItemMax)
{
return ListView_GetItemCount(m_cList);
}
uType = LVNI_ALL;
break;

default:
return(0);
}

while((iItem=ListView_GetNextItem(m_cList, iItem, uType)) != -1)
{
if (cItem < cItemMax)
{
// Check if the item is the focused one or not.
if (iItem == iItemFocus)
{
// Yes, put it at the top.
apidl[cItem] = apidl[0];
apidl[0] = GetPIDL(iItem);
}
else
{
// No, put it at the end of the list.
apidl[cItem] = GetPIDL(iItem);
}
}

cItem++;
}

return cItem;
}

HRESULT CSFViewDlg::GetItemObjects(LPCITEMIDLIST **ppidl, UINT uItem)
{
UINT cItems = GetItemPIDLS(NULL, 0, uItem);
LPCITEMIDLIST * apidl;

if (ppidl != NULL)
{
*ppidl = NULL;

if (cItems == 0)
{
return(ResultFromShort(0));  // nothing allocated...
}

apidl = new LPCITEMIDLIST[cItems];
if (!apidl)
{
return(E_OUTOFMEMORY);
}

*ppidl = apidl;
cItems = GetItemPIDLS(apidl, cItems, uItem);
}

return(ResultFromShort(cItems));
}

HRESULT CSFViewDlg::GetUIObjectFromItem(REFIID riid, LPVOID * ppv, UINT uItem)
{
LPCITEMIDLIST * apidl;
HRESULT hres = GetItemObjects(&apidl, uItem);
UINT cItems = ShortFromResult(hres);

if (FAILED(hres))
{
return(hres);
}

if (!cItems)
{
return(E_INVALIDARG);
}

hres = m_psfv->m_psf->GetUIObjectOf(m_psfv->m_hwndMain, cItems, apidl, riid, 0, ppv);

delete apidl;

return hres;
}

HRESULT CSFViewDlg::GetAttributesFromItem(ULONG *pdwAttr, UINT uItem)
{
LPCITEMIDLIST * apidl;
HRESULT hres = GetItemObjects(&apidl, uItem);
UINT cItems = ShortFromResult(hres);

if (FAILED(hres))
{
return(hres);
}

if (!cItems)
{
return(E_INVALIDARG);
}

hres = m_psfv->m_psf->GetAttributesOf(cItems, apidl, pdwAttr);

delete apidl;

return hres;
}

BOOL CSFViewDlg::IsMenuSeparator(HMENU hm, int i)
{
MENUITEMINFO mii;

mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_TYPE;
mii.cch = 0;    // WARNING: We MUST initialize it to 0!!!
if (!GetMenuItemInfo(hm, i, TRUE, &mii))
{
return(FALSE);
}

if (mii.fType & MFT_SEPARATOR)
{
return(TRUE);
}

return(FALSE);
}


SFVWND.H

//*******************************************************************************************
//
// Filename : SFVWnd.h
//
//Definitions of CListView, CSFViewDlg, CAccelerator, CSFView
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _SFVWnd_H_
#define _SFVWnd_H_

#include "ThisDll.H"

#include "SFView.H"

#include "XIcon.H"
#include "Dlg.H"
#include "Unknown.H"

#define IDC_ARRANGE_BY (FCIDM_SHVIEWFIRST + 0x100)
#define MAX_COL 0x20

#define SFV_CONTEXT_FIRST (FCIDM_SHVIEWFIRST + 0x1000)
#define SFV_CONTEXT_LAST (FCIDM_SHVIEWFIRST + 0x2000)

BOOL StrRetToStr(LPSTR szOut, UINT uszOut, LPSTRRET pStrRet, LPCITEMIDLIST pidl);

class CListView
{
public:
CListView() {}
~CListView() {}

operator HWND() const {return(m_hwndList);}

void Init(HWND hwndList, HWND hwndLB, UINT idiDef)
{
m_hwndList = hwndList;

m_cxi.Init(hwndLB, idiDef);

ListView_SetImageList(hwndList, m_cxi.GetIML(TRUE), LVSIL_NORMAL);
ListView_SetImageList(hwndList, m_cxi.GetIML(FALSE), LVSIL_SMALL);
}

int InsertItem(LV_ITEM *pItem)
{
return(ListView_InsertItem(m_hwndList, pItem));
}

void DeleteAllItems() {ListView_DeleteAllItems(m_hwndList);}

enum
{
AI_LARGE = CXIcon::AI_LARGE,
AI_SMALL = CXIcon::AI_SMALL,
} ;

int GetIcon(IShellFolder *psf, LPCITEMIDLIST pidl)
{
return(m_cxi.GetIcon(psf, pidl));
}

private:
HWND m_hwndList;
CXIcon m_cxi;
} ;

class CSFViewDlg : public CDlg
{
public:
CSFViewDlg(class CSFView *psfv) : m_psfv(psfv), m_hrOLE(E_UNEXPECTED)
{
m_hDlg = NULL;
}
~CSFViewDlg() {}

operator HWND() const {return(m_hDlg);}

int AddObject(LPCITEMIDLIST pidl);
void DeleteAllItems() {m_cList.DeleteAllItems();}

BOOL DestroyWindow() {BOOL bRet=::DestroyWindow(m_hDlg); m_hDlg = NULL; return(bRet);}

void SetStyle(DWORD dwAdd, DWORD dwRemove)
{
SetWindowLong(m_cList, GWL_STYLE, dwAdd |
(GetWindowStyle(m_cList) & ~dwRemove));
}

void SelAll()
{
ListView_SetItemState(m_cList, -1, LVIS_SELECTED, LVIS_SELECTED);
}

void InvSel()
{
int iItem = -1;
while ((iItem=ListView_GetNextItem(m_cList, iItem, 0)) != -1)
{
UINT flag;

// flip the selection bit on each item
flag = ListView_GetItemState(m_cList, iItem, LVIS_SELECTED);
flag ^= LVNI_SELECTED;
ListView_SetItemState(m_cList, iItem, flag, LVIS_SELECTED);
}
}

UINT CharWidth();

BOOL GetColumn(int i, LV_COLUMN *pcol) {return(ListView_GetColumn(m_cList, i, pcol));}
BOOL SetColumn(int i, LV_COLUMN *pcol) {return(ListView_SetColumn(m_cList, i, pcol));}
UINT InsertColumn(int i, LV_COLUMN *pcol) {return(ListView_InsertColumn(m_cList, i, pcol));}

void SortItems(PFNDPACOMPARE pfnCmp) {ListView_SortItems(m_cList, pfnCmp, m_psfv);}

static BOOL IsMenuSeparator(HMENU hm, int i);

HRESULT GetUIObjectFromItem(REFIID riid, LPVOID * ppv, UINT uItem);
HRESULT GetAttributesFromItem(ULONG *pdwAttr, UINT uItem);

BOOL OleInited() {return(SUCCEEDED(m_hrOLE));}

private:
virtual BOOL RealDlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);

UINT GetItemPIDLS(LPCITEMIDLIST apidl[], UINT cItemMax, UINT uItem);
HRESULT GetItemObjects(LPCITEMIDLIST **ppidl, UINT uItem);

LPCITEMIDLIST GetPIDL(int iItem);

void InitDialog();
LRESULT BeginDrag();
BOOL Notify(LPNMHDR pNotify);
    void ContextMenu(DWORD dwPos, BOOL bDoDefault=FALSE);

CListView m_cList;
HRESULT m_hrOLE;

class CSFView *m_psfv;
} ;

struct SFSTATE
{
LPARAM lParamSort;
} ;

class CAccelerator
{
public:
CAccelerator(UINT uID)
{
m_hAccel = LoadAccelerators(g_ThisDll.GetInstance(), MAKEINTRESOURCE(uID));
}

int TranslateAccelerator(HWND hwnd, LPMSG pmsg)
{
if (!m_hAccel)
{
return(FALSE);
}

return(::TranslateAccelerator(hwnd, m_hAccel, pmsg));
}

private:
HACCEL m_hAccel;
} ;

//
// CSFView - IShellView implementation
//

class CSFView : public CUnknown, public IShellView
{
public:
CSFView(LPSHELLFOLDER psf, IShellFolderViewCallback *psfvcb);
virtual ~CSFView();

STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();

// *** IOleWindow methods ***
STDMETHODIMP GetWindow(HWND * lphwnd);
STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);

// *** IShellView methods ***
STDMETHODIMP TranslateAccelerator(LPMSG lpmsg);
STDMETHODIMP EnableModeless(BOOL fEnable);
STDMETHODIMP UIActivate(UINT uState);
STDMETHODIMP Refresh();

STDMETHODIMP CreateViewWindow(IShellView  *lpPrevView,
                LPCFOLDERSETTINGS lpfs, IShellBrowser  * psb,
                RECT * prcView, HWND  *phWnd);
STDMETHODIMP DestroyViewWindow();
STDMETHODIMP GetCurrentInfo(LPFOLDERSETTINGS lpfs);
STDMETHODIMP AddPropertySheetPages(DWORD dwReserved,
                LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam);
STDMETHODIMP SaveViewState();
STDMETHODIMP SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags);
STDMETHODIMP GetItemObject(UINT uItem, REFIID riid,
                LPVOID *ppv);

private:
static int CALLBACK CSFView::CompareIDs(LPVOID p1, LPVOID p2, LPARAM lParam);

void AddColumns();
BOOL SaveColumns(LPSTREAM pstm);
void RestoreColumns(LPSTREAM pstm, int nCols);
void RestoreViewState();

void ColumnClick(int iCol)
{
m_sfState.lParamSort = (LPARAM)DPA_GetPtr(m_aParamSort, iCol);
        m_cView.SortItems(CompareIDs);
}

HRESULT CallCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return(m_psfvcb ? m_psfvcb->Message(uMsg, wParam, lParam) : E_NOTIMPL);
}

int GetMenuIDFromViewMode();
BOOL IsInCommDlg() {return(m_pCDB != NULL);}
HRESULT IncludeObject(LPCITEMIDLIST pidl)
{
return(IsInCommDlg() ? m_pCDB->IncludeObject(this, pidl) : S_OK);
}
HRESULT OnDefaultCommand()
{
return(IsInCommDlg() ? m_pCDB->OnDefaultCommand(this) : S_FALSE);
}
HRESULT OnStateChange(UINT uFlags)
{
return(IsInCommDlg() ? m_pCDB->OnStateChange(this, uFlags) : S_FALSE);
}

void InitFileMenu(HMENU hmInit);
void InitEditMenu(HMENU hmInit);
void InitViewMenu(HMENU hmInit);
int AddObject(LPCITEMIDLIST pidl);

HRESULT FillList(BOOL bInteractive);
BOOL ShowAllObjects() {return(TRUE);}

void MergeArrangeMenu(HMENU hmView);
void MergeViewMenu(HMENU hmenu, HMENU hmMerge);
BOOL OnActivate(UINT uState);
BOOL OnDeactivate();

IContextMenu * GetSelContextMenu();
void ReleaseSelContextMenu();

BOOL OnInitMenuPopup(HMENU hmInit, int nIndex, BOOL fSystemMenu);
void OnCommand(IContextMenu *pcm, WPARAM wParam, LPARAM lParam);

void CheckToolbar();
void MergeToolBar();

BOOL GetArrangeText(int iCol, UINT idFmt, LPSTR pszText, UINT cText);
void GetCommandHelpText(UINT id, LPSTR pszText, UINT cchText, BOOL bToolTip);
LRESULT OnMenuSelect(UINT idCmd, UINT uFlags, HMENU hmenu);

LPSHELLFOLDER m_psf;                       // ShellFolder pointer
ICommDlgBrowser *m_pCDB;                   // ICommdlgBrowser
IShellFolderViewCallback *m_psfvcb;        // pointer to ShellFolderView
                                           // callback
CEnsureRelease m_erFolder;
CEnsureRelease m_erCB;

CSFViewDlg m_cView;                        //  ViewDlg which contains the
                                           //  listview in the right pane
HWND m_hwndMain;

FOLDERSETTINGS m_fs;
IShellBrowser *m_psb;
SFSTATE m_sfState;

CMenuTemp m_cmCur;
UINT m_uState;

IContextMenu *m_pcmSel;

HDPA m_aParamSort;                         // maintains a sorted list of
                                           // items in a DPA

CAccelerator m_cAccel;

    CSafeMalloc m_cMalloc;

friend class CSFViewDlg;
} ;

#endif // _SFVWnd_H_


STRINGS.C

//*******************************************************************************************
//
// Filename : strings.c
//
//DBCS aware string routines...
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"
#include "strings.h"

#ifdef UNICODE
#define _StrEndN    _StrEndNW
#define ChrCmp      ChrCmpW
#define ChrCmpI     ChrCmpIW
#else
#define _StrEndN    _StrEndNA
#define ChrCmp      ChrCmpA
#define ChrCmpI     ChrCmpIA
#endif

/*
 * StrEndN - Find the end of a string, but no more than n bytes
 * Assumes   pStart points to start of null terminated string
 *           nBufSize is the maximum length
 * returns ptr to just after the last byte to be included
 */
LPSTR _StrEndNA(LPCSTR pStart, int nBufSize)
{
  LPCSTR pEnd;

  for (pEnd = pStart + nBufSize; *pStart && pStart < pEnd; pStart = AnsiNext(pStart))
    continue;   /* just getting to the end of the string */
  if (pStart > pEnd)
    {
      /* We can only get here if the last byte before pEnd was a lead byte
       */
      pStart -= 2;
    }
  return (LPSTR)pStart;
}

LPWSTR _StrEndNW(LPCWSTR pStart, int nBufSize)
{
#ifdef UNICODE
  LPCWSTR pEnd;

  for (pEnd = pStart + nBufSize; *pStart && (pStart < pEnd);
pStart++)
    continue;   /* just getting to the end of the string */

  return((LPWSTR)pStart);

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return NULL;

#endif
}

/*
 * ChrCmp -  Case sensitive character comparison for DBCS
 * Assumes   w1, wMatch are characters to be compared
 * Return    FALSE if they match, TRUE if no match
 */
BOOL ChrCmpA(WORD w1, WORD wMatch)
{
  /* Most of the time this won't match, so test it first for speed.
   */
  if (LOBYTE(w1) == LOBYTE(wMatch))
    {
      if (IsDBCSLeadByte(LOBYTE(w1)))
{
  return(w1 != wMatch);
}
      return FALSE;
    }
  return TRUE;
}

BOOL ChrCmpW(WORD w1, WORD wMatch)
{
#ifdef UNICODE
   return(!(w1 == wMatch));
#else
    SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
    return FALSE;
#endif
}

/*
 * ChrCmpI - Case insensitive character comparison for DBCS
 * Assumes   w1, wMatch are characters to be compared;
 *           HIBYTE of wMatch is 0 if not a DBC
 * Return    FALSE if match, TRUE if not
 */
BOOL ChrCmpIA(WORD w1, WORD wMatch)
{
  char sz1[3], sz2[3];

  if (IsDBCSLeadByte(sz1[0] = LOBYTE(w1)))
    {
      sz1[1] = HIBYTE(w1);
      sz1[2] = '\0';
    }
  else
      sz1[1] = '\0';

  *(WORD *)sz2 = wMatch;
  sz2[2] = '\0';
  return lstrcmpiA(sz1, sz2);
}

BOOL ChrCmpIW(WORD w1, WORD wMatch)
{
#ifdef UNICODE
  TCHAR sz1[2], sz2[2];

  sz1[0] = w1;
  sz1[1] = TEXT('\0');
  sz2[0] = wMatch;
  sz2[1] = TEXT('\0');

  return lstrcmpiW(sz1, sz2);

#else
  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return FALSE;

#endif
}

/*
 * StrChr - Find first occurrence of character in string
 * Assumes   pStart points to start of null terminated string
 *           wMatch  is the character to match
 * returns ptr to the first occurrence of ch in str, NULL if not found.
 */
LPSTR StrChrA(LPCSTR pStart, WORD wMatch)
{
  for ( ; *pStart; pStart = AnsiNext(pStart))
    {
      if (!ChrCmpA(*(UNALIGNED WORD *)pStart, wMatch))
      {
  return((LPSTR)pStart);
      }
   }
   return (NULL);
}

LPWSTR StrChrW(LPCWSTR pStart, WORD wMatch)
{
#ifdef UNICODE

  for ( ; *pStart; pStart = AnsiNext(pStart))
  {
      // Need a tmp word since casting ptr to WORD * will
      // fault on MIPS, ALPHA

      WORD wTmp;
      memcpy(&wTmp, pStart, sizeof(WORD));

      if (!ChrCmpW(wTmp, wMatch))
      {
  return((LPWSTR)pStart);
      }
  }
  return (NULL);

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return NULL;

#endif
}

/*
 * StrRChr - Find last occurrence of character in string
 * Assumes   pStart points to start of string
 *           pEnd   points to end of string (NOT included in search)
 *           wMatch  is the character to match
 * returns ptr to the last occurrence of ch in str, NULL if not found.
 */
LPSTR StrRChrA(LPCSTR pStart, LPCSTR pEnd, WORD wMatch)
{
  LPCSTR lpFound = NULL;

  if (!pEnd)
      pEnd = pStart + lstrlenA(pStart);

  for ( ; pStart < pEnd; pStart = AnsiNext(pStart))
    {
      if (!ChrCmpA(*(UNALIGNED WORD *)pStart, wMatch))
  lpFound = pStart;
    }
  return ((LPSTR)lpFound);
}

LPWSTR StrRChrW(LPCWSTR pStart, LPCWSTR pEnd, WORD wMatch)
{
#ifdef UNICODE

  LPCWSTR lpFound = NULL;

  if (!pEnd)
      pEnd = pStart + lstrlenW(pStart);

  for ( ; pStart < pEnd; pStart++)
    {
      if (!ChrCmpW(*(UNALIGNED WORD *)pStart, wMatch))
  lpFound = pStart;
    }
  return ((LPWSTR)lpFound);

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return NULL;

#endif
}

/*
 * StrChrI - Find first occurrence of character in string, case insensitive
 * Assumes   pStart points to start of null terminated string
 *           wMatch  is the character to match
 * returns ptr to the first occurrence of ch in str, NULL if not found.
 */
LPSTR StrChrIA(LPCSTR pStart, WORD wMatch)
{
  wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));

  for ( ; *pStart; pStart = AnsiNext(pStart))
    {
      if (!ChrCmpIA(*(UNALIGNED WORD *)pStart, wMatch))
  return((LPSTR)pStart);
    }
  return (NULL);
}

LPWSTR StrChrIW(LPCWSTR pStart, WORD wMatch)
{
#ifdef UNICODE

  for ( ; *pStart; pStart++)
    {
      if (!ChrCmpIW(*(WORD *)pStart, wMatch))
  return((LPWSTR)pStart);
    }
  return (NULL);

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return NULL;

#endif
}

/*
 * StrRChrI - Find last occurrence of character in string, case insensitive
 * Assumes   pStart points to start of string
 *           pEnd   points to end of string (NOT included in search)
 *           wMatch  is the character to match
 * returns ptr to the last occurrence of ch in str, NULL if not found.
 */
LPSTR StrRChrIA(LPCSTR pStart, LPCSTR pEnd, WORD wMatch)
{
  LPCSTR lpFound = NULL;

  if (!pEnd)
      pEnd = pStart + lstrlenA(pStart);

  wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));

  for ( ; pStart < pEnd; pStart = AnsiNext(pStart))
    {
      if (!ChrCmpIA(*(UNALIGNED WORD *)pStart, wMatch))
  lpFound = pStart;
    }
  return ((LPSTR)lpFound);
}

LPWSTR StrRChrIW(LPCWSTR pStart, LPCWSTR pEnd, WORD wMatch)
{
#ifdef UNICODE

  LPCWSTR lpFound = NULL;

  if (!pEnd)
      pEnd = pStart + lstrlenW(pStart);

  for ( ; pStart < pEnd; pStart++)
    {
      if (!ChrCmpIW(*(WORD *)pStart, wMatch))
  lpFound = pStart;
    }
  return ((LPWSTR)lpFound);

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return NULL;

#endif
}

// StrCSpn: return index to first char of lpStr that is present in lpSet.
// Includes the NUL in the comparison; if no lpSet chars are found, returns
// the index to the NUL in lpStr.
// Just like CRT strcspn.
//
int StrCSpnA(LPCSTR lpStr, LPCSTR lpSet)
{
// nature of the beast: O(lpStr*lpSet) work
LPCSTR lp = lpStr;
if (!lpStr || !lpSet)
return 0;

while (*lp)
{
 if (StrChrA(lpSet, *(UNALIGNED WORD *)lp))
return (int)(lp-lpStr);
lp = AnsiNext(lp);
}

return (int)(lp-lpStr); // ==lstrlen(lpStr)
}

int StrCSpnW(LPCWSTR lpStr, LPCWSTR lpSet)
{
#ifdef UNICODE

// nature of the beast: O(lpStr*lpSet) work
LPCWSTR lp = lpStr;
if (!lpStr || !lpSet)
return 0;

while (*lp)
{
if (StrChrW(lpSet, *(WORD *)lp))
return (int)(lp-lpStr);
lp++;
}

return (int)(lp-lpStr); // ==lstrlen(lpStr)

#else

        SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
return -1;

#endif
}

// StrCSpnI: case-insensitive version of StrCSpn.
//
int StrCSpnIA(LPCSTR lpStr, LPCSTR lpSet)
{
// nature of the beast: O(lpStr*lpSet) work
LPCSTR lp = lpStr;
if (!lpStr || !lpSet)
return 0;

while (*lp)
{
if (StrChrIA(lpSet, *(UNALIGNED WORD *)lp))
return (int)(lp-lpStr);
lp = AnsiNext(lp);
}

return (int)(lp-lpStr); // ==lstrlen(lpStr)
}

int StrCSpnIW(LPCWSTR lpStr, LPCWSTR lpSet)
{
#ifdef UNICODE
// nature of the beast: O(lpStr*lpSet) work
LPCWSTR lp = lpStr;
if (!lpStr || !lpSet)
return 0;

while (*lp)
{
if (StrChrIW(lpSet, *(WORD *)lp))
return (int)(lp-lpStr);
lp++;
}

return (int)(lp-lpStr); // ==lstrlen(lpStr)

#else

        SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
return -1;

#endif
}

/*
 * StrCmpN      - Compare n bytes
 *
 * returns   See lstrcmp return values.
 * won't work if source strings are in ROM
 */
int StrCmpNA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
{
    char sz1[4];
    char sz2[4];
    int i;
    LPCSTR lpszEnd = lpStr1 + nChar;

    //DebugMsg(DM_TRACE, "StrCmpN: %s %s %d returns:", lpStr1, lpStr2, nChar);

    for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1 = AnsiNext(lpStr1), lpStr2 = AnsiNext(lpStr2)) {
        WORD wMatch;

        if (IsDBCSLeadByte(*lpStr2))
            lpStr2++;

        wMatch = (WORD) *lpStr2;

        i = ChrCmpA(*(UNALIGNED WORD *)lpStr1, wMatch);
        if (i) {
            int iRet;

            (*(WORD *)sz1) = *(UNALIGNED WORD *)lpStr1;
            (*(WORD *)sz2) = *(UNALIGNED WORD *)lpStr2;
            *AnsiNext(sz1) = 0;
            *AnsiNext(sz2) = 0;
            iRet = lstrcmpA(sz1, sz2);
            //DebugMsg(DM_TRACE, ".................... %d", iRet);
            return iRet;
        }
    }

    //DebugMsg(DM_TRACE, ".................... 0");
    return 0;
}

int StrCmpNW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
{
#ifdef UNICODE

    WCHAR sz1[2];
    WCHAR sz2[2];
    int i;
    LPCWSTR lpszEnd = lpStr1 + nChar;

    //DebugMsg(DM_TRACE, "StrCmpN: %s %s %d returns:", lpStr1, lpStr2, nChar);

    for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++) {
        i = ChrCmpW(*lpStr1, *lpStr2);
        if (i) {
            int iRet;

            sz1[0] = *lpStr1;
            sz2[0] = *lpStr2;
            sz1[1] = TEXT('\0');
            sz2[1] = TEXT('\0');
            iRet = lstrcmpW(sz1, sz2);
            //DebugMsg(DM_TRACE, ".................... %d", iRet);
            return iRet;
        }
    }

    //DebugMsg(DM_TRACE, ".................... 0");
    return 0;

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return -1;

#endif
}

/*
 * StrCmpNI     - Compare n bytes, case insensitive
 *
 * returns   See lstrcmpi return values.
 */
int StrCmpNIA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
{
    int i;
    LPCSTR lpszEnd = lpStr1 + nChar;

    //DebugMsg(DM_TRACE, "StrCmpNI: %s %s %d returns:", lpStr1, lpStr2, nChar);

    for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); (lpStr1 = AnsiNext(lpStr1)), (lpStr2 = AnsiNext(lpStr2))) {
        WORD wMatch;

        wMatch = (UINT)(IsDBCSLeadByte(*lpStr2)) ? *(WORD *)lpStr2 : (WORD)(BYTE)(*lpStr2);

        i = ChrCmpIA(*(UNALIGNED WORD *)lpStr1, wMatch);
        if (i) {
            //DebugMsg(DM_TRACE, ".................... %d", i);
            return i;
        }
    }
    //DebugMsg(DM_TRACE, ".................... 0");
    return 0;
}

int StrCmpNIW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
{
#ifdef UNICODE

    int i;
    LPCWSTR lpszEnd = lpStr1 + nChar;

    //DebugMsg(DM_TRACE, "StrCmpNI: %s %s %d returns:", lpStr1, lpStr2, nChar);

    for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++) {
        i = ChrCmpIW(*lpStr1, *lpStr2);
        if (i) {
            //DebugMsg(DM_TRACE, ".................... %d", i);
            return i;
        }
    }
    //DebugMsg(DM_TRACE, ".................... 0");
    return 0;

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return -1;

#endif
}

/*
 * StrRStrI      - Search for last occurrence of a substring
 *
 * Assumes   lpSource points to the null terminated source string
 *           lpLast points to where to search from in the source string
 *           lpLast is not included in the search
 *           lpSrch points to string to search for
 * returns   last occurrence of string if successful; NULL otherwise
 */
LPSTR StrRStrIA(LPCSTR lpSource, LPCSTR lpLast, LPCSTR lpSrch)
{
    LPCSTR lpFound = NULL;
    LPSTR pEnd;
    char cHold;

    if (!lpLast)
lpLast = lpSource + lstrlenA(lpSource);

    if (lpSource >= lpLast || *lpSrch == 0)
return NULL;

    pEnd = _StrEndNA(lpLast, (UINT)(lstrlenA(lpSrch)-1));
    cHold = *pEnd;
    *pEnd = 0;

    while ((lpSource = StrStrIA(lpSource, lpSrch)) != 0 && lpSource < lpLast)
    {
lpFound = lpSource;
lpSource = AnsiNext(lpSource);
    }
    *pEnd = cHold;
    return((LPSTR)lpFound);
}

LPWSTR StrRStrIW(LPCWSTR lpSource, LPCWSTR lpLast, LPCWSTR lpSrch)
{
#ifdef UNICODE
    LPCWSTR lpFound = NULL;
    LPWSTR pEnd;
    WCHAR cHold;

    if (!lpLast)
lpLast = lpSource + lstrlenW(lpSource);

    if (lpSource >= lpLast || *lpSrch == 0)
return NULL;

    pEnd = _StrEndNW(lpLast, (UINT)(lstrlenW(lpSrch)-1));
    cHold = *pEnd;
    *pEnd = 0;

    while ((lpSource = StrStrIW(lpSource, lpSrch))!=0 &&
  lpSource < lpLast)
    {
lpFound = lpSource;
lpSource++;
    }
    *pEnd = cHold;
    return((LPWSTR)lpFound);

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return NULL;

#endif
}

/*
 * StrStr      - Search for first occurrence of a substring
 *
 * Assumes   lpSource points to source string
 *           lpSrch points to string to search for
 * returns   first occurrence of string if successful; NULL otherwise
 */
LPSTR StrStrA(LPCSTR lpFirst, LPCSTR lpSrch)
{
  UINT uLen;
  WORD wMatch;

  uLen = (UINT)lstrlenA(lpSrch);
  wMatch = *(UNALIGNED WORD *)lpSrch;

  for ( ; (lpFirst=StrChrA(lpFirst, wMatch))!=0 && StrCmpNA(lpFirst, lpSrch, uLen);
lpFirst=AnsiNext(lpFirst))
    continue; /* continue until we hit the end of the string or get a match */

  return((LPSTR)lpFirst);
}

LPWSTR StrStrW(LPCWSTR lpFirst, LPCWSTR lpSrch)
{
#ifdef UNICODE

  UINT uLen;
  WORD wMatch;

  uLen = (UINT)lstrlenW(lpSrch);
  wMatch = *(WORD *)lpSrch;

  for ( ; (lpFirst=StrChrW(lpFirst, wMatch))!=0 && StrCmpNW(lpFirst, lpSrch, uLen);
lpFirst++)
    continue; /* continue until we hit the end of the string or get a match */

  return((LPWSTR)lpFirst);

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return NULL;

#endif
}

/*
 * StrStrI   - Search for first occurrence of a substring, case insensitive
 *
 * Assumes   lpFirst points to source string
 *           lpSrch points to string to search for
 * returns   first occurrence of string if successful; NULL otherwise
 */
LPSTR StrStrIA(LPCSTR lpFirst, LPCSTR lpSrch)
{
  UINT uLen;
  WORD wMatch;

  uLen = (UINT)lstrlenA(lpSrch);
  wMatch = *(UNALIGNED WORD *)lpSrch;

  for ( ; (lpFirst = StrChrIA(lpFirst, wMatch)) != 0 && StrCmpNIA(lpFirst, lpSrch, uLen);
lpFirst=AnsiNext(lpFirst))
      continue; /* continue until we hit the end of the string or get a match */

  return((LPSTR)lpFirst);
}

LPWSTR StrStrIW(LPCWSTR lpFirst, LPCWSTR lpSrch)
{
#ifdef UNICODE

  UINT uLen;
  WORD wMatch;

  uLen = (UINT)lstrlenW(lpSrch);
  wMatch = *(WORD *)lpSrch;

  for ( ; (lpFirst = StrChrIW(lpFirst, wMatch)) != 0 && StrCmpNIW(lpFirst, lpSrch, uLen);
lpFirst++)
      continue; /* continue until we hit the end of the string or get a match */

  return((LPWSTR)lpFirst);

#else

  SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  return NULL;

#endif
}


STRINGS.H

//*******************************************************************************************
//
// Filename : Strings.h
//
//Common defines for Strings stuff
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

LPSTR  StrChrA(LPCSTR lpStart, WORD wMatch);
LPWSTR StrChrW(LPCWSTR lpStart, WORD wMatch);
LPSTR  StrRChrA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch);
LPWSTR StrRChrW(LPCWSTR lpStart, LPCWSTR lpEnd, WORD wMatch);
LPSTR  StrChrIA(LPCSTR lpStart, WORD wMatch);
LPWSTR StrChrIW(LPCWSTR lpStart, WORD wMatch);
LPSTR  StrRChrIA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch);
LPWSTR StrRChrIW(LPCWSTR lpStart, LPCWSTR lpEnd, WORD wMatch);
int    StrCmpNA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar);
int    StrCmpNW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar);
int    StrCmpNIA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar);
int    StrCmpNIW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar);
LPSTR  StrStrA(LPCSTR lpFirst, LPCSTR lpSrch);
LPWSTR StrStrW(LPCWSTR lpFirst, LPCWSTR lpSrch);
LPSTR  StrRStr(LPCSTR lpSource, LPCSTR lpLast, LPCSTR lpSrch);
LPSTR  StrStrIA(LPCSTR lpFirst, LPCSTR lpSrch);
LPWSTR StrStrIW(LPCWSTR lpFirst, LPCWSTR lpSrch);
LPSTR  StrRStrIA(LPCSTR lpSource, LPCSTR lpLast, LPCSTR lpSrch);
LPWSTR StrRStrIW(LPCWSTR lpSource, LPCWSTR lpLast, LPCWSTR lpSrch);
int    StrCSpnA(LPCSTR lpStr, LPCSTR lpSet);
int    StrCSpnW(LPCWSTR lpStr, LPCWSTR lpSet);
int    StrCSpnIA(LPCSTR lpStr, LPCSTR lpSet);
int    StrCSpnIW(LPCWSTR lpStr, LPCWSTR lpSet);
int    StrToIntA(LPCSTR lpSrc);
int    StrToIntW(LPCWSTR lpSrc);

#ifdef UNICODE
#define StrToInt                StrToIntW
#define StrChr                  StrChrW
#define StrRChr                 StrRChrW
#define StrChrI                 StrChrIW
#define StrRChrI                StrRChrIW
#define StrCSpn                 StrCSpnW
#define StrCSpnI                StrCSpnIW
#define StrCmpN                 StrCmpNW
#define StrCmpNI                StrCmpNIW
#define StrStr                  StrStrW
#define StrStrI                 StrStrIW
#define StrRStrI                StrRStrIW
#else
#define StrToInt                StrToIntA
#define StrChr                  StrChrA
#define StrRChr                 StrRChrA
#define StrChrI                 StrChrIA
#define StrRChrI                StrRChrIA
#define StrCSpn                 StrCSpnA
#define StrCSpnI                StrCSpnIA
#define StrCmpN                 StrCmpNA
#define StrCmpNI                StrCmpNIA
#define StrStr                  StrStrA
#define StrStrI                 StrStrIA
#define StrRStrI                StrRStrIA
#endif


THISDLL.CPP

//*******************************************************************************************
//
// Filename : ThisDll.cpp
//
//Generic OLE DLL routines
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

// ********************************************************
// Initialize GUIDs
//
#pragma data_seg(".text")
#define INITGUID
#include <initguid.h>
#include <cguid.h>
#include "thisguid.h"
//#undef INITGUID
#pragma data_seg()

#include "thisdll.h"

#include "resource.h"

CThisDll g_ThisDll;

// ******************************************************************
// ******************************************************************
// DllMain
STDAPI_(BOOL) APIENTRY DllMain(
   HINSTANCE hDll,
   DWORD dwReason,
   LPVOID lpReserved)
{
   switch(dwReason)
   {
      case DLL_PROCESS_ATTACH:
         g_ThisDll.SetInstance(hDll);

         // Initialize the various modules.
         //

         //
         // Tell system to fix up alignment faults at runtime
         //

         SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT);

         break;

      case DLL_PROCESS_DETACH:
         break;

      case DLL_THREAD_ATTACH:
         break;

      case DLL_THREAD_DETACH:
         break;

      default:
         break;

   } // switch

   return(TRUE);
}

// ******************************************************************
// DllCanUnloadNow
STDAPI DllCanUnloadNow()
{
HRESULT retval = (HRESULT)((g_ThisDll.m_cRef.GetRef() == 0)
&& (g_ThisDll.m_cLock.GetRef() == 0) ? S_OK : S_FALSE);
return(retval);
}

// Procedure for uninstalling this DLL (given an INF file)
void CALLBACK Uninstall(HWND hwndStub, HINSTANCE hInstance, LPSTR lpszCmdLine,
int nCmdShow)
{
RUNDLLPROC pfnCheckAPI = Uninstall;

static char szFmt[] = "rundll.exe setupx.dll,InstallHinfSection DefaultUninstall 132 %s";

if (!lpszCmdLine || lstrlen(lpszCmdLine)>=MAX_PATH)
{
return;
}
if (MessageBox(hwndStub, MAKEINTRESOURCE(IDS_SUREUNINST),
MAKEINTRESOURCE(IDS_THISDLL), MB_YESNO|MB_ICONSTOP) != IDYES)
{
return;
}

char szCmd[sizeof(szFmt)+MAX_PATH];
wsprintf(szCmd, szFmt, lpszCmdLine);

// Note that I use START.EXE, to minimize the chance that this DLL will still be loaded
// when SETUPX tries to delete it
ShellExecute(hwndStub, NULL, "start.exe", szCmd, NULL, SW_SHOWMINIMIZED);
}

// CThisDll::CThisDll()

// ********************************************************************
class CThisDllClassFactory : public IClassFactory
{
public:
// *** IUnknown methods ***
STDMETHODIMP QueryInterface(REFIID riid,
LPVOID FAR* ppvObj);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);

// *** IClassFactory methods ***
STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter,
REFIID riid,
LPVOID FAR* ppvObject);
STDMETHODIMP LockServer(BOOL fLock);

private:
CRefDll m_cRefDll;

CRefCount m_cRef;
};

// ******************************************************************
// ******************************************************************
// DllGetClassObject
STDAPI DllGetClassObject(
   REFCLSID rclsid,
   REFIID riid,
   LPVOID FAR* ppvObj)
{
*ppvObj = NULL;

if(! (rclsid == CLSID_ThisDll))
{
return(E_FAIL);
}

CThisDllClassFactory *pcf = new CThisDllClassFactory;
if (!pcf)
{
return(E_OUTOFMEMORY);
}

// Note if the QueryInterface fails, the Release will delete the object
pcf->AddRef();
HRESULT hRes = pcf->QueryInterface(riid, ppvObj);
pcf->Release();

return(hRes);
}

// ***********************************************************************
// ***********************************************************************
// CImpIClassFactory member functions

// *** IUnknown methods ***
STDMETHODIMP CThisDllClassFactory::QueryInterface(REFIID riid,
   LPVOID FAR* ppvObj)
{
*ppvObj = NULL;

// Any interface on this object is the object pointer
if((riid == IID_IUnknown) || (riid == IID_IClassFactory))
{
*ppvObj = (LPVOID)(IClassFactory *)this;
}

if(*ppvObj)
{
((LPUNKNOWN)*ppvObj)->AddRef();
return NOERROR;
}

return(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG) CThisDllClassFactory::AddRef(void)
{
return(m_cRef.AddRef());
}

STDMETHODIMP_(ULONG) CThisDllClassFactory::Release(void)
{
if (!m_cRef.Release())
{
   delete this;
return(0);
}

return(m_cRef.GetRef());
}

// *** IClassFactory methods ***
STDMETHODIMP CThisDllClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
        REFIID riid,
        LPVOID FAR* ppvObj)
{
// we do not support aggregation
if(pUnkOuter)
{
return(CLASS_E_NOAGGREGATION);
}

return(::CreateInstance(riid, ppvObj));
}

STDMETHODIMP CThisDllClassFactory::LockServer(BOOL fLock)
{
if(fLock)
{
g_ThisDll.m_cLock.AddRef();
}
else
{
g_ThisDll.m_cLock.Release();
}

return(NOERROR);
}

void *  __cdecl operator new(unsigned int nSize)
{
return((LPVOID)LocalAlloc(LMEM_FIXED, nSize));
}

void  __cdecl operator delete(void *pv)
{
LocalFree((HLOCAL)pv);
}

extern "C" int __cdecl _purecall()
{
return(0);
}


THISDLL.H

//*******************************************************************************************
//
// Filename : ThisDll.h
//
//Generic OLE header file
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _THISDLL_H_
#define _THISDLL_H_

class CWaitCursor
{
public:
CWaitCursor() {m_cOld=SetCursor(LoadCursor(NULL, IDC_WAIT));}
~CWaitCursor() {SetCursor(m_cOld);}

private:
HCURSOR m_cOld;
} ;

class CRefCount
{
public:
CRefCount() : m_cRef(0) {};

UINT AddRef()  {return(++m_cRef);}
UINT Release() {return(--m_cRef);}
UINT GetRef()  {return(  m_cRef);}

private:
UINT m_cRef;

} ;

class CThisDll
{
public:
CThisDll() {
        m_hInst=NULL;
}
    // Make no destructor for global classes (requires CRT stuff)

void SetInstance(HINSTANCE hInst) {m_hInst=hInst;}
HINSTANCE GetInstance() {return(m_hInst);}

CRefCount m_cRef;
CRefCount m_cLock;

private:
HINSTANCEm_hInst;
} ;

extern CThisDll g_ThisDll;

class CRefDll
{
public:
CRefDll()  {g_ThisDll.m_cRef.AddRef ();}
~CRefDll() {g_ThisDll.m_cRef.Release();}
} ;

extern HRESULT CreateInstance(REFIID riid, LPVOID *ppvObj);

class CSafeMalloc
{
public:
    CSafeMalloc()
    {
        if (!SUCCEEDED(SHGetMalloc(&m_ShIMalloc)))
        {
            m_ShIMalloc = NULL;
        }
    }
    ~CSafeMalloc() {if (m_ShIMalloc) m_ShIMalloc->Release();}

    LPVOID Alloc(UINT cb) {return(m_ShIMalloc ? m_ShIMalloc->Alloc(cb) : NULL);}
    void Free(LPVOID pv) {if (m_ShIMalloc) m_ShIMalloc->Free(pv);}

private:
    IMalloc* m_ShIMalloc;
} ;

#endif// _THISDLL_H_


THISGUID.H

//*******************************************************************************************
//
// Filename : ThisGUID.h
//
//GUID for ThisDll
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

//
DEFINE_GUID(CLSID_ThisDll, 0x0CD7A5C0L, 0x9F37, 0x11CE, 0xAE, 0x65, 0x08, 0x00, 0x2B, 0x2E, 0x12, 0x62);


UNKNOWN.CPP

//*******************************************************************************************
//
// Filename : Unknown.cpp
//
//Customized CUnknown implmentations
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "Pch.H"

#include "ThisDll.H"

#include "Unknown.H"

CUnknown::~CUnknown()
{
}

HRESULT CUnknown::QIHelper(REFIID riid, LPVOID *ppvObj, const IID *apiid[],
LPUNKNOWN aobj[])
{
*ppvObj = NULL;

LPUNKNOWN pObj;

if (riid == IID_IUnknown)
{
pObj = aobj[0];
}
else
{
for (int i=0; ; ++i)
{
if (!apiid[i])
{
   return(E_NOINTERFACE);
}

if (*apiid[i] == riid)
{
pObj = aobj[i];
break;
}
}
}

pObj->AddRef();
*ppvObj = pObj;

return(NOERROR);
}

ULONG CUnknown::AddRefHelper()
{
return(m_cRef.AddRef());
}

ULONG CUnknown::ReleaseHelper()
{
if (!m_cRef.Release())
{
   delete this;
return(0);
}

return(m_cRef.GetRef());
}


UNKNOWN.H

//*******************************************************************************************
//
// Filename : Unknown.h
//
//Definitions for some customized routines
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _UNKNOWN_H_
#define _UNKNOWN_H_

class CUnknown
{
public:
CUnknown() {}
virtual ~CUnknown();// virtual destructor called from Release

HRESULT QIHelper(REFIID riid, LPVOID *ppvObj, const IID *apiid[], LPUNKNOWN aobj[]);
ULONG AddRefHelper();
ULONG ReleaseHelper();

private:
CRefCount m_cRef;
CRefDll m_cRefDll;
} ;

// Only for ensuring Release in all control paths
class CEnsureRelease
{
public:
CEnsureRelease(IUnknown *pUnk) : m_pUnk(pUnk) {}
~CEnsureRelease() {if (m_pUnk) m_pUnk->Release();}

operator IUnknown*() {return(m_pUnk);}

void Attach(IUnknown *pUnk) {m_pUnk = pUnk;}

private:
IUnknown *m_pUnk;
} ;

class CObjTemp
{
public:
CObjTemp() : m_hObj(0) {}
~CObjTemp() {}

operator HANDLE() const {return(m_hObj);}

HANDLE Attach(HANDLE hObjNew) {HANDLE hObj=m_hObj; m_hObj=hObjNew; return(hObj);}
HANDLE Detach() {return(Attach(0));}

protected:
HANDLE m_hObj;
} ;

class CMenuTemp : public CObjTemp
{
public:
CMenuTemp() : CObjTemp() {}
CMenuTemp(HMENU hm) : CObjTemp() {Attach(hm);}
~CMenuTemp() {if (m_hObj) DestroyMenu(Detach());}

operator HMENU() const {return((HMENU)m_hObj);}

HMENU Attach(HMENU hObjNew) {return((HMENU)CObjTemp::Attach((HANDLE)hObjNew));}
HMENU Detach() {return((HMENU)CObjTemp::Detach());}

BOOL CreatePopupMenu() {Attach(::CreatePopupMenu()); return(m_hObj!=NULL);}
} ;

class CGotGlobal : public CObjTemp
{
public:
CGotGlobal(HGLOBAL hData, IUnknown *pUnk) : m_erData(pUnk) {Attach(hData);}
~CGotGlobal() {if (!(IUnknown*)m_erData) GlobalFree(m_hObj);}

private:
CEnsureRelease m_erData;
} ;

class CHIDA : public CGotGlobal
{
public:
CHIDA(HGLOBAL hIDA, IUnknown *pUnk) : CGotGlobal(hIDA, pUnk)
{
m_lpIDA = (LPIDA)GlobalLock(hIDA);
}
~CHIDA() {GlobalUnlock(m_hObj);}

LPCITEMIDLIST operator [](UINT nIndex)
{
if (!m_lpIDA || nIndex>m_lpIDA->cidl)
{
return(NULL);
}

return((LPCITEMIDLIST)(((LPSTR)m_lpIDA)+m_lpIDA->aoffset[nIndex]));
}

int GetCount() {return(m_lpIDA ? m_lpIDA->cidl : 0);}

private:
LPIDA m_lpIDA;
} ;

#endif // _UNKNOWN_H_


VIEW.CPP

//*******************************************************************************************
//
// Filename : View.cpp
//
//Implementation file for CCabView
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "pch.h"

#include "thisdll.h"

#include "resource.h"

#include "folder.h"
#include "view.h"
#include "os.h"

#include "unknown.h"

class CCabView : public CUnknown, public IShellFolderViewCallback
{
public:
CCabView() {}
virtual ~CCabView();

// *** IUnknown methods ***
STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();

// *** IShellFolderViewCallback methods ***
STDMETHODIMP Message(UINT uMsg, WPARAM wParam, LPARAM lParam);

private:
HRESULT GetDetailsOf(UINT iColumn, SFVCB_GETDETAILSOF_DATA* lpDetails);
} ;

CCabView::~CCabView()
{
}

STDMETHODIMP CCabView::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
static const IID *apiid[] = { &IID_IShellFolderViewCallback, NULL };
LPUNKNOWN aobj[] = { (IShellFolderViewCallback *)this };

return(QIHelper(riid, ppvObj, apiid, aobj));
}

STDMETHODIMP_(ULONG) CCabView::AddRef()
{
return(AddRefHelper());
}

STDMETHODIMP_(ULONG) CCabView::Release()
{
return(ReleaseHelper());
}

STDMETHODIMP CCabView::Message(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_SFVCB_MSG(SFVCB_GETDETAILSOF, GetDetailsOf);

default:
return(E_NOTIMPL);
}

return(S_OK);
}

struct _CVCOLINFO
{
UINT iColumn;
UINT iTitle;
UINT cchCol;
UINT iFmt;
} s_aCVColInfo[] = {
    {CV_COL_NAME,     IDS_CV_COL_NAME,     20, LVCFMT_LEFT},
    {CV_COL_SIZE,     IDS_CV_COL_SIZE,     10, LVCFMT_RIGHT},
    {CV_COL_TYPE,     IDS_CV_COL_TYPE,     20, LVCFMT_LEFT},
    {CV_COL_MODIFIED, IDS_CV_COL_MODIFIED, 20, LVCFMT_LEFT},
};

HRESULT CCabView::GetDetailsOf(UINT iColumn, SFVCB_GETDETAILSOF_DATA* lpDetails)
{
LPCITEMIDLIST pidl = lpDetails->pidl;
LPCABITEM pit = (LPCABITEM)pidl;

if (iColumn >= CV_COL_MAX)
{
return(E_NOTIMPL);
}

lpDetails->str.uType = STRRET_CSTR;
lpDetails->str.cStr[0] = '\0';

if (!pit)
{
LoadString(g_ThisDll.GetInstance(), s_aCVColInfo[iColumn].iTitle,
lpDetails->str.cStr, sizeof(lpDetails->str.cStr));
lpDetails->fmt = s_aCVColInfo[iColumn].iFmt;
lpDetails->cChar = s_aCVColInfo[iColumn].cchCol;
lpDetails->lParamSort = iColumn;
return S_OK;
}

switch (iColumn)
{
case CV_COL_NAME:
CCabFolder::GetNameOf(pit, &lpDetails->str);
break;

case CV_COL_SIZE:
{
    char szOrder[10];

    LoadString(g_ThisDll.GetInstance(), IDS_ORDERKB, szOrder, sizeof(szOrder));
    wsprintf(lpDetails->str.cStr, szOrder, (pit->dwFileSize + 1023) / 1024);
break;
}

case CV_COL_TYPE:
CCabFolder::GetTypeOf(pit, &lpDetails->str);
break;

case CV_COL_MODIFIED:
CFileTime::DateTimeToString(pit->uFileDate, pit->uFileTime,
lpDetails->str.cStr);
break;
}

return(S_OK);
}

HRESULT CabView_CreateCallback(IShellFolderViewCallback **ppcb)
{
*ppcb = NULL;

CCabView *pcb = new CCabView;

if (!pcb)
{
return(E_OUTOFMEMORY);
}

//  #pragma message("Make 1 the initial refcount in CUnknown")
pcb->AddRef();

*ppcb = (IShellFolderViewCallback *)pcb;
return(NOERROR);
}


VIEW.H

//*******************************************************************************************
//
// Filename : View.h
//
//SFView and more
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _CABVIEW_H_
#define _CABVIEW_H_

#include "SFView.H"

HRESULT CabView_CreateCallback(IShellFolderViewCallback **ppcb);

#endif // _CABVIEW_H_


XICON.CPP

//*******************************************************************************************
//
// Filename : XIcon.cpp
//
//Implementation file for CIconTemp and CXIcon
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#include "Pch.H"

#include "ThisDll.H"

#include "Unknown.H"
#include "path.h"
#include "XIcon.H"

class CIconTemp : public CObjTemp
{
public:
CIconTemp() : CObjTemp() {}
CIconTemp(HICON hi) : CObjTemp() {Attach(hi);}
~CIconTemp() {if (m_hObj) DestroyIcon(Detach());}

operator HICON() const {return((HICON)m_hObj);}

HICON Attach(HICON hObjNew) {return((HICON)CObjTemp::Attach((HANDLE)hObjNew));}
HICON Detach() {return((HICON)CObjTemp::Detach());}
} ;

BOOL CXIcon::Init(HWND hwndLB, UINT idiDef)
{
m_hwndLB = hwndLB;

m_cimlLg.Create(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 8);
m_cimlSm.Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 8);

HICON hIcon = LoadIcon(g_ThisDll.GetInstance(), MAKEINTRESOURCE(idiDef));

return(m_cimlLg && m_cimlSm && AddIcon(hIcon)>=0);
}

UINT CXIcon::GetIDString(LPSTR pszIDString, UINT uIDLen,
LPCSTR szIconFile, int iIndex, UINT wFlags)
{
static const char szIDFmt[] = "\"%s\" %d %08x";
char szIDString[MAX_PATH + 2*sizeof(szIDFmt)];

int iRet = wsprintf(szIDString, szIDFmt, szIconFile, iIndex, wFlags);
lstrcpyn(pszIDString, szIDString, uIDLen);

return(iRet + 1);
}

int CXIcon::GetCachedIndex(LPCSTR szIconFile, int iIndex, UINT wFlags)
{
char szIDString[MAX_PATH*2];
GetIDString(szIDString, sizeof(szIDString), szIconFile, iIndex, wFlags);
int iLB = ListBox_FindStringExact(m_hwndLB, -1, szIDString);

if (iLB < 0)
{
return(-1);
}

return(ListBox_GetItemData(m_hwndLB, iLB));
}

int CXIcon::CacheIcons(HICON hiLarge, HICON hiSmall,
LPCSTR szIconFile, int iIndex, UINT wFlags)
{
CIconTemp ciLarge(hiLarge);
CIconTemp ciSmall(hiSmall);

if (!hiLarge || !hiSmall)
{
return(-1);
}

char szIDString[MAX_PATH*2];
GetIDString(szIDString, sizeof(szIDString), szIconFile, iIndex, wFlags);
int iLB = ListBox_AddString(m_hwndLB, szIDString);

if (iLB < 0)
{
return(-1);
}

int iLarge = AddIcon(ciLarge, AI_LARGE);
if (iLarge >= 0)
{
int iSmall = AddIcon(ciSmall, AI_SMALL);
if (iSmall >= 0)
{
if (iLarge == iSmall)
{
// Should always happen;
ListBox_SetItemData(m_hwndLB, iLB, iLarge);
return(iLarge);
}

ImageList_Remove(m_cimlSm, iSmall);
}

ImageList_Remove(m_cimlLg, iLarge);
}

ListBox_DeleteString(m_hwndLB, iLB);
return(-1);
}

HRESULT CXIcon::ExtractIcon(LPCSTR szIconFile, int iIndex, UINT wFlags,
HICON *phiconLarge, HICON *phiconSmall, DWORD dwSizes)
{
return(E_NOTIMPL);
}

int CXIcon::GetIcon(IShellFolder *psf, LPCITEMIDLIST pidl)
{
int iImage = -1;

IExtractIcon *pxi;
HRESULT hres = psf->GetUIObjectOf(NULL, 1, &pidl, IID_IExtractIcon, NULL, (LPVOID *)&pxi);

if (FAILED(hres))
{
return(0);
}
CEnsureRelease erExtractIcon(pxi);

    char szIconFile[MAX_PATH];
    int iIndex;
    UINT wFlags = 0;

    hres = pxi->GetIconLocation(GIL_FORSHELL,
szIconFile, sizeof(szIconFile), &iIndex, &wFlags);
    if (FAILED(hres))
    {
return(0);
}

    //
    // if GIL_DONTCACHE was returned by the icon handler, dont
    // lookup the previous icon, assume a cache miss.
    //
    if (!(wFlags & GIL_DONTCACHE))
{
        iImage = GetCachedIndex(szIconFile, iIndex, wFlags);
}

    // if we miss our cache...
    if (iImage == -1)
    {
HICON hiconLarge = NULL;
HICON hiconSmall = NULL;

int cxIcon = GetSystemMetrics(SM_CXICON);
int cxSmIcon = GetSystemMetrics(SM_CXSMICON);

        // try getting it from the Extract member fuction
        hres = pxi->Extract(szIconFile, iIndex, &hiconLarge, &hiconSmall,
        MAKELONG(cxIcon, cxSmIcon));

// S_FALSE means can you please do it
if (hres == S_FALSE)
{
hres = ExtractIcon(szIconFile, iIndex, wFlags,
&hiconLarge, &hiconSmall, MAKELONG(cxIcon, cxSmIcon));
}
if (SUCCEEDED(hres))
{
// Let CacheIcons check and destroy the hicon's
iImage = CacheIcons(hiconLarge, hiconSmall, szIconFile, iIndex, wFlags);
}

// if we failed in any way pick a default icon
        if (iImage == -1)
        {
            if (wFlags & GIL_SIMULATEDOC)
{
                iImage = 0; // II_DOCUMENT;
}
            else if ((wFlags & GIL_PERINSTANCE) && PathIsExe(szIconFile))
{
                iImage = 0; // II_APPLICATION;
}
            else
{
                iImage = 0; // II_DOCNOASSOC;
}
}
}

    if (iImage < 0)
{
        iImage = 0; // II_DOCNOASSOC;
}

return(iImage);
}


XICON.H

//*******************************************************************************************
//
// Filename : XIcon.h
//
//defines for  CImageList and CXIcon
//
// Copyright 1994 - 1998 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************

#ifndef _XICON_H_
#define _XICON_H_

class CImageList
{
public:
CImageList() : m_himl(NULL) {}
~CImageList() {if (m_himl) ImageList_Destroy(m_himl);}

operator HIMAGELIST() {return(m_himl);}

BOOL Create(int cx, int cy, int cGrow)
{
m_himl = ImageList_Create(cx, cy, ILC_MASK, cGrow, cGrow);
return(m_himl != NULL);
}

private:
HIMAGELIST m_himl;
} ;

class CXIcon
{
public:
CXIcon() {}
~CXIcon() {}

BOOL Init(HWND hwndLB, UINT idiDef);
CImageList& GetIML(BOOL bLarge) {return(bLarge ? m_cimlLg : m_cimlSm);}

enum
{
AI_LARGE = 0x0001,
AI_SMALL = 0x0002,
} ;

int AddIcon(HICON hIcon, UINT uFlags=AI_LARGE|AI_SMALL)
{
int i = -1;

if (uFlags & AI_LARGE)
{
i = ImageList_AddIcon(m_cimlLg, hIcon);
}

if (uFlags & AI_SMALL)
{
i = ImageList_AddIcon(m_cimlSm, hIcon);
}

return(i);
}

int GetIcon(IShellFolder *psf, LPCITEMIDLIST pidl);

private:
int GetCachedIndex(LPCSTR szIconFile, int iIndex, UINT wFlags);
int CacheIcons(HICON hiconLarge, HICON hiconSmall,
LPCSTR szIconFile, int iIndex, UINT wFlags);
static HRESULT ExtractIcon(LPCSTR szIconFile, int iIndex, UINT wFlags,
HICON *phiconLarge, HICON *phiconSmall, DWORD dwSizes);
static UINT GetIDString(LPSTR pszIDString, UINT uIDLen,
LPCSTR szIconFile, int iIndex, UINT wFlags);

HWND m_hwndLB;

CImageList m_cimlLg;
CImageList m_cimlSm;
} ;

#endif // _XICON_H_


README.TXT

Context Menu Extension

The DROPEXT sample demonstrates how to extend the drag-and-drop context
menu.

To test DROPEXT.DLL, you must:

1. Build DROPEXT.DLL.

2. Copy DROPEXT.DLL to the system directory (for example, c:\windows\system).

   -or-

   Modify DROPEXT.REG file to points to a fully qualified path to the DLL.

3. Run the command: regedit dropext.reg.

4. Use the right mouse button to drag a file from one folder to another.
   You will see additional menu items for the drop operation.


DROPEXT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1994 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//---------------------------------------------------------------------------
//
// File: dropext.c
//
//  This file contains a sample source code of context menu extension handler.
//
//---------------------------------------------------------------------------

#include "priv.h"

//
// Initialize GUIDs (should be done only and at-least once per DLL/EXE)
//
#pragma data_seg(".text")
#define INITGUID
#include <initguid.h>
#include "dropext.h"
#pragma data_seg()

//
// Function prototypes
//
HRESULT CALLBACK DropExt_CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR*);

//
// Global variables
//
UINT g_cRefThisDll = 0;// Reference count of this DLL.
UINT g_cfNetResource = 0;// Clipboard format

//---------------------------------------------------------------------------
// LibMain
//---------------------------------------------------------------------------
BOOL APIENTRY LibMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
    switch(dwReason)
    {
    case DLL_PROCESS_ATTACH:
    break;
    case DLL_PROCESS_DETACH:
    break;

    case DLL_THREAD_DETACH:
        break;

    case DLL_THREAD_ATTACH:
    default:
    break;
    } // end switch()

    return TRUE;
}

//---------------------------------------------------------------------------
// DllCanUnloadNow
//---------------------------------------------------------------------------

STDAPI DllCanUnloadNow(void)
{
    return ResultFromScode((g_cRefThisDll==0) ? S_OK : S_FALSE);
}

//---------------------------------------------------------------------------
//
// DllGetClassObject
//
//  This is the entry of this DLL, which all the In-Proc server DLLs should
// export. See the description of "DllGetClassObject" of OLE 2.0 reference
// manual for detail.
//
//---------------------------------------------------------------------------

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppvOut)
{
    //
    //  This DLL has only one class (CLSID_SampleDropExt). If a DLL supports
    // multiple classes, it should have either multiple if-statements or
    // efficient table lookup code.
    //
    if (IsEqualIID(rclsid, &CLSID_SampleDropExt))
    {
//
//  We are supposed return the class object for this class. Instead
// of fully implementing it in this DLL, we just call a helper
// function in the shell DLL which creates a default class factory
// object for us. When its CreateInstance member is called, it
// will call back our create instance function (DropExt_CreateInstance).
//
return SHCreateDefClassObject(
    riid,    //
    ppvOut,    //
    DropExt_CreateInstance, // callback function
    &g_cRefThisDll,    // reference count of this DLL
    &IID_IShellExtInit    // init interface
    );
    }

    return ResultFromScode(E_FAIL);
}

//---------------------------------------------------------------------------
//
// CSampleDropExt class
//
// In C++:
//  class CSampleDropExt : protected IContextMenu, protected IShellExtInit
//  {
//  protected:
//      UINT         _cRef;
//      LPDATAOBJECT _pdtobj;
//HKEY     _hkeyProgID;
//  public:
//      CSampleDropExt() _cRef(1), _pdtobj(NULL), _hkeyProgID(NULL) {};
//      ...
//  };
//
//---------------------------------------------------------------------------
typedef struct _CSampleDropExt// smx
{
    IContextMenu    _ctm;           // 1st base class
    IShellExtInit   _sxi;    // 2nd base class
    UINT            _cRef;          // reference count
    LPDATAOBJECT    _pdtobj;    // data object
    HKEY    _hkeyProgID;    // reg. database key to ProgID
    char    _szTargetFolder[MAX_PATH];
} CSampleDropExt, * PSAMPLEDROPEXT;

#define SMX_OFFSETOF(x)        ((UINT)(&((PSAMPLEDROPEXT)0)->x))
#define PVOID2PSMX(pv,offset)   ((PSAMPLEDROPEXT)(((LPBYTE)pv)-offset))
#define PCTM2PSMX(pctm)        PVOID2PSMX(pctm, SMX_OFFSETOF(_ctm))
#define PSXI2PSMX(psxi)        PVOID2PSMX(psxi, SMX_OFFSETOF(_sxi))

//
// Vtable prototype
//
extern IContextMenuVtbl     c_SampleDropExt_CTMVtbl;
extern IShellExtInitVtbl    c_SampleDropExt_SXIVtbl;

//---------------------------------------------------------------------------
//
// DropExt_CreateInstance
//
//  This function is called back from within IClassFactory::CreateInstance()
// of the default class factory object, which is created by SHCreateClassObject.
//
//---------------------------------------------------------------------------

HRESULT CALLBACK DropExt_CreateInstance(LPUNKNOWN punkOuter,
        REFIID riid, LPVOID FAR* ppvOut)
{
    HRESULT hres;
    PSAMPLEDROPEXT psmx;

    //
    // Shell extentions typically does not support aggregation.
    //
    if (punkOuter) {
return ResultFromScode(CLASS_E_NOAGGREGATION);
    }

    //
    // in C++:
    //  psmx = new CSampleDropExt();
    //
    psmx = LocalAlloc(LPTR, sizeof(CSampleDropExt));
    if (!psmx) {
return ResultFromScode(E_OUTOFMEMORY);
    }
    psmx->_ctm.lpVtbl = &c_SampleDropExt_CTMVtbl;
    psmx->_sxi.lpVtbl = &c_SampleDropExt_SXIVtbl;
    psmx->_cRef = 1;
    psmx->_pdtobj = NULL;
    psmx->_hkeyProgID = NULL;
    g_cRefThisDll++;

    //
    // in C++:
    //  hres = psmx->QueryInterface(riid, ppvOut);
    //  psmx->Release();
    //
    // Note that the Release member will free the object, if QueryInterface
    // failed.
    //
    hres = c_SampleDropExt_CTMVtbl.QueryInterface(&psmx->_ctm, riid, ppvOut);
    c_SampleDropExt_CTMVtbl.Release(&psmx->_ctm);

    return hres;// S_OK or E_NOINTERFACE
}

//---------------------------------------------------------------------------
// CSampleDropExt::Initialize (IShellExtInit override)
//---------------------------------------------------------------------------
STDMETHODIMP DropExt_Initialize(LPSHELLEXTINIT psxi,
LPCITEMIDLIST pidlFolder,
LPDATAOBJECT pdtobj,
HKEY hkeyProgID)
{
    PSAMPLEDROPEXT this = PSXI2PSMX(psxi);

    // Initialize can be called more than once.
    if (this->_pdtobj) {
this->_pdtobj->lpVtbl->Release(this->_pdtobj);
    }

    if (this->_hkeyProgID) {
RegCloseKey(this->_hkeyProgID);
    }

    // Duplicate the pdtobj pointer
    if (pdtobj) {
this->_pdtobj = pdtobj;
pdtobj->lpVtbl->AddRef(pdtobj);
    }

    // Duplicate the handle
    if (hkeyProgID) {
RegOpenKeyEx(hkeyProgID, NULL, 0L, MAXIMUM_ALLOWED, &this->_hkeyProgID);
    }

    // Get the path to the drop target folder
    if (pidlFolder) {
SHGetPathFromIDList(pidlFolder, this->_szTargetFolder);
    }

    return NOERROR;
}

STDMETHODIMP DropExt_QueryContextMenu(LPCONTEXTMENU pctm,
                                               HMENU hmenu,
                                               UINT indexMenu,
                                               UINT idCmdFirst,
                                               UINT idCmdLast,
       UINT uFlags)
{
    UINT idCmd = idCmdFirst;
    InsertMenu(hmenu, indexMenu++, MF_STRING|MF_BYPOSITION, idCmd++, "Check H&DROP (dropext)");
    InsertMenu(hmenu, indexMenu++, MF_STRING|MF_BYPOSITION, idCmd++, "Check H&NRES (dropext)");
    return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)2));
}

//
// Converts an offset to a string to a string pointer.
//
LPCSTR _Offset2Ptr(LPSTR pszBase, UINT offset, UINT * pcb)
{
    LPSTR pszRet;
    if (offset==0) {
pszRet = NULL;
*pcb = 0;
    } else {
pszRet = pszBase + offset;
*pcb = lstrlen(pszRet) + 1;
    }
    return pszRet;
}

//
// This is a helper routine which extracts a specified NETRESOURCE from hnres.
//
UINT DropExt_GetNetResource(HGLOBAL hnres, UINT iItem, LPNETRESOURCE pnresOut, UINT cbMax)
{
    LPNRESARRAY panr = GlobalLock(hnres);
    UINT iRet = 0;// assume error
    if (hnres)
    {
if (iItem==(UINT)-1)
{
    iRet = panr->cItems;
}
else if (iItem < panr->cItems)
{
    UINT cbProvider, cbRemoteName;
    LPCSTR pszProvider = _Offset2Ptr((LPSTR)panr, (UINT)panr->nr[iItem].lpProvider, &cbProvider);
    LPCSTR pszRemoteName = _Offset2Ptr((LPSTR)panr, (UINT)panr->nr[iItem].lpRemoteName, &cbRemoteName);
    iRet = sizeof(NETRESOURCE) + cbProvider + cbRemoteName;
    if (iRet <= cbMax)
    {
LPSTR psz = (LPSTR)(pnresOut+1);
*pnresOut = panr->nr[iItem];
if (pnresOut->lpProvider) {
    pnresOut->lpProvider = psz;
    lstrcpy(psz, pszProvider);
    psz += cbProvider;
}
if (pnresOut->lpRemoteName) {
    pnresOut->lpRemoteName = psz;
    lstrcpy(psz, pszRemoteName);
}
    }
}
GlobalUnlock(hnres);
    }
    return iRet;
}

HRESULT DropExt_CheckForHDROP(PSAMPLEDROPEXT this, HWND hwnd)
{
    FORMATETC fmte = {
        CF_HDROP,
        (DVTARGETDEVICE FAR *)NULL,
        DVASPECT_CONTENT,
        -1,
        TYMED_HGLOBAL };
    STGMEDIUM medium;
    HRESULT hres = this->_pdtobj->lpVtbl->GetData(this->_pdtobj, &fmte, &medium);

    if (SUCCEEDED(hres))
    {
HDROP hdrop = medium.hGlobal;
UINT cFiles = DragQueryFile(hdrop, (UINT)-1, NULL, 0);
char szFile[MAX_PATH];
char szBuf[MAX_PATH*2+64];

DragQueryFile(hdrop, 0, szFile, sizeof(szFile));
wsprintf(szBuf,
 "The drop target is %s.\n"
  "%d files/directories in HDROP\n"
 "The path to the first object is\n"
    "\t%s.",
 this->_szTargetFolder,
 cFiles,
 szFile);

MessageBox(hwnd, szBuf, "DropExt", MB_OK);

//
// HACK: We are supposed to call ReleaseStgMedium. This is a temporary
//  hack until OLE 2.01 for Windows 95 is released.
//
if (medium.pUnkForRelease)
{
    medium.pUnkForRelease->lpVtbl->Release(medium.pUnkForRelease);
}
else
{
    GlobalFree(medium.hGlobal);
}
    }
    else
    {
MessageBox(hwnd, "No file system object in the selection", "DropExt", MB_OK);
    }

    return hres;
}

HRESULT DropExt_CheckForHNRES(PSAMPLEDROPEXT this, HWND hwnd)
{
    FORMATETC fmte = {
        g_cfNetResource ? g_cfNetResource
    : (g_cfNetResource=RegisterClipboardFormat("Net Resource")),
        (DVTARGETDEVICE FAR *)NULL,
        DVASPECT_CONTENT,
        -1,
        TYMED_HGLOBAL };
    STGMEDIUM medium;
    HRESULT hres = this->_pdtobj->lpVtbl->GetData(this->_pdtobj, &fmte, &medium);
    if (SUCCEEDED(hres))
    {
HGLOBAL hnres = medium.hGlobal;
LPNETRESOURCE pnr = (LPNETRESOURCE)LocalAlloc(LPTR, 1024);

if (pnr)
{
    char szBuf[512];
    UINT cItems = DropExt_GetNetResource(hnres, (UINT)-1, NULL, 0);

    // Get the NETRESOURCE of the first item
    DropExt_GetNetResource(hnres, 0, pnr, 1024);

    wsprintf(szBuf,
     "%d network resource objects in HNRES\n"
     "The attributes of the first object are\n"
     "\tProvider      = %s\n"
     "\tRemoteName    = %s\n"
     "\tdwDisplayType = %x\n"
     "\tdwType        = %x\n"
     "\tdwUsage       = %x\n",
     cItems,
     pnr->lpProvider,
     pnr->lpRemoteName ? pnr->lpRemoteName : "N/A",
     pnr->dwDisplayType,
     pnr->dwType,
     pnr->dwUsage);

    MessageBox(hwnd, szBuf, "DropExt", MB_OK);

    LocalFree(pnr);
}
else
{
    hres = ResultFromScode(E_OUTOFMEMORY);
}

//
// HACK: We are supposed to call ReleaseStgMedium. This is a temporary
//  hack until OLE 2.01 for Windows 95 is released.
//
if (medium.pUnkForRelease)
{
    medium.pUnkForRelease->lpVtbl->Release(medium.pUnkForRelease);
}
else
{
    GlobalFree(medium.hGlobal);
}
    }
    else
    {
MessageBox(hwnd, "No network objects in the selection", "DropExt", MB_OK);
    }
    return hres;
}

STDMETHODIMP DropExt_InvokeCommand(LPCONTEXTMENU pctm,
   LPCMINVOKECOMMANDINFO lpici)
{
    PSAMPLEDROPEXT this = PCTM2PSMX(pctm);
    HRESULT hres = ResultFromScode(E_INVALIDARG);// assume error
    //
    // No need to support string based command.
    //
    if (!HIWORD(lpici->lpVerb))
    {
UINT idCmd = LOWORD(lpici->lpVerb);

switch(idCmd)
{
case 0:
    hres = DropExt_CheckForHDROP(this, lpici->hwnd);
    break;

case 1:
    hres = DropExt_CheckForHNRES(this, lpici->hwnd);
    break;
}
    }
    return hres;
}

STDMETHODIMP DropExt_GetCommandString(
                                        LPCONTEXTMENU pctm,
                                        UINT        idCmd,
                                        UINT        wReserved,
                                        UINT FAR *  pwReserved,
                                        LPSTR       pszName,
                                        UINT        cchMax)
{
    return NOERROR;
}

//---------------------------------------------------------------------------
// CSampleDropExt::AddRef (IContextMenu override)
//---------------------------------------------------------------------------

STDMETHODIMP_(UINT) DropExt_CTM_AddRef(LPCONTEXTMENU pctm)
{
    PSAMPLEDROPEXT this = PCTM2PSMX(pctm);
    return ++this->_cRef;
}

//---------------------------------------------------------------------------
// CSampleDropExt::AddRef (IShellExtInit override)
//---------------------------------------------------------------------------

STDMETHODIMP_(UINT) DropExt_SXI_AddRef(LPSHELLEXTINIT psxi)
{
    PSAMPLEDROPEXT this = PSXI2PSMX(psxi);
    return ++this->_cRef;
}

//---------------------------------------------------------------------------
// CSampleDropExt::Release (IContextMenu override)
//---------------------------------------------------------------------------

STDMETHODIMP_(UINT) DropExt_CTM_Release(LPCONTEXTMENU pctm)
{
    PSAMPLEDROPEXT this = PCTM2PSMX(pctm);
    if (--this->_cRef) {
return this->_cRef;
    }

    if (this->_pdtobj) {
this->_pdtobj->lpVtbl->Release(this->_pdtobj);
    }

    if (this->_hkeyProgID) {
RegCloseKey(this->_hkeyProgID);
    }

    LocalFree((HLOCAL)this);
    g_cRefThisDll--;

    return 0;
}

//---------------------------------------------------------------------------
// CSampleDropExt::Release (IShellExtInit thunk)
//---------------------------------------------------------------------------

STDMETHODIMP_(UINT) DropExt_SXI_Release(LPSHELLEXTINIT psxi)
{
    PSAMPLEDROPEXT this = PSXI2PSMX(psxi);
    return DropExt_CTM_Release(&this->_ctm);
}

//---------------------------------------------------------------------------
// CSampleDropExt::QueryInterface (IContextMenu override)
//---------------------------------------------------------------------------

STDMETHODIMP DropExt_CTM_QueryInterface(LPCONTEXTMENU pctm, REFIID riid, LPVOID FAR* ppvOut)
{
    PSAMPLEDROPEXT this = PCTM2PSMX(pctm);
    if (IsEqualIID(riid, &IID_IContextMenu) || IsEqualIID(riid, &IID_IUnknown))
    {
        (LPCONTEXTMENU)*ppvOut=pctm;
        this->_cRef++;
        return NOERROR;
    }
    if (IsEqualIID(riid, &IID_IShellExtInit))
    {
        (LPSHELLEXTINIT)*ppvOut=&this->_sxi;
        this->_cRef++;
        return NOERROR;
    }
    return ResultFromScode(E_NOINTERFACE);
}

//---------------------------------------------------------------------------
// CSampleDropExt::QueryInterface (IShellExtInit thunk)
//---------------------------------------------------------------------------

STDMETHODIMP DropExt_SXI_QueryInterface(LPSHELLEXTINIT psxi, REFIID riid, LPVOID FAR* ppv)
{
    PSAMPLEDROPEXT this = PSXI2PSMX(psxi);
    return DropExt_CTM_QueryInterface(&this->_ctm, riid, ppv);
}

//---------------------------------------------------------------------------
// CSampleDropExt class : Vtables
//---------------------------------------------------------------------------

#pragma data_seg(".text")
IContextMenuVtbl c_SampleDropExt_CTMVtbl = {
    DropExt_CTM_QueryInterface,
    DropExt_CTM_AddRef,
    DropExt_CTM_Release,
    DropExt_QueryContextMenu,
    DropExt_InvokeCommand,
    DropExt_GetCommandString,
};

IShellExtInitVtbl c_SampleDropExt_SXIVtbl = {
    DropExt_SXI_QueryInterface,
    DropExt_SXI_AddRef,
    DropExt_SXI_Release,
    DropExt_Initialize
};
#pragma data_seg()


DROPEXT.H

//
// The class ID of this menu extension class.
//  "{A4D78B20-6E05-1069-8758-08002B2BD64F}"
//
DEFINE_GUID(CLSID_SampleDropExt, 0xA4D78B20, 0x6E05, 0x1069, 0x87,0x58,0x08,0x00,0x2B,0x2B,0xD6,0x4F);


PRIV.H

//--------------------------------------------------------------
// common user interface routines
//
//
//--------------------------------------------------------------

#define STRICT
#define _INC_OLE        // WIN32, get ole2 from windows.h
#define CONST_VTABLE

#include <windows.h>
#include <commdlg.h>
#include <dlgs.h>       // commdlg IDs
#include <shellapi.h>
#include <commctrl.h>
#include <windowsx.h>
#include <ole2.h>
#include <shlobj.h>

//
// defclsf.c
//
typedef HRESULT (CALLBACK FAR * LPFNCREATEINSTANCE)(LPUNKNOWN pUnkOuter,
REFIID riid, LPVOID FAR* ppvObject);

STDAPI SHCreateDefClassObject(REFIID riid, LPVOID FAR* ppv,
 LPFNCREATEINSTANCE lpfnCI, UINT FAR * pcRefDll,
 REFIID riidInst);

//
// Helper macro for implemting OLE classes in C
//
#define _IOffset(class, itf)         ((UINT)&(((class *)0)->itf))
#define IToClass(class, itf, pitf)   ((class  *)(((LPSTR)pitf)-_IOffset(class, itf)))
#define IToClassN(class, itf, pitf)  IToClass(class, itf, pitf)


DEFCLSF.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1995 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//
// This file contains the implementation of SHCreateDefClassObject
//

#include "priv.h"

//=========================================================================
// CDefClassFactory class
//=========================================================================

STDMETHODIMP CDefClassFactory_QueryInterface(IClassFactory FAR * pcf, REFIID riid, LPVOID FAR* ppvObj);
ULONG STDMETHODCALLTYPE CDefClassFactory_AddRef(IClassFactory FAR * pcf);
ULONG STDMETHODCALLTYPE CDefClassFactory_Release(IClassFactory FAR * pcf);
STDMETHODIMP CDefClassFactory_CreateInstance(IClassFactory FAR * pcf, LPUNKNOWN pUnkOuter,
                              REFIID riid,
                              LPVOID FAR* ppvObject);
STDMETHODIMP CDefClassFactory_LockServer(IClassFactory FAR * pcf, BOOL fLock);

//
// CDefClassFactory: Class definition
//
#pragma data_seg(".text")
IClassFactoryVtbl c_vtblAppUIClassFactory = {
            CDefClassFactory_QueryInterface,
            CDefClassFactory_AddRef,
            CDefClassFactory_Release,
            CDefClassFactory_CreateInstance,
    CDefClassFactory_LockServer
};
#pragma data_seg()

typedef struct
{
    IClassFactory      cf;
    UINT               cRef;// Reference count
    LPFNCREATEINSTANCE lpfnCI;// CreateInstance callback entry
    UINT FAR *         pcRefDll;// Reference count of the DLL
    const IID FAR *    riidInst;// Optional interface for instance
} CDefClassFactory;

//
// CDefClassFactory::QueryInterface
//
STDMETHODIMP CDefClassFactory_QueryInterface(IClassFactory FAR * pcf, REFIID riid, LPVOID FAR* ppvObj)
{
    register CDefClassFactory * this=IToClassN(CDefClassFactory, cf, pcf);
    if (IsEqualIID(riid, &IID_IClassFactory)
     || IsEqualIID(riid, &IID_IUnknown))
    {
(LPCLASSFACTORY)*ppvObj = &this->cf;
this->cRef++;
        return NOERROR;
    }
    return ResultFromScode(E_NOINTERFACE);
}

//
// CDefClassFactory::AddRef
//
ULONG STDMETHODCALLTYPE CDefClassFactory_AddRef(IClassFactory FAR * pcf)
{
    register CDefClassFactory * this=IToClassN(CDefClassFactory, cf, pcf);
    return (++this->cRef);
}

//
// CDefClassFactory::Release
//
ULONG STDMETHODCALLTYPE CDefClassFactory_Release(IClassFactory FAR * pcf)
{
    register CDefClassFactory * this=IToClassN(CDefClassFactory, cf, pcf);
    if (--this->cRef > 0)
return this->cRef;

    if (this->pcRefDll) {
(*this->pcRefDll)--;
    }
    LocalFree((HLOCAL)this);

    return 0;
}

//
// CDefClassFactory::CDefClassFactory
//
STDMETHODIMP CDefClassFactory_CreateInstance(IClassFactory FAR * pcf, LPUNKNOWN pUnkOuter,
                              REFIID riid,
                              LPVOID FAR* ppvObject)
{
    register CDefClassFactory * this=IToClassN(CDefClassFactory, cf, pcf);

    //
    // We don't support aggregation at all.
    //
    if (pUnkOuter)
    {
return ResultFromScode(CLASS_E_NOAGGREGATION);
    }

    //
    // if this->riidInst is specified, they should match.
    //
    if (this->riidInst==NULL
     || IsEqualIID(riid, this->riidInst)
     || IsEqualIID(riid, &IID_IUnknown))
    {
return this->lpfnCI(pUnkOuter, riid, ppvObject);
    }

    return ResultFromScode(E_NOINTERFACE);
}

//
// CDefClassFactory::LockServer
//
STDMETHODIMP CDefClassFactory_LockServer(IClassFactory FAR * pcf, BOOL fLock)
{
    // REVIEW: Is this appropriate?
    return ResultFromScode(E_NOTIMPL);
}

//
// CDefClassFactory constructor
//
CDefClassFactory * NEAR PASCAL CDefClassFactory_Create(
    LPFNCREATEINSTANCE lpfnCI, UINT FAR * pcRefDll, REFIID riidInst)
{
    register CDefClassFactory * pacf;
    pacf = (CDefClassFactory *)LocalAlloc(LPTR, sizeof(CDefClassFactory));
    if (pacf)
    {
pacf->cf.lpVtbl = &c_vtblAppUIClassFactory;
pacf->cRef++;  // pacf->cRef=0; (generates smaller code)
pacf->pcRefDll = pcRefDll;
pacf->lpfnCI = lpfnCI;
pacf->riidInst = riidInst;
if (pcRefDll) {
    (*pcRefDll)++;
}
    }
    return pacf;
}

// BUGBUG: remove this for build 86

STDAPI Shell_CreateDefClassObject(REFIID riid, LPVOID FAR* ppv,
 LPFNCREATEINSTANCE lpfnCI, UINT FAR * pcRefDll,
 REFIID riidInst)
{
    MessageBeep(0);
    // DebugMsg(DM_TRACE, "Shell_CreateDefClassObject: obsolete, use SHCreateDefClassObject");
    return SHCreateDefClassObject(riid, ppv, lpfnCI, pcRefDll, riidInst);
}

//
// creates a simple default implementation of IClassFactory
//
// Parameters:
//  riid     -- Specifies the interface to the class object
//  ppv      -- Specifies the pointer to LPVOID where the class object pointer
//               will be returned.
//  lpfnCI   -- Specifies the callback entry for instanciation.
//  pcRefDll -- Specifies the address to the DLL reference count (optional)
//  riidInst -- Specifies the interface to the instance (optional).
//
// Notes:
//   The riidInst will be specified only if the instance of the class
//  support only one interface.
//
STDAPI SHCreateDefClassObject(REFIID riid, LPVOID FAR* ppv,
 LPFNCREATEINSTANCE lpfnCI, UINT FAR * pcRefDll,
 REFIID riidInst)
{
    // The default class factory supports only IClassFactory interface
    if (IsEqualIID(riid, &IID_IClassFactory))
    {
CDefClassFactory * pacf = CDefClassFactory_Create(lpfnCI, pcRefDll, riidInst);
if (pacf)
{
    (IClassFactory FAR *)*ppv = &pacf->cf;
    return NOERROR;
}
return ResultFromScode(E_OUTOFMEMORY);
    }
    return ResultFromScode(E_NOINTERFACE);
}


README.TXT

File Viewer

SUMMARY
=======

The FILEVIEW sample demonstrates the basic steps to implement a simple
text-based file viewer. This particular sample is a viewer for text files,
those with the .TXT extension, as well as any compound file with the CLSID
of CLSID_SampleTextFile, defined as 0x00021116-0000-C000-000000000046.
The files FLATTEXT.TXT and COMPTEXT.TXT are two such files.

If you build the sample viewer and register it via the FVTEXT.REG file,
you will be able to use the sample viewer. Note that the system has
a .TXT file viewer installed by default. To disable the default viewer,
rename the vsasc8.dll file in the system\viewers subdirectory or edit
FVTEXT.REG and change the .TXT extension to another extension not
supported by QuickView, like XXX, and then use text files with the XXX
extension.

You can build the sample using Microsoft NMAKE. Be aware that building the
sample produces a DLL. Once this DLL is registered, clicking with the right
mouse button on one of the .TXT (XXX) files will display a context menu.
Within this context menu, you will see the QuickView option. If you click on
this menu item, the sample file viewer will be invoked.

MORE INFORMATION
================

This sample was designed to require only small amounts of modification
to build a custom viewer and the areas where you would need to make
changes are clearly marked in the source code. The FileViewer component
object itself used CLSID_FileViewerText:0x00021117-0000-C000-000000000046.
Do not use this CLSID yourself! You will need to create your own unique
CLSID using the GUIDGEN.EXE utility that comes with the SDK.

This sample builds FVTEXT.DLL and places it in the
C:\WINDOWS\SYSTEM\VIEWERS directory. You need to create a different name
for your own Viewer and use your particular system path. Rename the
following files to your custom name and make the indicated filename changes
within those files.

Files        Description

FVTEXT.CPP   Main source file. Change occurrences of FVTEXT to your own
             name.
FVTEXT.CPP   Contains the CFileViewer object implementation, where most of
             the significant code is. There is not much in this file but
             the CFileViewer member functions. Most of the functions will
             require modification, depending on the type of viewer you
             plan to implement. For the most part, most of the functions and
             fields in CFileViewer need no modification. Those specific
             places needing modification are marked with MODIFY.
FVTEXT.DEF   Module definition file. Change the LIBRARY and DESCRIPTION
             lines to match your implementation. NOTE: You must export
             DllGetClassObject and DllCanUnloadNow.
FVTEXT.H     Main object header file. Change occurrences of FVTEXT to your
             own name.

             It is MOST IMPORTANT that you not use the CLSIDs defined here.
             These are strictly for use with this sample. Use the SDK tool
             GUIDGEN.EXE to generate your own unique CLSIDs.
FVTEXT.ICO   Icon to use for FileViewer window. Use your own icon.
FVTEXT.RC    Resources. Change the FVTEXT in the header and the references
             to fvtext.ico and fvtext.rcv to your own names.
FVTEXT.RCV   Version information. Change FVTEXT in the header to your own
             name.
FVTEXT.REG   Registry information for the FileViewer. Change the
             InprocServer32 entry (the last line) to your own DLL name.
             Change the extension from CPP to the extension you are
             supporting.
MAKEFILE     This is an external makefile for use with NMAKE. It is not a
             Visual C++ project file. In general, you will have to change
             the following macros definitions for a custom viewer:

             Macro           Location    Use

             DEFFILE         Line 24     Indicates your .DEF file
             TARGET          Line 42     Indicates the name of the
                                         FileViewer. MAKEFILE assumes you
                                         have .CPP, .H, .RC, .RCV, and .ICO
                                         files with this name and are
                                         building a .DLL with this name.

             The dependency list at the bottom of the file should not need
             any modifications unless you rename other files or add new
             files. If you add new files, add them to the OBJS macro at
             line 58.
CSTATHLP.CPP Definition and implementation of a class called CStatusHelper
CSTATHLP.H   that simplifies WM_MENUSELECT processing. To utilize this
             object, create one and call its MessageMap function (see
             CSTATHLP.CPP for documentation). You can then call its
             MenuSelect function to handle all WM_MENUSELECT messages. You
             can also ask CStatusHelper to display a specific message in
             the message map by calling MessageDisplay. You pass an
             identifier of the message in the message map and the
             CStatusHelper will go locate the string and display it.
CSTRTABL.CPP Definition and implementation of a CStingTable class that
CSTRTABL.H   simplifies stringtable management.
DBGOUT.H     Contains a number of macros that help keep source code clean
             when including debug output and conditional code.
IFILEVW.CPP  Implements the IFileViewer interface required of a FileViewer
             using a class CImpIFileViewer (defined in FVTEXT.H). You should
             not have to make any changes to this file. The IUnknown members
             are delegated to CFileViewer's IUnknown implementation.
IPERFILE.CPP Implements the IPersistFile interface for a FileViewer using
             the class CImpIPersistFile.
FILEVIEW.CPP
FILEVIEW.H   The master header file for the entire sample that most of the
             .CPP files include. It pulls in the other header files
             necessary for the specific viewer. In this sample, it brings in
             FVTEXT.H and RESOURCE.H. Your FileViewer should have a
             different name, so you'll want to change the reference to
             FVTEXT.H.
FVPROC.CPP   Contains the window procedures for the frame window, the
             viewport window, and the about box. ViewportWndProc is what
             generates the display of the file. This will require major
             modifications depending on what you plan to do.


CSTATHLP.H

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM: CSTATHLP.H
//
//PURPOSE: Class and structure definitions for the CStatusHelper class
//  that manages messages to display in the status bar.
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS: N/A
//
#ifndef _CSTATHLP_H_
#define _CSTATHLP_H_

//For loading the RCDATA mapping menu item IDs to string IDs
typedef struct tagSTATMESSAGEMAP
    {
    USHORT      uID;
    USHORT      idsMsg;
    } STATMESSAGEMAP, *PSTATMESSAGEMAP;

//Array mapping menu handles to menu item IDs
typedef struct tagPOPUPMENUMAP
    {
    HMENU       hMenu;
    USHORT      uID;
    } POPUPMENUMAP, *PPOPUPMENUMAP;

/*
 * The CStatusHelper class simplifies run-time management of
 * status bar messages by setting up a mapping between popup
 * menu handles and item IDs to resource string IDs.  The
 * MenuSelect member processes any WM_MENUSELECT message
 * automatically to display the appropriate message.
 */

class CStatusHelper
    {
    protected:
        HWND                m_hWnd;             //hWnd of control
        HINSTANCE           m_hInst;            //Module instance
        BOOL                m_fMapped;          //MessageMap called?
        PCStringTable       m_pST;              //Message strings

        UINT                m_uIDCur;           //Current displayed ID.

        HWND                m_hWndOwner;        //Frame window
        USHORT              m_cMessages;        //Number of messages
        UINT                m_idsMin;           //Lowest string ID
        UINT                m_idsMax;           //Maximum string ID

        USHORT              m_uIDStatic;        //Quiescent message ID
        USHORT              m_uIDBlank;         //Blank message ID
        USHORT              m_uIDSysMenu;       //System menu message ID

        USHORT              m_cPopups;          //Number of menus
        USHORT              m_uIDPopupMin;      //Lowest menu ID
        USHORT              m_uIDPopupMax;      //Highest menu ID

        HGLOBAL             m_hMemSMM;          //Mapping resource
        PSTATMESSAGEMAP     m_pSMM;             //Item to ID mapping
        PPOPUPMENUMAP       m_pPMM;             //Menu to ID mapping

    public:
        CStatusHelper(HWND, HINSTANCE);
        ~CStatusHelper(void);

        BOOL   MessageMap(HWND, UINT, UINT, UINT, UINT, UINT, UINT, UINT, UINT, UINT);
        void   MessageDisplay(UINT);
        void   MenuSelect(WORD, WORD, HMENU);

    private:
        void   Sort(void);
        USHORT IDFromHMenu(HMENU);
        UINT   IStringFromID(USHORT);

    };

typedef CStatusHelper *PCStatusHelper;

#endif //_CSTATHLP_H_


CSTRTABL.CPP

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM:CSTRTABL.CPP
//
//PURPOSE:   Implementation of a string table handler.  The CStringTable
// class hides details of storage from the user.  The strings might
// be cached, or they might be loaded as necessary.  In either case,
// we must know the number of strings so we know whether or not to
// reload strings.
//
//PLATFORMS:Windows 95
//
//FUNCTIONS:
//
//
//SPECIAL INSTRUCTIONS: N/A
//
#include "fileview.h"

//
//   FUNCTION: CStringTable::CStringTable
//
//   PURPOSE: Constructor
//
//   PARAMETERS:
//     hInst           HANDLE to the module instance from which we  load strings.
//
CStringTable::CStringTable(HINSTANCE hInst)
    {
    m_hInst=hInst;
    m_pszStrings=NULL;
    m_ppszTable=NULL;
    return;
    }

//
//   FUNCTION:  CStringTable::~CStringTable
//
//   PURPOSE: Destructor
//
CStringTable::~CStringTable(void)
    {
    if (NULL!=m_pszStrings)
        free(m_pszStrings);

    if (NULL!=m_ppszTable)
        free(m_ppszTable);

    return;
    }
//
//   FUNCTION: CStringTable::FInit
//
//   PURPOSE:    Initialization function for a StringTable that is prone to
//  failure.  If this fails then the caller is responsible for
//  guaranteeing that the destructor is called quickly.
//
//   PARAMETERS:
//   idsMin          UINT first identifier in the stringtable
//  idsMax          UINT last identifier in the stringtable.
//  cchMax          UINT with the maximum string length allowed.
//
//   RETURN VALUE:
//    BOOL            TRUE if the function is successful,  FALSE otherwise.
//
BOOL CStringTable::FInit(UINT idsMin, UINT idsMax, UINT cchMax)
    {
    UINT        i;
    UINT        cch;
    UINT        cchUsed=0;
    LPSTR       psz;

    m_idsMin=idsMin;
    m_idsMax=idsMax;
    m_cStrings=(idsMax-idsMin+1);

    //Allocate space for the pointer table.
    m_ppszTable=(LPSTR *)malloc(sizeof(LPSTR)*m_cStrings);

    if (NULL==m_ppszTable)
        return FALSE;
     // Allocate enough memory for cStrings*cchMax characters.  80
     // characters is the maximum string length we allow.  This
     // will result in some unused memory, but a few K is not
     // worth quibbling over.
    m_pszStrings=(LPSTR)malloc(m_cStrings * cchMax);

    if (NULL==m_pszStrings)
        {
        free(m_ppszTable);
        m_ppszTable=NULL;
        return FALSE;
        }
     // Load the strings:  we load each string in turn into psz,
     // store the string pointer into the table and increment psz
     // to the next positions.
    psz=m_pszStrings;

    for (i=idsMin; i <= idsMax; i++)
        {
        m_ppszTable[i-idsMin]=psz;
        cch=LoadString(m_hInst, i, psz, 255);

        //Account for a null terminator with +1
        psz    +=cch+1;
        cchUsed+=cch;
        }

    return TRUE;
    }
//
//   FUNCTION: CStringTable::operator[]
//
//   PURPOSE:  Returns a pointer to the requested string in the stringtable
//  or NULL if the specified string does not exist.
//
const LPSTR CStringTable::operator[](const UINT uID) const
    {
    if (uID < m_idsMin || uID > m_idsMax)
        return NULL;

    return (const LPSTR)m_ppszTable[uID-m_idsMin];
    }


CSTRTABL.H

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM:CSTRTABL.H
//
//PURPOSE: Class and structure definitions for the CStringTable class
//   that helps to manage and retrieve strings from stringtables.
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS: N/A
//
#ifndef _CSTRTABL_H_
#define _CSTRTABL_H_

/*
 * CStringTable providing string table management.  Provides
 * simple [] array lookup using a stringtable ID to obtain
 * string pointers.
 */

class CStringTable
    {
    protected:
        HINSTANCE       m_hInst;
        UINT            m_idsMin;
        UINT            m_idsMax;
        USHORT          m_cStrings;
        LPSTR           m_pszStrings;
        LPSTR          *m_ppszTable;

    public:
        CStringTable(HINSTANCE);
        ~CStringTable(void);

        BOOL FInit(UINT, UINT, UINT);

        //Function to resolve an ID into a string pointer.
        const LPSTR operator [](const UINT) const;
    };

typedef CStringTable * PCStringTable;

#endif //_CSTRTABL_H_


DBGOUT.H

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM:DBGOUT.H
//
//PURPOSE:  Useful debugging output macros that compile to nothing and
//   eliminate ugly #ifdef DEBUGs from source code.
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS: N/A
//
#ifndef _DBGOUT_H
#define _DBGOUT_H

#ifdef DEBUG

//Basic debug macros
#define D(x)        {x;}

#define ODS(x)      {\
                    char        szDebug[128];\
                    OutputDebugString(x);\
                    wsprintf(szDebug, " %s, %u\r\n", (LPSTR)__FILE__, __LINE__);\
                    OutputDebugString(szDebug);\
                    }

#define ODSsz(f, s)  {\
                     char        szDebug[128];\
                     wsprintf(szDebug, f, (LPSTR)s);\
                     OutputDebugString(szDebug);\
                     wsprintf(szDebug, " %s, %u\r\n", (LPSTR)__FILE__, __LINE__);\
                     OutputDebugString(szDebug);\
                     }

#define ODSu(f, u)  {\
                    char        szDebug[128];\
                    wsprintf(szDebug, f, (UINT)u);\
                    OutputDebugString(szDebug);\
                    wsprintf(szDebug, " %s, %u\r\n", (LPSTR)__FILE__, __LINE__);\
                    OutputDebugString(szDebug);\
                    }

#define ODSlu(f, lu) {\
                     char        szDebug[128];\
                     wsprintf(szDebug, f, (DWORD)lu);\
                     OutputDebugString(szDebug);\
                     wsprintf(szDebug, " %s, %u\r\n", (LPSTR)__FILE__, __LINE__);\
                     OutputDebugString(szDebug);\
                     }

#define ODSszu(f, s, u) {\
                        char        szDebug[128];\
                        wsprintf(szDebug, f, (LPSTR)s, (UINT)u);\
                        OutputDebugString(szDebug);\
                        wsprintf(szDebug, " %s, %u\r\n", (LPSTR)__FILE__, __LINE__);\
                        OutputDebugString(szDebug);\
                        }

#define ODSszlu(f, s, lu) {\
                          char        szDebug[128];\
                          wsprintf(szDebug, f, (LPSTR)s, (DWORD)lu);\
                          OutputDebugString(szDebug);\
                          wsprintf(szDebug, " %s, %u\r\n", (LPSTR)__FILE__, __LINE__);\
                          OutputDebugString(szDebug);\
                          }

#else   //NO DEBUG

#define D(x)
#define ODS(x)

#define ODSsz(f, s)
#define ODSu(f, u)
#define ODSlu(f, lu)
#define ODSszu(f, s, u)
#define ODSszlu(f, s, lu)

#endif //DEBUG

#endif //_DBGOUT_H


FILEVIEW.CPP

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//    PROGRAM: FILEVIEW.CPP
//
//    PURPOSE:   File Viewer Component Object to work with Windows 95 Explorer.
// This file contains functions not specific to the nature of
// the data to view but contains the non-file-specific DLL
// and component object structure.  This can be used for a
// custom viewer by making modifications marked with //MODIFY
// comments.
//
//    PLATFORMS:    Windows 95
//
//    SPECIAL INSTRUCTIONS: N/A
//

//Define INITGUIDS once in entire module build
#define INITGUIDS
#include "fileview.h"

#include <initguid.h>
#include <shlguid.h>
#include "fvtext.h"     //FileViewer specifics

//Count number of objects and number of locks.
ULONG       g_cObj=0;
ULONG       g_cLock=0;

//Save this from LibMain
HINSTANCE   g_hInst;

//
//   FUNCTION:     LibMain
//
//   PURPOSE:   Entry point for a Win32 DLL
//
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, ULONG ulReason
    , PVOID pvReserved)
    {
    if (DLL_PROCESS_DETACH==ulReason)
        {
        return TRUE;
        }
    else
        {
        if (DLL_PROCESS_ATTACH!=ulReason)
            return TRUE;
        }

    g_hInst=hInstance;
    return TRUE;
    }
//
//   FUNCTION:     DllGetClassObject
//
//   PURPOSE:  Provides an IClassFactory for a given CLSID that this DLL is
//  registered to support.  This DLL is placed under the CLSID
//  in the registration database as the InProcServer.
//
//   PARAMETERS:
//       clsID           REFCLSID that identifies the class factory
//                  desired.  Since this parameter is passed this
//                  DLL can handle any number of objects simply
//                 by returning different class factories here
//                  for different CLSIDs.
//
//  riid            REFIID specifying the interface the caller wants
//                  on the class object, usually IID_ClassFactory.
//
//  ppv             PPVOID in which to return the interface pointer.
//
//   RETURN VALUE:
//        HRESULT         NOERROR on success, otherwise an error code.
//
HRESULT PASCAL DllGetClassObject(REFCLSID rclsid, REFIID riid
    , PPVOID ppv)
    {
    // Warning:: The shell may not have initialized OLE so
    // do it here to be sure...
    OleInitialize(NULL);

    //MODIFY:  Change CLSID_FileViewerText to your own CLSID
    if (!IsEqualCLSID(rclsid, CLSID_FileViewerText))
        return ResultFromScode(E_FAIL);

    //Check that we can provide the interface
    if (!IsEqualIID(riid, IID_IUnknown)
        && !IsEqualIID(riid, IID_IClassFactory))
        return ResultFromScode(E_NOINTERFACE);

    //Return our IClassFactory for our viewer objects
    *ppv=(LPVOID)new CFVClassFactory();

    if (NULL==*ppv)
        return ResultFromScode(E_OUTOFMEMORY);

    //AddRef the object through any interface we return
    ((LPUNKNOWN)*ppv)->AddRef();

    return NOERROR;
    }
//
//   FUNCTION: DllCanUnloadNow
//
//   PURPOSE:  Answers if the DLL can be freed, that is, if there are no
//    references to anything this DLL provides.
//
//   RETURN VALUE:
//       BOOL            TRUE if nothing is using us, FALSE otherwise.
//
STDAPI DllCanUnloadNow(void)
    {
    SCODE   sc;

    //Our answer is whether there are any object or locks
    sc=(0L==g_cObj && 0L==g_cLock) ? S_OK : S_FALSE;
    return ResultFromScode(sc);
    }
//
//   FUNCTION:     ObjectDestroyed
//
//   PURPOSE:  Function for the FileViewer object to call when it gets
//  destroyed. Since we're in a DLL we only track the number of
//  objects here, letting DllCanUnloadNow take care of the rest.
//
void PASCAL ObjectDestroyed(void)
    {
    g_cObj--;
    return;
    }
//
//   FUNCTION: CFVClassFactory::CFVClassFactory
//
//   PURPOSE:  Constructor
//
CFVClassFactory::CFVClassFactory(void)
    {
    m_cRef=0L;
    return;
    }

//
//   FUNCTION: CFVClassFactory::~CFVClassFactory
//
//   PURPOSE:   Destructor
//
CFVClassFactory::~CFVClassFactory(void)
    {
    return;
    }

//
//   FUNCTION:     CFVClassFactory::QueryInterface
//
STDMETHODIMP CFVClassFactory::QueryInterface(REFIID riid
    , PPVOID ppv)
    {
    *ppv=NULL;

    //Any interface on this object is the object pointer.
    if (IsEqualIID(riid, IID_IUnknown)
        || IsEqualIID(riid, IID_IClassFactory))
        *ppv=(LPVOID)this;

     // If we actually assign an interface to ppv we need to AddRef
     // it since we're returning a new pointer.
    if (NULL!=*ppv)
        {
        ((LPUNKNOWN)*ppv)->AddRef();
        return NOERROR;
        }

    return ResultFromScode(E_NOINTERFACE);
    }
//
//   FUNCTION:     CFVClassFactory::AddRef
//
STDMETHODIMP_(ULONG) CFVClassFactory::AddRef(void)
    {
    return ++m_cRef;
    }

//
//   FUNCTION:     CFVClassFactory::Release
//
STDMETHODIMP_(ULONG) CFVClassFactory::Release(void)
    {
    ULONG           cRefT;

    cRefT=--m_cRef;

    if (0L==m_cRef)
        delete this;

    return cRefT;
    }

//
//   FUNCTION:     CFVClassFactory::CreateInstance
//
//   PURPOSE:    Instantiates a CFileViewer object that will provide the
//  IPersistFile and IFileViewer interfaces for use with the Windows 95 Explorer.
//
//   PARAMETERS:
//  pUnkOuter       LPUNKNOWN to the controlling IUnknown if we are being used in an aggregation.
//  riid            REFIID identifying the interface the caller desires to have for the new object.
//  ppvObj          PPVOID in which to store the desired interface pointer for the new object.
//
//   RETURN VALUE:
//   HRESULT         NOERROR if successful, otherwise E_NOINTERFACE
//                  if we cannot support the requested interface.
//
STDMETHODIMP CFVClassFactory::CreateInstance(LPUNKNOWN pUnkOuter
    , REFIID riid, PPVOID ppvObj)
    {
    PCFileViewer        pObj;
    HRESULT             hr;

    *ppvObj=NULL;
    hr=ResultFromScode(E_OUTOFMEMORY);

    //Verify that a controlling unknown asks for IUnknown
    if (NULL!=pUnkOuter && !IsEqualIID(riid, IID_IUnknown))
        return ResultFromScode(E_NOINTERFACE);

     // MODIFY:  If you use a different object than CFileViewer
    // be sure to change the name and parameters here.  I do
    // recommend that you continue to follow this model, however,
    // and just modify CFileViewer as necessary.
    //Create the object passing function to notify on destruction.
    pObj=new CFileViewer(pUnkOuter, g_hInst, ObjectDestroyed);

    if (NULL==pObj)
        return hr;

    //MODIFY:  Add other parameters to Init as necessary.
    hr=pObj->Init();

    if (SUCCEEDED(hr))
        {
        //Return the requested interface
        hr=pObj->QueryInterface(riid, ppvObj);

        if (SUCCEEDED(hr))
            {
            //Everything worked:  count the object
            g_cObj++;
            return NOERROR;
            }
        }

    //Kill the object if anything failed after creation.
    delete pObj;

    return hr;
    }
//
//   FUNCTION: CFVClassFactory::LockServer
//
//   PURPOSE:  Increments or decrements the lock count of the DLL.  If the
//  lock count goes to zero and there are no objects, the DLL
//  is allowed to unload.  See DllCanUnloadNow.
//
//   PARAMETERS:
//    fLock           BOOL specifying whether to increment or decrement the lock count.
//
//   RETURN VALUE:
//     HRESULT         NOERROR always
//
STDMETHODIMP CFVClassFactory::LockServer(BOOL fLock)
    {
    if (fLock)
        g_cLock++;
    else
        g_cLock--;

    return NOERROR;
    }


FILEVIEW.H

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM: FILEVIEW.H
//
//PURPOSE:   File Viewer Component Object to work with Windows 95 Explorer.
//  Definitions, classes, and prototypes for a FileViewer DLL.
//    Necessary modifications marked with MODIFY
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS: N/A
//
#ifndef _FILEVIEW_H_
#define _FILEVIEW_H_

//Prevent windows.h from pulling in OLE 1
#define INC_OLE2

#include <windows.h>
#include <stdlib.h>
#include <ole2.h>
#include <commdlg.h>
#include <shlobj.h>

#include "dbgout.h"
#include "cstrtabl.h"
#include "cstathlp.h"

//Types needed for other include files.
#ifndef PPVOID
typedef LPVOID * PPVOID;
#endif

/*
 * Type and function for an object-destroyed callback.  An
 * Object will call ObjectDestoyed in FILEVIEW.CPP when it
 * deletes itself.  That way the server code, independent
 * of the object, can implement DllCanUnloadNow properly.
 */
typedef void (WINAPI *PFNDESTROYED)(void);
void WINAPI ObjectDestroyed(void);

//MODIFY:  Other viewer-specific headers
#include "fvtext.h"     //FileViewer specifics
#include "resource.h"   //Resource definitions

//FILEVIEW.CPP
HRESULT PASCAL DllGetClassObject(REFCLSID, REFIID, PPVOID);
STDAPI         DllCanUnloadNow(void);

//A class factory that creates CFileViewer objects
class CFVClassFactory : public IClassFactory
    {
    protected:
        ULONG           m_cRef;

    public:
        CFVClassFactory(void);
        ~CFVClassFactory(void);

        //IUnknown members
        STDMETHODIMP         QueryInterface(REFIID, PPVOID);
        STDMETHODIMP_(ULONG) AddRef(void);
        STDMETHODIMP_(ULONG) Release(void);

        //IClassFactory members
        STDMETHODIMP         CreateInstance(LPUNKNOWN, REFIID, PPVOID);
        STDMETHODIMP         LockServer(BOOL);
    };

typedef CFVClassFactory *PCFVClassFactory;

#endif //_FILEVIEW_H_


FVINIT.CPP

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//      PROGRAM: FVINIT.CPP
//
//      PURPOSE:  All initialization functions and some miscellany for the CFileViewer implementation.
//
//      PLATFORMS:      Windows 95
//
//      FUNCTIONS:
//
//
//      SPECIAL INSTRUCTIONS: N/A
//
#include "fileview.h"

//
//   FUNCTION: CFileViewer::Init
//
//   PURPOSE: Performs any intiailization of a CFileViewer that's prone to
//  failure that we also use internally before exposing the object
//  outside.
//
//   RETURN VALUE:
//   HRESULT         NOERROR if successful, error code otherwise.
//
HRESULT CFileViewer::Init(void)
    {
    LPUNKNOWN       pIUnknown=(LPUNKNOWN)this;
    WNDCLASS        wc;
    HRESULT         hr;
    HDC             hDC;

    //Default error code
    hr=ResultFromScode(E_OUTOFMEMORY);

    // Make pIUnknown point to outer unknown if we get one.
    // The interfaces allocated below are always given an IUnknown
    // to which they delegate at all times.  This will be either
    // the CFileViewer object's IUnknown itself or the outer
    // unknown if this object is aggregated.  The interfaces
    // need not know which case is being used.
    if (NULL!=m_pUnkOuter)
pIUnknown=m_pUnkOuter;

    //Allocate contained interfaces.
    m_pIPersistFile=new CImpIPersistFile(this, pIUnknown);

    if (NULL==m_pIPersistFile)
return hr;

    m_pIFileViewer=new CImpIFileViewer(this, pIUnknown);

    if (NULL==m_pIFileViewer)
return hr;

    m_pST=new CStringTable(m_hInst);

    if (NULL==m_pST)
return hr;

    // Go load the strings we need in CFileViewer::String.
    if (!m_pST->FInit(IDS_MIN, IDS_MAX, CCHSTRINGMAX))
return hr;

    //Register window classes that we'll need to display the file
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = FileViewerFrameProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = CBWNDEXTRAFRAME;
    wc.hInstance     = m_hInst;
    wc.hIcon         = LoadIcon(m_hInst, "Icon");
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1);
    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU);
    wc.lpszClassName = String(IDS_CLASSFRAME);

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

    // MODIFY:  This is the viewport window that does the work
    // of displaying the data.  Modify it to your needs.
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = ViewportWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = CBWNDEXTRAVIEWPORT;
    wc.hInstance     = m_hInst;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU);
    wc.lpszClassName = String(IDS_CLASSVIEWPORT);

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

    m_fClassReg=TRUE;

    // Create a default fixed pitch font for the Viewport.  We
    // use Courier New here instead of say, SYSTEM_FIXED_FONT
    // because we want the font sizing buttons to work normally
     // when this window appears, and the system fonts do not
     // scale, but Courier New does.  Default is 10 point calculated
     // from 10*PIXELSPERINCH/72.
    hDC=GetDC(NULL);
    m_cyPPI=GetDeviceCaps(hDC, LOGPIXELSY);
    ReleaseDC(NULL, hDC);

    m_hFont=CreateFont(MulDiv(-10, m_cyPPI, 72)
, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE
, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, "Courier New");

    return NOERROR;
    }
//
//   FUNCTION: CFileViewer::FInitFrameControls
//
//   PURPOSE:  Creates and initializes the toolbar and status bar for the frame window.
//
//   PARAMETERS:
//
//   RETURN VALUE:
//  BOOL            TRUE if the function worked, FALSE otherwise.
//
BOOL CFileViewer::FInitFrameControls(void)
    {
    BOOL        fRet;
    RECT        rc;
    TBBUTTON    rgtb[CTBBUTTONS];

     // Initialize the toolbar button array.  This uses the TOOLS.BMP
     // resource which has three buttons and one separator.  The
     // buttons are tied to File/Open As and Increase/Decrease Font
     // size.
    rgtb[0].iBitmap=IDBT_OPENAS;
    rgtb[0].idCommand=IDM_FILEOPENAS;
    rgtb[0].fsState=TBSTATE_ENABLED;
    rgtb[0].fsStyle=TBSTYLE_BUTTON;
    rgtb[0].dwData=0L;
    rgtb[0].iString=0;

    rgtb[1].iBitmap=0;
    rgtb[1].idCommand=0;
    rgtb[1].fsState=TBSTATE_ENABLED;
    rgtb[1].fsStyle=TBSTYLE_SEP;
    rgtb[1].dwData=0L;
    rgtb[1].iString=0;

    rgtb[2].iBitmap=IDBT_FONTSIZEINCREASE;
    rgtb[2].idCommand=IDM_VIEWFONTINCREASE;
    rgtb[2].fsState=TBSTATE_ENABLED;
    rgtb[2].fsStyle=TBSTYLE_BUTTON;
    rgtb[2].dwData=0L;
    rgtb[2].iString=0;

    rgtb[3].iBitmap=IDBT_FONTSIZEDECREASE;
    rgtb[3].idCommand=IDM_VIEWFONTDECREASE;
    rgtb[3].fsState=TBSTATE_ENABLED;
    rgtb[3].fsStyle=TBSTYLE_BUTTON;
    rgtb[3].dwData=0L;
    rgtb[3].iString=0;

     // BUGBUG:  Open as button:  call SHGetIconOfFile to retrieve
     // the image, then overwrite it in the toolbar bitmap we send
     // to CreateToolbarEx.  If SHGetIconOfFile fails, then just
     // use the one in the toolbar bitmap already.
     //
    m_hWndToolbar=CreateToolbarEx(m_hWnd, TBSTYLE_TOOLTIPS
| WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, ID_TOOLBAR
, CTBBITMAPS, m_hInst, IDB_TOOLS, rgtb, CTBBUTTONS
, 0, 0, 0, 0, sizeof(TBBUTTON));

    if (NULL==m_hWndToolbar)
{
ODS("CFileViewer::FInitFrameTools failed to create toolbar");
return FALSE;
}

     // Remember the height for resizing this and the viewport.
     // Use window rectangles for toolbars.
    GetWindowRect(m_hWndToolbar, &rc);
    m_cyTools=rc.bottom-rc.top;

     // Status line
    m_hWndStatus=CreateStatusWindow(WS_CHILD | WS_VISIBLE
, String(IDS_MSGREADY), m_hWnd, ID_STATUSBAR);

    if (NULL==m_hWndStatus)
{
ODS("CFileViewer::FInitFrameTools failed to create status bar");
return FALSE;
}

     // Remember the height for resizing this and the viewport.
     // Use client rectangle for status lines.
    GetClientRect(m_hWndStatus, &rc);
    m_cyStatus=rc.bottom-rc.top;

     // Create a status bar helper and tell it what messages
     // to use.  This object will be called from WM_MENUSELECT
     // in FileViewerFrameProc in FVPROC.CPP.
    m_pSH=new CStatusHelper(m_hWndStatus, m_hInst);

    if (NULL==m_pSH)
return FALSE;

    fRet=m_pSH->MessageMap(m_hWnd, IDR_STATMESSAGEMAP, IDS_STATUSMSGMIN
, IDS_STATUSMSGMAX, CCHSTATUSMSGMAX, ID_MENUFILE, ID_MENUHELP
, ID_MSGREADY, ID_MSGEMPTY, ID_MENUSYS);

    return fRet;
    }

//
//   FUNCTION:  CFileViewer::String
//
//   PURPOSE:    string lookup function for access to stringtable.
//
LPSTR CFileViewer::String(UINT uID)
    {
    return (*m_pST)[uID];
    }

//
//   FUNCTION:  CFileViewer::MemAlloc
//
//   PURPOSE:Central allocation function using IMalloc.
//
LPVOID CFileViewer::MemAlloc(ULONG cb)
    {
    LPMALLOC    pIMalloc;
    LPVOID      pv;
    HRESULT     hr;

    if (FAILED(hr = CoGetMalloc(MEMCTX_TASK, &pIMalloc)))
{
ODSu("CFileViewer::MemAlloc CoGetMalloc failed %ld", hr);

if (hr == CO_E_NOTINITIALIZED)
    {
    if ((FAILED(hr=CoInitialize(NULL))) ||
FAILED(hr=CoGetMalloc(MEMCTX_TASK, &pIMalloc)))
{
ODSu("CFileViewer::MemAlloc CoInitialize/CoGetMalloc failed %x", hr);
return NULL;
}

    }
else
    return NULL;
}

    pv=pIMalloc->Alloc(cb);
    pIMalloc->Release();
    return pv;
    }
//
//   FUNCTION:CFileViewer::MemFree
//
//   PURPOSE:  Central free function using IMalloc.
//
void CFileViewer::MemFree(LPVOID pv)
    {
    LPMALLOC    pIMalloc;

    if (NULL==pv)
{
ODS("CFileViewer::MemFree passed NULL pointer");
return;
}

    if (FAILED(CoGetMalloc(MEMCTX_SHARED, &pIMalloc)))
{
ODS("CFileViewer::MemFree CoGetMalloc failed");
return;
}

    pIMalloc->Free(pv);
    pIMalloc->Release();
    return;
    }
//
//   FUNCTION: CFileViewer::QueryInterface
//
STDMETHODIMP CFileViewer::QueryInterface(REFIID riid, PPVOID ppv)
    {
    *ppv=NULL;

     //The only calls for IUnknown are either in a nonaggregated
     // case or when created in an aggregation, so in either case
     // always return our IUnknown for IID_IUnknown.
    if (IsEqualIID(riid, IID_IUnknown))
*ppv=(LPVOID)this;

    //IPersist is base of IPersistFile
    if (IsEqualIID(riid, IID_IPersist)
|| IsEqualIID(riid, IID_IPersistFile))
*ppv=(LPVOID)m_pIPersistFile;

    if (IsEqualIID(riid, IID_IFileViewer))
*ppv=(LPVOID)m_pIFileViewer;

    //AddRef any interface we'll return.
    if (NULL!=*ppv)
{
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}

    return ResultFromScode(E_NOINTERFACE);
    }

//
//   FUNCTION:  CFileViewer::AddRef
//
STDMETHODIMP_(ULONG) CFileViewer::AddRef(void)
    {
    return ++m_cRef;
    }

//
//   FUNCTION: CFileViewer::Release
//
STDMETHODIMP_(ULONG) CFileViewer::Release(void)
    {
    ULONG       cRefT;

    cRefT=--m_cRef;

    ODSu("CFileViewer::Release Count=%d", cRefT);

    if (0L==m_cRef)
{
 // Tell the server that an object is going away
 // so it can shut down if appropriate.  (See FILEVIEW.CPP
 // for the ObjectDestroyed function).
if (NULL!=m_pfnDestroy)
    (*m_pfnDestroy)();

delete this;
}

    return cRefT;
    }


FVPROC.CPP

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM:FVPROC.CPP
//
//PURPOSE:  Window procedures for main window and About box of the
// sample FileViewer for text files.
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS: N/A
//
//Always include the master FileViewer source include file here.
#include "fileview.h"

//
//   FUNCTION:  FileViewerFrameProc
//
//   PURPOSE:  Standard window procedure for the text file viewer frame window.
//  Processes menu commands, acclerators, and handles resizing of
//  the window
//
long WINAPI FileViewerFrameProc(HWND hWnd, UINT iMsg, WPARAM wParam
    , LPARAM lParam)
    {
    LONG            lRet;
    PCFileViewer    pObj;
    LPTOOLTIPTEXT   pTTT;

    //This is invalid until WM_NCCREATE is called.
    pObj=(PCFileViewer)GetWindowLong(hWnd, FVWL_OBJECTPOINTER);

    switch (iMsg)
        {
        case WM_NCCREATE:
            //Save the the CFileViewer object pointer we're passed
            lRet=(LONG)((LPCREATESTRUCT)lParam)->lpCreateParams;
            SetWindowLong(hWnd, FVWL_OBJECTPOINTER, lRet);
            return DefWindowProc(hWnd, iMsg, wParam, lParam);

        case WM_COMMAND:
            pObj->OnCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
            break;

        case WM_NOTIFY:
             // The toolbar, created with TBSTYLE_TOOLTIPS, will
             // send notifications for each button.  lParam will
             // be an LPTOOLTIPTEXT in such a case, but even when
             // the structure is something different it will always
             // have a hdr field of type NMHDR as the first field
             // which we use to see if it comes from the toolbar.
             // The notification we want is TTN_NEEDTEXT.
            pTTT=(LPTOOLTIPTEXT)lParam;

            if (NULL==pTTT)
                return 0L;

            if (TTN_NEEDTEXT==pTTT->hdr.code)
                pTTT->lpszText=pObj->PszToolTip(pTTT->hdr.idFrom);

            return 0L;

        case WM_CLOSE:
            DestroyWindow(hWnd);
            break;

    case WM_DESTROY:
            if (NULL != pObj)
            {
                ODSu("FileViewerFrameProc Quit? ", pObj->m_fPostQuitMsg);
                if (pObj->m_fPostQuitMsg)
                    PostQuitMessage(0);
                pObj->m_fPostQuitMsg = TRUE;    // One shot that it did not...

                if (pObj->m_hWnd == hWnd)
                    pObj->m_hWnd = NULL;        // Don't try to destory this again...
            }
            break;

        case WM_SIZE:
            //Resize frame tools and viewport
            pObj->ChildrenResize();
            break;

        case WM_MENUSELECT:
            //Win32 Parameters are wItem, wFlags, and hMenu
            pObj->m_pSH->MenuSelect(LOWORD(wParam)
                , HIWORD(wParam), (HMENU)lParam);
            break;

        case WM_DROPFILES:
            // We have a new file dropped on us so we need to pass this
            // information back to the caller of the viewer...
            pObj->DropFiles((HDROP)wParam);
            break;

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

    return 0L;
    }
//
//   FUNCTION:  ViewportWndProc
//
//   PURPOSE: Window procedure for the viewport window.  The viewport in
//  this example just does draws the text into itself using an
//  edit control.
//
//   COMMENTS:
//   This will need to be modified for your file viewer.
//
long WINAPI ViewportWndProc(HWND hWnd, UINT iMsg, WPARAM wParam
    , LPARAM lParam)
    {
    LONG            lRet;
    PCFileViewer    pObj;
    PAINTSTRUCT     ps;

    //This is invalid until WM_NCCREATE is called.
    pObj=(PCFileViewer)GetWindowLong(hWnd, VPWL_OBJECTPOINTER);

    switch (iMsg)
        {
        case WM_NCCREATE:
            //Save the the CFileViewer object pointer we're passed
            lRet=(LONG)((LPCREATESTRUCT)lParam)->lpCreateParams;
            SetWindowLong(hWnd, VPWL_OBJECTPOINTER, lRet);
            return DefWindowProc(hWnd, iMsg, wParam, lParam);

        case WM_PAINT:
            BeginPaint(hWnd, &ps);

            if (NULL!=pObj->m_hMemText)
                {
                LPSTR       psz;
                RECT        rc;
                HFONT       hFont;

                GetClientRect(hWnd, &rc);

                SetTextColor(ps.hdc, GetSysColor(COLOR_WINDOWTEXT));
                SetBkColor(ps.hdc, GetSysColor(COLOR_WINDOW));
                hFont=(HFONT)SelectObject(ps.hdc, pObj->m_hFont);

                psz=(LPSTR)GlobalLock(pObj->m_hMemText);

                 // We can use the client area rectangle here for
                 // drawing since we also use DT_NOCLIP, so we'll
                 // always draw all over the client area.
                OffsetRect(&rc, -pObj->m_xPos, -pObj->m_yPos);
                DrawText(ps.hdc, psz, -1, &rc, DT_LEFT
                    | DT_NOCLIP| DT_EXPANDTABS);

                GlobalUnlock(pObj->m_hMemText);
                SelectObject(ps.hdc, hFont);
                }
            EndPaint(hWnd, &ps);
            break;

        case WM_MOUSEMOVE:
             // If this message is already displayed, CStatusHelper
             // will just ignore this call and return very fast.
            pObj->m_pSH->MessageDisplay(ID_MSGCHOOSEOPEN);
            break;

        case WM_RBUTTONDOWN:
            {
            HMENU           hMenu, hMenuRes;
            POINT           pt;
            UINT            i, cItems;

            //Load our context menu.
            hMenuRes=LoadMenu(pObj->m_hInst
                , MAKEINTRESOURCE(IDR_MENUVIEWPORT));

            if (NULL==hMenuRes)
                break;

             // Make a copy popup menu because you cannot
             // use a resource-loaded menu with TrackPopupMenu.
            cItems=GetMenuItemCount(hMenuRes);
            hMenu=CreatePopupMenu();

            for (i=0; i < cItems; i++)
                {
                char    szItem[80];
                int     id, uFlags;

                GetMenuString(hMenuRes, i, szItem, sizeof(szItem)
                    , MF_BYPOSITION);
                id=GetMenuItemID(hMenuRes, i);

                uFlags=(0==id) ? MF_SEPARATOR : MF_STRING | MF_ENABLED;
                AppendMenu(hMenu, uFlags, id, szItem);
                }

            DestroyMenu(hMenuRes);

            pt.x=LOWORD(lParam);
            pt.y=HIWORD(lParam);
            ClientToScreen(hWnd, &pt);

            //Messages sent to frame window from this menu.
            TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON
                , pt.x, pt.y, 0, pObj->m_hWnd, NULL);

            DestroyMenu(hMenu);
            }
            break;

        case WM_HSCROLL:
        case WM_VSCROLL:
            {
            int             iTmp;
            UINT            idScroll;
            SCROLLINFO      si;

            idScroll=(WM_HSCROLL==iMsg) ? SB_HORZ : SB_VERT;

            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
            GetScrollInfo(hWnd, idScroll, &si);

            iTmp=si.nPos;

            switch (LOWORD(wParam))
                {
                case SB_LINEUP:     si.nPos -= 20;  break;
                case SB_PAGEUP:     si.nPos -=si.nPage;  break;
                case SB_LINEDOWN:   si.nPos += 20;  break;
                case SB_PAGEDOWN:   si.nPos +=si.nPage;  break;

                case SB_THUMBPOSITION:
                case SB_THUMBTRACK:
                    si.nPos=HIWORD(wParam);
                    break;
                }

            si.nPos=max(si.nMin, min(si.nPos, si.nMax));

            if (si.nPos!=iTmp)
                {
                //Set the new position and scroll the window
                SetScrollPos(hWnd, idScroll, si.nPos, TRUE);

                if (SB_HORZ==idScroll)
                    {
                    pObj->m_xPos=si.nPos;
                    ScrollWindow(hWnd, iTmp-si.nPos, 0, NULL, NULL);
                    }
                else
                    {
                    pObj->m_yPos=si.nPos;
                    ScrollWindow(hWnd, 0, iTmp-si.nPos, NULL, NULL);
                    }

                UpdateWindow(hWnd);
                }
            }
            break;

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

    return 0L;
    }
//
//   FUNCTION: AboutProc
//
//   PURPOSE:  Dialog procedure for the omnipresent About box.
//
BOOL APIENTRY AboutProc(HWND hDlg, UINT iMsg, WPARAM wParam
    , LPARAM lParam)
    {
    switch (iMsg)
        {
        case WM_INITDIALOG:
            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam))
                {
                case IDOK:
                    EndDialog(hDlg, TRUE);
                    break;
                }
            break;

        case WM_CLOSE:
            EndDialog(hDlg, FALSE);
            break;
        }
    return FALSE;
    }


FVTEXT.CPP

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//    PROGRAM: FVTEXT.CPP
//
//    PURPOSE:  Sample FileViewer for text files to integrate with the Explorer.  This sample serves as a
//    framework for custom viewers.  Initialization and other low-use functions are in FVMISC.CPP.
//
//    PLATFORMS:    Windows 95
//
//    FUNCTIONS:
//
//
//    SPECIAL INSTRUCTIONS: N/A
//

#include "fileview.h"

//
//   FUNCTION: CFileViewer::CFileViewer
//
//   PURPOSE: Constructor for the CFileViewer class.  Initializes all variables.
//
//   PARAMETERS:
//    pUnkOuter - LPUNKNOWN of a controlling unknown.
//    hInst - HINSTANCE of the module we're in
//    pfnDestroy - LPFNDESTROYED to call when an object is destroyed.
//
CFileViewer::CFileViewer(LPUNKNOWN pUnkOuter, HINSTANCE hInst, PFNDESTROYED pfnDestroy)
{
    m_cRef=0;
    m_pUnkOuter=pUnkOuter;
    m_hInst=hInst;
    m_pfnDestroy=pfnDestroy;
    m_lpfsi = NULL;

    //MODIFY:  Change this CLSID to your own
    m_clsID=CLSID_FileViewerText;

    m_pszPath=NULL;
    m_grfMode=0L;
    m_fLoadCalled=FALSE;
    m_fShowInit=FALSE;
    m_fPostQuitMsg=TRUE;

    //NULL any contained interfaces initially.
    m_pIPersistFile=NULL;
    m_pIFileViewer=NULL;
    m_pvsi = NULL;

    m_pST=NULL;
    m_pSH=NULL;
    m_fClassReg=FALSE;

    m_hWnd=NULL;
    m_hWndOld = NULL;
    m_hWndToolbar=NULL;
    m_hWndStatus=NULL;
    m_hWndViewport=NULL;

    //MODIFY:  Initalize viewer-specific values
    m_hMemText=NULL;
    m_hFont=NULL;
    m_xPos=0;
    m_yPos=0;
    return;
}

//
//   FUNCTION:  CFileViewer::~CFileViewer
//
//   PURPOSE:   Destructor:deletes all the windows, all allocations, cleans up objects, and
//   frees the filename copied in IPersistFile::Load.
//
CFileViewer::~CFileViewer(void)
{
    //MODIFY:  m_hMemText and m_hFont are viewer-specific.
    if (NULL!=m_hMemText)
        GlobalFree(m_hMemText);

    if (NULL!=m_hFont)
        DeleteObject(m_hFont);

    //MODIFY:  Do any other viewer-specific cleanup
    //Destroying the parent destroys the children as well
    if (NULL!=m_hWnd)
    {
        m_fPostQuitMsg = FALSE;    // Destroy from here implies not from our loop
        DestroyWindow(m_hWnd);
    }

     // Unregistering the classes is important for DLL's because we
     // should not assume that whoever loaded us (the task) is going
     // to quit anytime soon and unregister the class.  Normally
     // QVStub is going to quit, but a test app like TestFV does not
     // which can cause some development headaches.
    if (m_fClassReg)
        {
        UnregisterClass(String(IDS_CLASSFRAME),    m_hInst);
        UnregisterClass(String(IDS_CLASSVIEWPORT), m_hInst);
        }

    if (NULL!=m_pSH)
        delete m_pSH;

    if (NULL!=m_pST)
        delete m_pST;

    //Free the pathname string from IPersistFile::Load if we got one
    MemFree(m_pszPath);

    //Free contained interfaces.
    if (NULL!=m_pIPersistFile)
        delete m_pIPersistFile;

    if (NULL!=m_pIFileViewer)
        delete m_pIFileViewer;
    if (NULL!=m_lpfsi)
        m_lpfsi->Release();

    return;
}

//
//   FUNCTION: CFileViewer::FileShowInit
//
//   PURPOSE:Provides the implementation of IFileViewer::ShowInitialize that performs
//   anything subject to failure. This function creates the main window, initializes the toolbar,
//   initializes the status bar controls, sets the initial menu state, creates the viewport window, and
//   loads the accelerators.
//
//   RETURN VALUE:
//
//   HRESULT         NOERROR or an appropriate error code.
//
STDMETHODIMP CFileViewer::FileShowInit(LPFILEVIEWERSITE lpfsi)
{
    HRESULT         hr;
    HMENU           hMenu;

    //MODIFY:  Do pre-show initialization here.
    if (m_lpfsi != lpfsi)
    {
        if (NULL!=m_lpfsi)
            m_lpfsi->Release();

        m_lpfsi = lpfsi;
        lpfsi->AddRef();
    }

    //Default error code
    hr=ResultFromScode(E_OUTOFMEMORY);

    //Create the main window passing "this" to it
    m_hWndOld = m_hWnd;
    m_hWnd=CreateWindow(String(IDS_CLASSFRAME)
        , String(IDS_CAPTION), WS_MINIMIZEBOX | WS_OVERLAPPEDWINDOW
        | WS_CLIPCHILDREN, CW_USEDEFAULT , CW_USEDEFAULT, 350, 450
        , NULL, NULL, m_hInst, (LPVOID)this);

    if (NULL==m_hWnd)
        {
        ODS("CFileViewer::FileShow failed to create main window.");
        return hr;
        }

    // Let us accept files.
    DragAcceptFiles(m_hWnd, TRUE);

    if (!FInitFrameControls())
        {
        ODS("CFileViewer::FileShow failed to create frame tools.");
        return hr;
        }

    //Set initial view menu item checks
    hMenu=GetMenu(m_hWnd);
    CheckMenuItem(hMenu, IDM_VIEWTOOLBAR, MF_BYCOMMAND | MF_CHECKED);
    CheckMenuItem(hMenu, IDM_VIEWSTATUSBAR, MF_BYCOMMAND | MF_CHECKED);

    m_fToolsVisible=TRUE;
    m_fStatusVisible=TRUE;

    m_pSH->MessageDisplay(ID_MSGREADY);

     // ViewportResize puts the viewport window created here
     // in the right location, so we don't have to worry
     // about initial position.
    //BUGBUG:  Not getting proportional thumbs here.

    m_hWndViewport=CreateWindowEx(WS_EX_CLIENTEDGE
        , String(IDS_CLASSVIEWPORT), "Viewport"
        , WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL
        , 0, 0, 100, 100, m_hWnd, (HMENU)ID_VIEWPORT
        , m_hInst, (LPVOID)this);

    if (NULL==m_hWndViewport)
        {
        ODS("CFileViewer::FileShow failed to create viewport.");
        return hr;
        }

    ViewportResize();

     // Go load the file.  This means pulling into memory whatever
     // is necessary for the initial display.
    hr=FileLoad();

    if (FAILED(hr))
        {
        ODS("CFileViewer::FileShow failed to load file.");
        return hr;
        }

    m_hAccel=LoadAccelerators(m_hInst
        , MAKEINTRESOURCE(IDR_ACCELERATORS));

    //Tell IFileViewer::Show it's OK to call it
    m_fShowInit=TRUE;
    return NOERROR;
}

//
//   FUNCTION:      CFileViewer::FileShow
//
//   PURPOSE: Displays the viewing window and enters a message loop.  This function must not fail.
//
//   PARAMETERS:
//   pvsi - LPFVSHOWINFO indicates how to initially show the FileViewer window.
//
//   RETURN VALUE:
//    HRESULT  - NOERROR always
//
STDMETHODIMP CFileViewer::FileShow(LPFVSHOWINFO pvsi)
{
    MSG             msg;

    // We need to handle the case where the ShowInitialize may have
    // failed and we set the hwnd to NULL and the hwndOld is not NULL.
    // and the FVSIF_NEWFAILED is set.  In this case set the hwnd Back
    // to the old hwnd...
    if ((pvsi->dwFlags & FVSIF_NEWFAILED) && (m_hWnd == NULL))
    {
        m_hWnd = m_hWndOld;
    }

    if (!IsWindow (m_hWnd))
            return ResultFromScode(E_UNEXPECTED);

    m_pvsi = pvsi;

    // If the new failed flag was passed to us we know that we got here
    // because we tried to view a file and it failed, so simply go back
    // to message loop...
    if ((pvsi->dwFlags & FVSIF_NEWFAILED) == 0)
    {
        if (pvsi->dwFlags & FVSIF_RECT)
            SetWindowPos(m_hWnd, NULL, pvsi->rect.left, pvsi->rect.top,
                    pvsi->rect.right - pvsi->rect.left, pvsi->rect.bottom - pvsi->rect.top,
                    SWP_NOZORDER | SWP_NOACTIVATE);
        ShowWindow(m_hWnd, pvsi->iShow);

        if (SW_HIDE!=pvsi->iShow)
        {
            SetForegroundWindow(m_hWnd);
            UpdateWindow(m_hWnd);
        }

        // If there is an Old window destroy it now
        // It would be nicer to reuse the window!
        if (pvsi->dwFlags & FVSIF_PINNED)
        {
            m_lpfsi->SetPinnedWindow(NULL);
            m_lpfsi->SetPinnedWindow(m_hWnd);

            HMENU hMenu=GetMenu(m_hWnd);
            CheckMenuItem(hMenu, IDM_VIEWREPLACE, MF_BYCOMMAND|MF_CHECKED);
        }

        if (SW_HIDE!=pvsi->iShow)
            UpdateWindow(m_hWnd);

        if ((NULL!=m_hWndOld) && IsWindow(m_hWndOld))
        {
            ODS("CFileViewer::FileShow Destroy Previous hwnd");
            m_fPostQuitMsg = FALSE; // Don't destroy the queue for this one.
            DestroyWindow(m_hWndOld);
            m_hWndOld = NULL;
        }

        if (NULL!=pvsi->punkRel)
        {
            ODSlu("CFileViewer::FileShow Release of previous viewers punkRel(%x)", pvsi->punkRel);
            pvsi->punkRel->Release();
            pvsi->punkRel = NULL;
        }
    }

    while (GetMessage(&msg, NULL, 0,0 ))
        {
        if (!TranslateAccelerator(m_hWnd, m_hAccel, &msg))
            {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            }
        // If there is a new file bail out now.
        if (m_pvsi->dwFlags & FVSIF_NEWFILE)
            break;
        }

    //MODIFY:  Perform cleanup here.
    return NOERROR;
}

//
//   FUNCTION:     CFileViewer::PrintTo
//
//   PURPOSE:  Provides the implementation of IFileViewer::PrintTo that
//  prints the file to a given printer driver, suppressing UI
//  if necessary.  This function currently does nothing.
//
//   PARAMETERS:
//    pszDriver - LPSTR with the path of the driver to use.  If NULL, use the default driver.
//    fSuppressUI - BOOL indicating if this function is to show any  UI or not.
//
//   RETURN VALUE:
//   HRESULT - NOERROR on success, error code otherwise.
//
STDMETHODIMP CFileViewer::PrintTo(LPSTR pszDriver, BOOL fSuppressUI)
    {
    //Printing not implemented in this sample
    //MODIFY:  Add your printing code here.
    return ResultFromScode(E_NOTIMPL);
}

//
//   FUNCTION:      CFileViewer::FileLoad
//
//   PURPOSE: Loads the file with the path in m_pszPath and places the
//   contents into the m_hWndViewport window.  This function can load
//   a compound file with a "Text" stream or text from a .TXT file.
//
HRESULT CFileViewer::FileLoad(void)
{
    HRESULT     hr;
    HGLOBAL     hMem=NULL;
    LPSTR       psz;
    OLECHAR     szwFile[512];

    //MODIFY: Load your own file type here.
    if (NULL==m_pszPath)
        return ResultFromScode(E_UNEXPECTED);

     // This FileViewer is registered for a .TXT extension that
     // is either in a compound file (single stream called "Text"
     // or in a flat text file.  This sample is meant to show
     // how to open and work with both types of files.
    //Make Unicode copy of filename
    mbstowcs(szwFile, m_pszPath, sizeof(szwFile));

     // CAREFUL:  StgIsStorageFile returns S_FALSE if not--
     //           don't use SUCCEEDED to test the return value!
    if (NOERROR==StgIsStorageFile(szwFile))
        {
        LPSTORAGE   pIStorage=NULL;
        LPSTREAM    pIStream=NULL;
        STATSTG     stat;

        //Case for a compound file, open it and the text stream
        hr=StgOpenStorage(szwFile, NULL, m_grfMode, NULL, 0
            , &pIStorage);

        if (FAILED(hr))
            {
            ODS("CFileViewer::FileLoad failed StgOpenStorage");
            return hr;
            }

        mbstowcs(szwFile, "Text", sizeof(szwFile));

        hr=pIStorage->OpenStream(szwFile, 0, STGM_DIRECT | STGM_READ
            | STGM_SHARE_EXCLUSIVE, 0, &pIStream);

        if (SUCCEEDED(hr))
            {
            //Determine the amount of text and allocate memory
            hr=pIStream->Stat(&stat, STATFLAG_NONAME);

            if (SUCCEEDED(hr))
                {
                hMem=GlobalAlloc(GHND, stat.cbSize.LowPart+1);

                if (NULL!=hMem)
                    {
                     // Now load the text into the controls.
                     // GlobalLock will always work because we
                     // allocated at least 1 byte.
                    psz=(LPSTR)GlobalLock(hMem);
                    hr=pIStream->Read((LPVOID)psz
                        , stat.cbSize.LowPart, NULL);
                    GlobalUnlock(hMem);
                    }
                else
                    {
                    ODS("CFileViewer::FileLoad failed to allocate memory");
                    hr=ResultFromScode(E_OUTOFMEMORY);
                    }
                }

            pIStream->Release();
            }
        else
            {
            ODS("CFileViewer::FileLoad failed StgOpenStorage");
            }

        pIStorage->Release();
        }
    else
        {
        int         hFile=NULL;
        OFSTRUCT    of;
        DWORD       cb, cbRead;

         // Case for straight text file, open and read.  In opening
         // the file, mask off the upper word or m_grfMode which
         // contains the compound file specific flags.  The lower
         // word has OpenFile flags (consistent between
         // StgOpenStorage and OpenFile).
        hFile=OpenFile(m_pszPath, &of
            , (UINT)(m_grfMode & 0x0000FFFF));

        if (HFILE_ERROR==hFile)
            {
            ODS("CFileViewer::FileLoad failed OpenFile");
            return ResultFromScode(STG_E_FILENOTFOUND);
            }

        //Scan to end and then back to determine file size.
        cb=_llseek(hFile, 0L, 2);
        _llseek(hFile, 0L, 0);

        hMem=GlobalAlloc(GHND, cb+1);

        if (NULL!=hMem)
            {
             // Now load the text into the controls.  GlobalLock will
             // always work because we allocated at least 1 byte
            psz=(LPSTR)GlobalLock(hMem);
            cbRead=_lread(hFile, psz, (UINT)cb);
            GlobalUnlock(hMem);

            hr=ResultFromScode((cb==cbRead) ? S_OK : STG_E_READFAULT);
            }
        else
            {
            ODS("CFileViewer::FileLoad failed to allocate memory");
            hr=ResultFromScode(E_OUTOFMEMORY);
            }

        _lclose(hFile);
        }

    //Set edit control to read-only:  no modifications allowed!
    if (SUCCEEDED(hr) && NULL!=hMem)
        {
        if (NULL!=m_hMemText)
            GlobalFree(m_hMemText);

        //Make the text current and repaint.
        m_hMemText=hMem;
        InvalidateRect(m_hWndViewport, NULL, TRUE);
        UpdateWindow(m_hWndViewport);
        }

    return hr;
}

//
//   FUNCTION:     CFileViewer::OnCommand
//
//   PURPOSE: WM_COMMAND message handler for a FileViewer window.
//
//   PARAMETERS:
//       wID             WORD ID of the command.
//  wCode           WORD notification code with the command
//  hWndCtl         HWND sending the message (if a control)
//
void CFileViewer::OnCommand(WORD wID, WORD wCode, HWND hWndCtl)
{
    //MODIFY:  Do your command processing here.
    switch (wID)
        {
        case IDM_FILEOPENAS:
            if (FOpenAs())
                PostMessage(m_hWnd, WM_CLOSE, 0, 0L);
            break;

        case IDM_FILEPAGESETUP:
            //Not implemented in this sample
            break;

        case IDM_FILEPRINT:
            //Not implemented in this sample
            break;

        case IDM_FILEEXIT:
            PostMessage(m_hWnd, WM_CLOSE, 0, 0L);
            break;

        case IDM_VIEWTOOLBAR:
            m_fToolsVisible=!m_fToolsVisible;

            CheckMenuItem(GetMenu(m_hWnd), IDM_VIEWTOOLBAR
                , MF_BYCOMMAND | (m_fToolsVisible
                ? MF_CHECKED : MF_UNCHECKED));

            ShowWindow(m_hWndToolbar, m_fToolsVisible
                ? SW_SHOW : SW_HIDE);

            //Resize the viewport window
            ViewportResize();
            break;

        case IDM_VIEWSTATUSBAR:
            m_fStatusVisible=!m_fStatusVisible;

            CheckMenuItem(GetMenu(m_hWnd), IDM_VIEWSTATUSBAR
                , MF_BYCOMMAND | (m_fStatusVisible
                ? MF_CHECKED : MF_UNCHECKED));

            ShowWindow(m_hWndStatus, m_fStatusVisible
                ? SW_SHOW : SW_HIDE);

            //Resize the viewport window
            ViewportResize();
            break;
        case IDM_VIEWREPLACE:
            // Switch the Replace mode
            ReplaceWindowModeChange();
            break;
        case IDM_VIEWFONT:
            FontChange(VIEWFONT_SELECT);
            break;

        case IDM_VIEWFONTINCREASE:
            FontChange(VIEWFONT_INCREASESIZE);
            break;

        case IDM_VIEWFONTDECREASE:
            FontChange(VIEWFONT_DECREASESIZE);
            break;

        case IDM_HELPCONTENTS:
            MessageBox(m_hWnd, String(IDS_NOHELP)
                , String(IDS_CAPTION), MB_OK);
            break;

        case IDM_HELPABOUT:
            DialogBox(m_hInst, MAKEINTRESOURCE(IDD_ABOUT), m_hWnd
                , (DLGPROC)AboutProc);
            break;
        }

    return;
}

//
//   FUNCTION:  CFileViewer::ChildrenResize
//
//   PURPOSE:    Handles the situation when the FileViewer frame window was
//   resized. The toolbar, status bar and viewport are resized via a call to the
//   the ViewportResize function.
//
//   COMMENTS:
//   Note that the toolbar and status bar are resized even when
//   they are not visible so we can just reshow them again when
//   necessary.
//
void CFileViewer::ChildrenResize(void)
{
    RECT        rc;

    GetClientRect(m_hWnd, &rc);

    //We resize toolbar and status bar regardless of visibility
    SetWindowPos(m_hWndToolbar, NULL, 0, 0
        , rc.right-rc.left, m_cyTools
        , SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

    SetWindowPos(m_hWndStatus, NULL, 0, rc.bottom-m_cyStatus
        , rc.right-rc.left, m_cyStatus
        , SWP_NOZORDER | SWP_NOACTIVATE);

    ViewportResize();
    return;
}

//
//   FUNCTION: CFileViewer::ViewportResize
//
//   PURPOSE: Updates the size and position of the viewport window
//   depending on visibility of the toolbar and status bar.
//
void CFileViewer::ViewportResize(void)
{
    RECT        rc;
    UINT        dy1, dy2;

    GetClientRect(m_hWnd, &rc);

    dy1=m_fToolsVisible  ? m_cyTools  : 0;
    dy2=m_fStatusVisible ? m_cyStatus : 0;

    //We resize toolbar and status bar regardless of visibility
    SetWindowPos(m_hWndViewport, NULL, 0, dy1
        , rc.right-rc.left, rc.bottom-rc.top-dy1-dy2
        , SWP_NOZORDER | SWP_NOACTIVATE);

    ViewportScrollSet();
    return;
}

//
//   FUNCTION:     CFileViewer::ViewportScrollSet
//
//   PURPOSE:  Updates the scrollbar ranges in the viewport depending on
//   the current font in use and the size of the window.
//
void CFileViewer::ViewportScrollSet(void)
{
    RECT        rc, rcFmt;
    LPSTR       psz;
    HDC         hDC;
    HFONT       hFont;
    SCROLLINFO  si;

    if (NULL==m_hMemText)
        return;

    psz=(LPSTR)GlobalLock(m_hMemText);
    hDC=GetDC(m_hWndViewport);
    hFont=(HFONT)SelectObject(hDC, m_hFont);

     // Set initially large formatting rectangle, and let
     // DrawText walk all over it.
    SetRect(&rcFmt, 0, 0, 32767, 32767);
    DrawText(hDC, psz, -1, &rcFmt, DT_LEFT | DT_CALCRECT
        | DT_EXPANDTABS);

    SelectObject(hDC, hFont);
    ReleaseDC(m_hWndViewport, hDC);

    GetClientRect(m_hWndViewport, &rc);

    //Scroll ranges are draw rect minus visible rect, +1 for buffer
    // First set the horizontal scrollbars information...
    si.cbSize = sizeof(si);
    si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
    si.nMin = 0;
    si.nMax = (rcFmt.right-rcFmt.left)+1;
    si.nPage = rc.right;
    si.nPos = 0;
    SetScrollInfo(m_hWndViewport, SB_HORZ, &si, TRUE);
    m_xPos=0;

    // Now Set the vertical scrollbar
    si.nMax = (rcFmt.bottom-rcFmt.top)+1;
    si.nPage = rc.bottom;
    SetScrollInfo(m_hWndViewport, SB_VERT, &si, TRUE);
    m_yPos=0;

    GlobalUnlock(m_hMemText);
    return;
}

//
//   FUNCTION: CFileViewer::FOpenAs
//
//   PURPOSE:  Attempts to launch an application to open the file for editing.
//
//   RETURN VALUE:
//   BOOL -  TRUE if the application is opened in which case
//                  the FileViewer can shut down.  Otherwise FALSE
//                  meaning launching failed.
//
BOOL CFileViewer::FOpenAs(void)
{
    SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "open",
            m_pszPath, NULL, NULL, SW_SHOWNORMAL, m_hInst};

    return ShellExecuteEx(&sei);
}

//
//   FUNCTION:     CFileViewer::FontChange
//
//   PURPOSE:  Either allows the user to choose a font or increments or
//  decrements the font size depending on uOpt.  This is all
//  handled in one function here because each operation
//  involves obtaining the current viewport font, messing with
//  it in some way, and setting a new font again.
//
//   PARAMETERS:
//     uOpt          VIEWFONTOPTION of the way to change the font:
//                      VIEWFONT_SELECT     Display dialog
//                      VIEWFONT_INCREASE   Increase size by 2pt
//                      VIEWFONT_DECREASE   Decreate size by 2pt
//
void CFileViewer::FontChange(VIEWFONTOPTION uOpt)
{
    HFONT       hFont;
    LOGFONT     lf;
    CHOOSEFONT  cf;
    BOOL        fChange=TRUE;
    int         z;

     // We have a system font from the constructor, so m_hFont
     // will never be NULL, but assert it anyway.
    D(if (NULL==m_hFont) ODS("Assertion Failed:  m_hFont is NULL"););

    GetObject(m_hFont, sizeof(lf), &lf);
     // Each option is responsible for manipulating
     // the LOGFONT structure in some way.  If there
     // is nothing to do, they set fChange to FALSE
    switch (uOpt)
        {
        case VIEWFONT_SELECT:
            memset(&cf, 0, sizeof(CHOOSECOLOR));
            cf.lStructSize=sizeof(CHOOSECOLOR);
            cf.hwndOwner  =m_hWnd;
            cf.lpLogFont  =&lf;
            cf.nSizeMin   =FONTSIZETHRESHOLDMIN;
            cf.nSizeMax   =FONTSIZETHRESHOLDMAX;

            cf.Flags=CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT
                | CF_FORCEFONTEXIST;

            if (!ChooseFont(&cf))
                {
                D(DWORD dwErr=CommDlgExtendedError();
                ODSlu("FVTEXT.DLL:  ChooseFont failed with %lu", dwErr););

                fChange=FALSE;
                }

            break;

        case VIEWFONT_INCREASESIZE:
            //Convert logical size to point size.
            z=MulDiv(-lf.lfHeight, 72, m_cyPPI);

            //Vary the size increase depending on point size.
            if (z < FONTSIZETHRESHOLDMAX)
                {
                if (z < FONTSIZETHRESHOLDLOW)
                    z+=FONTSIZEDELTASMALL;
                else
                    {
                    if (z < FONTSIZETHRESHOLDMID)
                        z+=FONTSIZEDELTAMEDIUM;
                    else
                        z+=FONTSIZEDELTALARGE;
                    }

                //Convert point size to logical size.
                lf.lfHeight=MulDiv(-z, m_cyPPI, 72);
                }
            else
                fChange=FALSE;

            break;

        case VIEWFONT_DECREASESIZE:
            //Convert logical size to point size.
            z=MulDiv(-lf.lfHeight, 72, m_cyPPI);

            //Vary the size decrease depending on point size.
            if (z > FONTSIZETHRESHOLDMIN)
                {
                if (z > FONTSIZETHRESHOLDMID)
                    z-=FONTSIZEDELTALARGE;
                else
                    {
                    if (z > FONTSIZETHRESHOLDLOW)
                        z-=FONTSIZEDELTAMEDIUM;
                    else
                        z-=FONTSIZEDELTASMALL;
                    }

                //Convert point size to logical size.
                lf.lfHeight=MulDiv(-z, m_cyPPI, 72);
                }
            else
                fChange=FALSE;

            break;

        default:
            fChange=FALSE;
        }

    //Return now if we didn't change anything.
    if (!fChange)
        return;

    //Now switch fonts and update the viewport.
    hFont=CreateFontIndirect(&lf);

    if (NULL!=hFont)
        {
        if (NULL!=m_hFont)
            DeleteObject(m_hFont);

        m_hFont=hFont;

        ViewportScrollSet();

        //Repaint the viewport
        InvalidateRect(m_hWndViewport, NULL, TRUE);
        UpdateWindow(m_hWndViewport);
        }

    return;
}

//
//   FUNCTION:     CFileViewer::ReplaceWindowModeChange
//
//   PURPOSE: Sets the window to be pinned or not to be pinned.  When the
//   window is pinned, the caller of the viewers will attempt
//   to replace the contents of the window instead of creating
//   new windows.
//
void CFileViewer::ReplaceWindowModeChange(void)
{
    HWND hwnd;
    UINT  uCheck = MF_BYCOMMAND;

    if (m_lpfsi)
    {
        m_lpfsi->GetPinnedWindow(&hwnd);
        if ((HWND)NULL==hwnd)
        {
            m_lpfsi->SetPinnedWindow(m_hWnd);
            uCheck = MF_BYCOMMAND | MF_CHECKED;
        }
        else
        {
            if (hwnd==m_hWnd)
                m_lpfsi->SetPinnedWindow(NULL);
        }

    }

    HMENU hMenu=GetMenu(m_hWnd);
    CheckMenuItem(hMenu, IDM_VIEWREPLACE, uCheck);
}

//
//   FUNCTION: CFileViewer::DropFiles
//
//   PURPOSE:   Processes a drop
//
BOOL    CFileViewer::DropFiles (HDROP hdrop)
{
    // We should now proces the files that were dropped on us
    char    szPath[MAX_PATH];
    int     cb;

    // For now only process the first file.
    if (DragQueryFile(hdrop, 0, szPath, sizeof(szPath)) > 0)
    {
        ODSsz("CFileViewer::DropFiles filename=%s", szPath);

        // Need to convert the string to ole string...
        cb = (lstrlen(szPath)+1) * sizeof(OLECHAR);
        mbstowcs(m_pvsi->strNewFile, szPath, cb);

        // Now get the window rectangle to use to display this in...
        GetWindowRect(m_hWnd, &m_pvsi->rect);
        m_pvsi->dwFlags |= (FVSIF_RECT | FVSIF_NEWFILE);

        // Should check for failure but now sure what to do with it anyway
        QueryInterface(IID_IUnknown, (LPVOID *)&m_pvsi->punkRel);
        ODSlu("CFileViewer::DropFiles Query Interface(%x)", m_pvsi->punkRel);
    }

    DragFinish(hdrop);
    return TRUE;
}

//
//   FUNCTION: CFileViewer::PszToolTip
//
//   PURPOSE: Returns a string pointer to a tool tip for the given command
//   ID value.  When asked for a string we also display a similar
//   one in the status bar.
//
//   PARAMETERS:
//   uID - UINT of the toolbar button command.
//
//   RETURN VALUE:
//   LPSTR - Pointer to the string to display.
//
LPSTR CFileViewer::PszToolTip(UINT uID)
{
    UINT        iString, iStatusMsg;

    switch (uID)
        {
        case IDM_FILEOPENAS:
            iString=IDS_TOOLTIPOPENAS;
            iStatusMsg=IDM_FILEOPENAS;
            break;

        case IDM_VIEWFONTINCREASE:
            iString=IDS_TOOLTIPFONTINC;
            iStatusMsg=ID_TIPFONTINC;
            break;

        case IDM_VIEWFONTDECREASE:
            iString=IDS_TOOLTIPFONTDEC;
            iStatusMsg=ID_TIPFONTDEC;
            break;

        default:
            return NULL;
        }

    m_pSH->MessageDisplay(iStatusMsg);
    return String(iString);
}


FVTEXT.H

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM: FVTEXT.H
//
//PURPOSE:   Classes that implement the CFileViewer object for integration
// with the Windows 95 Explorer.  Necessary modifications for a
// custom viewer marked with MODIFY
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS:
//
//DO NOT USE THESE GUIDS:  These are for this sample ONLY
//File CLSID for the text compound file example:  this is nothing official.
//Forward class references

DEFINE_GUID(CLSID_SampleTextFile, 0x00021116, 0, 0, 0xC0,0,0,0,0,0,0,0x46);

//MODIFY:  Define your custom FileViewer CLSID(s) here.
DEFINE_GUID(CLSID_FileViewerText, 0x00021117, 0, 0, 0xC0,0,0,0,0,0,0,0x46);

#ifndef _FVTEXT_H_
#define _FVTEXT_H_

class CImpIPersistFile;
typedef class CImpIPersistFile *PIMPIPERSISTFILE;

class CImpIFileViewer;
typedef class CImpIFileViewer *PIMPIFILEVIEWER;

//FVPROC.CPP
//MODIFY:  Window procedures for frame and viewport windows
long WINAPI FileViewerFrameProc(HWND, UINT, WPARAM, LPARAM);

//Extra bytes for frame
#define CBWNDEXTRAFRAME             sizeof(LPVOID)
#define FVWL_OBJECTPOINTER          0

/*
 * MODIFY:  Change viewport window procedure and defintions
 * to be specific to the file viewer in use.
 */
long WINAPI ViewportWndProc(HWND, UINT, WPARAM, LPARAM);

//Extra bytes for viewport
#define CBWNDEXTRAVIEWPORT          sizeof(LPVOID)
#define VPWL_OBJECTPOINTER          0

BOOL APIENTRY AboutProc(HWND, UINT, WPARAM, LPARAM);

//Child window IDs
#define ID_TOOLBAR                  50
#define ID_STATUSBAR                51
#define ID_VIEWPORT                 52

//Options for CFileViewer::FontChange.
typedef enum
    {
    VIEWFONT_SELECT=0,
    VIEWFONT_INCREASESIZE,
    VIEWFONT_DECREASESIZE
    } VIEWFONTOPTION;

/*
 * Limits to font sizes for the Font dialog.  The increase and
 * decrease buttons change the point size by different amounts
 * (FONTSIZEDELTA*) depending on the size of the current font
 * (where it falls in the FONTSIZETHRESHOLD*).  Note that these
 * values have to be multiplied by logical_pixels_per_inch/72
 * on the display to be accurate.  See CFileViewer::FontChange.
 *
 * Of course, there are better ways to do this that are font
 * specific.  This way works well for stock fonts (Arial, Courier
 * New, Times New Roman).
 */
#define FONTSIZETHRESHOLDMIN    4
#define FONTSIZETHRESHOLDLOW    32
#define FONTSIZETHRESHOLDMID    48
#define FONTSIZETHRESHOLDMAX    120
#define FONTSIZEDELTASMALL      2       //4 to 32pt
#define FONTSIZEDELTAMEDIUM     8       //32 to 48pt
#define FONTSIZEDELTALARGE      24      //48 to 120pt

//FVTEXT.CPP, FVINIT.CPP
/*
 * MODIFY:  Change this CFileViewer object to be more specific to
 * your implementations.  Specific parts are listed below.
 *
 * The CFileViewer object is implemented in its own class with its own
 * IUnknown to support aggregation.  It contains two interface
 * implementation objects (CImpIPersistFile and CImpIFileViewer)
 * to implement the externally exposed interfaces.
 */

class CFileViewer : public IUnknown
    {
    //Make any contained interfaces your friends
    friend class CImpIPersistFile;
    friend class CImpIFileViewer;

    friend long WINAPI FileViewerFrameProc(HWND, UINT, WPARAM, LPARAM);
    friend long WINAPI ViewportWndProc(HWND, UINT, WPARAM, LPARAM);

    protected:
        //NOTE:  These members usually need no modification
        ULONG               m_cRef;             //Object reference count

        LPUNKNOWN           m_pUnkOuter;        //Controlling unknown
        HINSTANCE           m_hInst;            //Module instance
        PFNDESTROYED        m_pfnDestroy;       //To call on closure
        LPFILEVIEWERSITE m_lpfsi;       // file viewer site

        CLSID               m_clsID;            //CLSID of this FileViewer
        LPSTR               m_pszPath;          //Path from IPersitFile::Load
        DWORD               m_grfMode;          //Open mode for the file
        BOOL                m_fLoadCalled;      //Load called already?
        BOOL                m_fShowInit;        //ShowInitialize called?

        BOOL                m_fClassReg;        //RegisterClass work?
        BOOL                m_fPostQuitMsg;     // Should destroy post quit
        HWND                m_hWnd;             //Main window
        HWND                m_hWndOld;          // Old Main window.
        HWND                m_hWndToolbar;      //Child windows
        HWND                m_hWndStatus;
        HWND                m_hWndViewport;
        HACCEL              m_hAccel;

        UINT                m_cyTools;          //Child window heights
        UINT                m_cyStatus;

        BOOL                m_fToolsVisible;    //Visible child windows.
        BOOL                m_fStatusVisible;

        PCStringTable       m_pST;              //Stringtable object
        PCStatusHelper      m_pSH;              //For WM_MENUSELECT

        //Interface implementations
        PIMPIPERSISTFILE    m_pIPersistFile;
        PIMPIFILEVIEWER     m_pIFileViewer;
        LPFVSHOWINFO        m_pvsi;

        /*
         * MODIFY:  Change these to your own FileViewer specifics.
         * The variables here are specific to text viewing.
         */
        HGLOBAL             m_hMemText;         //Loaded text.
        HFONT               m_hFont;            //Current viewport font
        int                 m_cyPPI;            //logical pix/inch
        int                 m_xPos;             //Scroll positions
        int                 m_yPos;

    protected:
        BOOL                FInitFrameControls(void);
        HRESULT             FileLoad(void);

        void                OnCommand(WORD, WORD, HWND);
        void                ChildrenResize(void);
        void                ViewportResize(void);
        BOOL                FOpenAs(void);
        LPSTR               PszToolTip(UINT);

        //MODIFY:  These may be irrelevant for a custom viewer
        void                ViewportScrollSet(void);
        void                FontChange(VIEWFONTOPTION);
        void                ReplaceWindowModeChange(void);
        BOOL                DropFiles(HDROP);

    public:
        CFileViewer(LPUNKNOWN, HINSTANCE, PFNDESTROYED);
        ~CFileViewer(void);

        HRESULT             Init(void);        //Called from IClassFactory::CreateInstance
        LPSTR               String(UINT);      //inline--see FVTEXT.CPP
        LPVOID              MemAlloc(ULONG);   //IMalloc helpers
        void                MemFree(LPVOID);

        //IFileViewer implementataions (called from CImpIFileViewer)
        STDMETHODIMP        FileShowInit(LPFILEVIEWERSITE lpfsi);
        STDMETHODIMP        FileShow(LPFVSHOWINFO pvsi);
        STDMETHODIMP        PrintTo(LPSTR, BOOL);

        //Non-delegating object IUnknown interface
        STDMETHODIMP         QueryInterface(REFIID, PPVOID);
        STDMETHODIMP_(ULONG) AddRef(void);
        STDMETHODIMP_(ULONG) Release(void);
    };

typedef CFileViewer * PCFileViewer;

/*
 * Interface implementations for the CFileViewer object.
 */

//IPERFILE.CPP
class CImpIPersistFile : public IPersistFile
    {
    private:
        PCFileViewer    m_pObj;         //Back pointer to object
        LPUNKNOWN       m_pUnkOuter;    //Controlling unknown

    public:
        CImpIPersistFile(PCFileViewer, LPUNKNOWN);
        ~CImpIPersistFile(void);

        //IUnknown members that delegate to m_pUnkOuter.
        STDMETHODIMP         QueryInterface(REFIID, PPVOID);
        STDMETHODIMP_(ULONG) AddRef(void);
        STDMETHODIMP_(ULONG) Release(void);

        //IPersist members
        STDMETHODIMP GetClassID(LPCLSID);

        //IPersistFile members
        STDMETHODIMP IsDirty(void);
        STDMETHODIMP Load(LPCOLESTR, DWORD);
        STDMETHODIMP Save(LPCOLESTR, BOOL);
        STDMETHODIMP SaveCompleted(LPCOLESTR);
        STDMETHODIMP GetCurFile(LPOLESTR *);
    };

//IFILEVW.CPP
class CImpIFileViewer : public IFileViewer
    {
    private:
        PCFileViewer    m_pObj;         //Back pointer to object
        LPUNKNOWN       m_pUnkOuter;    //Controlling unknown

    public:
        CImpIFileViewer(PCFileViewer, LPUNKNOWN);
        ~CImpIFileViewer(void);

        //IUnknown members that delegate to m_pUnkOuter.
        STDMETHODIMP         QueryInterface(REFIID, PPVOID);
        STDMETHODIMP_(ULONG) AddRef(void);
        STDMETHODIMP_(ULONG) Release(void);

        //IFileViewer members
        STDMETHODIMP PrintTo(LPSTR, BOOL);
        STDMETHODIMP ShowInitialize(LPFILEVIEWERSITE lpfsi);
        STDMETHODIMP Show(LPFVSHOWINFO pvsi);
    };

#endif //_FVTEXT_H_


IFILEVW.CPP

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM:IFILEVW.CPP
//
//PURPOSE: IFileViewer interface implementation for a FileViewer.
// Custom FileViewer objects should modify the Show member
// to suit its own needs.
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS: N/A
//

#include "fileview.h"

//
//   FUNCTION: CImpIFileViewer::CImpIFileViewer
//
//   PURPOSE:   Constructor
//
//   PARAMETERS:
//  pObj            PCFileViewer of the object we're in.
//    pUnkOuter       LPUNKNOWN to which we delegate.
//
CImpIFileViewer::CImpIFileViewer(PCFileViewer pObj
    , LPUNKNOWN pUnkOuter)
    {
    m_pObj=pObj;
    m_pUnkOuter=pUnkOuter;
    return;
    }
//
//   FUNCTION: CImpIFileViewer::~CImpIFileViewer
//
//   PURPOSE:   Destructor
//
CImpIFileViewer::~CImpIFileViewer(void)
    {
    return;
    }
//
//   FUNCTION: CImpIFileViewer::QueryInterface
// CImpIFileViewer::AddRef
// CImpIFileViewer::Release
//
//   PURPOSE:    IUnknown members for CImpIFileViewer object that only delegate.
//
STDMETHODIMP CImpIFileViewer::QueryInterface(REFIID riid
    , PPVOID ppv)
    {
    return m_pUnkOuter->QueryInterface(riid, ppv);
    }

STDMETHODIMP_(ULONG) CImpIFileViewer::AddRef(void)
    {
    return m_pUnkOuter->AddRef();
    }

STDMETHODIMP_(ULONG) CImpIFileViewer::Release(void)
    {
    return m_pUnkOuter->Release();
    }
//
//   FUNCTION:CImpIFileViewer::PrintTo
//
//   PURPOSE:    Asks a FileViewer to print the file that came through
//  IPersistFile::Load to a specific device with or without
//  any user interaction.  This function should not return
//  until the printing is complete.
//
//   PARAMETERS:
//    pszDriver       LPSTR with the path of the driver to use.  If NULL, use the default driver.
//      fSuppressUI     BOOL indicating if this function is to show anyUI or not.
//
//   RETURN VALUE:
//  HRESULT         NOERROR on success, error code otherwise.
//
STDMETHODIMP CImpIFileViewer::PrintTo(LPSTR pszDriver
    , BOOL fSuppressUI)
    {
     // Printing not implemented in this sample, but
     // make a stub call anyway.
    return m_pObj->PrintTo(pszDriver, fSuppressUI);
    }
//
//   FUNCTION: CImpIFileViewer::ShowInitialize
//
//   PURPOSE:  Initializes everything necessary to display the FileViewer
//  window but does not show it.  The FileViewer should do all
//  the necessary pre-visible work here as this is the only
//  time the FileViewer is allowed to fail.
//
//   PARAMETERS:
//
//   RETURN VALUE:
//   HRESULT         NOERROR or an appropriate error code such as
//                  E_UNEXPECTED, E_FAIL, or E_OUTOFMEMORY.
//
STDMETHODIMP CImpIFileViewer::ShowInitialize(LPFILEVIEWERSITE lpfsi)
    {
    return m_pObj->FileShowInit(lpfsi);
    }
//
//   FUNCTION:CImpIFileViewer::Show
//
//   PURPOSE:    Displays the FileViewer's window in which the file is
//  displayed.  This function cannot be called unless
//  Initialize has already been called.  If Initialize has
//  been called then this function is not allowed to fail and
//  should do little more than show the window and enter a
//  message loop, that is, perform no allocations or anything
//  else that may fail due to low-memory conditions.
//
//  Treat this function like a WinMain function.
//
//   PARAMETERS:
//    nCmdShow        int indicating how to initially show the FileViewer window.
//
//   RETURN VALUE:
//    HRESULT         E_UNEXPECTED if Initalize has not been called, otherwise must be NOERROR.
//
STDMETHODIMP CImpIFileViewer::Show(LPFVSHOWINFO pvsi)
    {
    if (!m_pObj->m_fShowInit)
        return ResultFromScode(E_UNEXPECTED);

    m_pObj->FileShow(pvsi);
    return NOERROR;
    }


IPERFILE.CPP

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM: IPERFILE.CPP
//
//PURPOSE:   IPersistFile interface implementation for a FileViewer.
// This interface is written to not generally require modifications
// for a custom FileViewer but is written to interact with the
// definition for CFileViewer in FVTEXT.H.
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS: N/A
//

#include "fileview.h"

//
//   FUNCTION:CImpIPersistFile::CImpIPersistFile
//
//   PURPOSE:  Constructor
//
//   PARAMETERS:
//pObj            PCFileViewer of the object we're in.
//  pUnkOuter       LPUNKNOWN to which we delegate.
//
CImpIPersistFile::CImpIPersistFile(PCFileViewer pObj
    , LPUNKNOWN pUnkOuter)
    {
    m_pObj=pObj;
    m_pUnkOuter=pUnkOuter;
    return;
    }
//
//   FUNCTION:  CImpIPersistFile::~CImpIPersistFile
//
//   PURPOSE:   Destructor
//
CImpIPersistFile::~CImpIPersistFile(void)
    {
    return;
    }
//
//   FUNCTIONS: CImpIPersistFile::QueryInterface
//  CImpIPersistFile::AddRef
//  CImpIPersistFile::Release
//
//   PURPOSE:
//    IUnknown members for CImpIPersistFile object that only delegate.
//
STDMETHODIMP CImpIPersistFile::QueryInterface(REFIID riid
    , PPVOID ppv)
    {
    return m_pUnkOuter->QueryInterface(riid, ppv);
    }

STDMETHODIMP_(ULONG) CImpIPersistFile::AddRef(void)
    {
    return m_pUnkOuter->AddRef();
    }

STDMETHODIMP_(ULONG) CImpIPersistFile::Release(void)
    {
    return m_pUnkOuter->Release();
    }
//
//   FUNCTION:  CImpIPersistFile::GetClassID
//
//   PURPOSE:   Returns the Class ID of this object.
//
//   PARAMETERS:
// pClsID          LPCLSID in which to store our class ID.
//
//   RETURN VALUE:
// HRESULT         NOERROR always.
//
STDMETHODIMP CImpIPersistFile::GetClassID(LPCLSID pClsID)
    {
    *pClsID=m_pObj->m_clsID;
    return NOERROR;
    }
//
//   FUNCTION: CImpIPersistFile::IsDirty
//
//   PURPOSE:  Always returns ResultFromScode(S_FALSE) for a FileViewer which
//   never makes any changes to the file.
//
//   RETURN VALUE:
//   HRESULT         S_FALSE always
//
STDMETHODIMP CImpIPersistFile::IsDirty(void)
    {
    return ResultFromScode(S_FALSE);
    }
//
//   FUNCTION: CImpIPersistFile::Load
//
//   PURPOSE:  Receives the filename of the path to show in this FileViewer.
//  The object need do nothing more than store this path for later
//  use in IFileViewer::Show.
//
//   PARAMETERS:
//   pszFile         LPCOLESTR of the filename to load.
//     grfMode         DWORD flags to use when opening the file.
//
//   RETURN VALUE:
//   HRESULT         NOERROR or a general error value.
//
STDMETHODIMP CImpIPersistFile::Load(LPCOLESTR pszFile, DWORD grfMode)
    {
    char        szFile[MAX_PATH];
    LPSTR       psz;

     // No modifications are necessary to this code:  it simply
     // copies the parameters into the CFileViewer::m_pszPath and
     // m_grfMode members for use in IFileViewer::ShowInitialize
     // and IFileViewer::Show later on.

    //We should never be called twice
    if (NULL!=m_pObj->m_pszPath)
    {
        // Review:: Make sure everything else is handled correctly
        m_pObj->m_fLoadCalled = FALSE;  // handle error case
        m_pObj->MemFree(m_pObj->m_pszPath);
        m_pObj->m_pszPath = NULL;

    }

    if (NULL==pszFile)
        {
        ODS("IPersistFile::Load called with NULL pointer");
        return ResultFromScode(E_INVALIDARG);
        }

    //Convert Unicode filename to ANSI
    wcstombs(szFile, pszFile, sizeof(szFile));

    psz=(LPSTR)m_pObj->MemAlloc(lstrlen(szFile)+1);

    if (NULL==psz)
        {
        ODS("IPersistFile::Load failed to allocate duplicate pathname");
        return ResultFromScode(E_OUTOFMEMORY);
        }

    //Copy the ANSI filename and the mode to use in opening it.
    lstrcpy(psz, szFile);
    m_pObj->m_pszPath=psz;
    m_pObj->m_grfMode=grfMode;

    //Remember that this function has been called.
    m_pObj->m_fLoadCalled=TRUE;
    return NOERROR;
    }
//
//   FUNCTION: CImpIPersistFile::Save
//
//   PURPOSE:  Not implemented in a FileViewer: since FileViewer objects never
//  make changes to a file there is nothing to save.  Parameters
//  are irrelevant.
//
//   RETURN VALUE:
//  HRESULT         Always contains E_NOTIMPL.
//
STDMETHODIMP CImpIPersistFile::Save(LPCOLESTR pszFile, BOOL fRemember)
    {
    ODS("IPersistFile::Save called...unexpected");
    return ResultFromScode(E_NOTIMPL);
    }
//
//   FUNCTION:  CImpIPersistFile::SaveCompleted
//
//   PURPOSE:  Not implemented in a FileViewer.  Parameters are irrelevant.
//
//   RETURN VALUE:
//   HRESULT         Always contains E_NOTIMPL.
//
STDMETHODIMP CImpIPersistFile::SaveCompleted(LPCOLESTR pszFile)
    {
    ODS("IPersistFile::SaveCompleted called...unexpected");
    return ResultFromScode(E_NOTIMPL);
    }
//
//   FUNCTION: CImpIPersistFile::GetCurFile
//
//   PURPOSE:   Not implemented in a FileViewer.  Normally this function
//  would return a copy of the pathname from IPersistFile::Load
//  in a piece of memory allocated with the shared allocator
//  and stored in *ppszFile.  However, this function will not
//  be called in a FileViewer and can be left unimplemented.
//
//   PARAMETERS:
//  ppszFile        LPOLESTR * into which we store a pointer to
//                  the filename that should be allocated with the shared IMalloc.
//
//   RETURN VALUE:
//  HRESULT         NOERROR or a general error value.
//
STDMETHODIMP CImpIPersistFile::GetCurFile(LPOLESTR *ppszFile)
    {
    LPOLESTR    psz;
    ULONG       cb;

     // No modifications are necessary to this code:  it simply
     // copies the CFileViewer::m_pszPath string into a piece
     // of memory and stores the pointer at *ppszFile.
    //Load must be called, of course.
    if (m_pObj->m_fLoadCalled)
        {
        ODS("IPersistFile::GetCurFile called without IPersistFile::Load");
        return ResultFromScode(E_UNEXPECTED);
        }

    if (NULL==ppszFile)
        {
        ODS("IPersistFile::GetCurFile called with NULL pointer");
        return ResultFromScode(E_INVALIDARG);
        }

    cb=(lstrlen(m_pObj->m_pszPath)+1)*sizeof(OLECHAR);
    psz=(LPOLESTR)m_pObj->MemAlloc(cb);

    if (NULL==psz)
        {
        ODS("IPersistFile::GetCurFile failed to allocate duplicate pathname");
        return ResultFromScode(E_OUTOFMEMORY);
        }

    //Copy the ANSI filename to the new memory, converting to Unicode
    mbstowcs(psz, m_pObj->m_pszPath, cb);

    //Save the pointer which is not caller's responsibility
    *ppszFile=psz;
    return NOERROR;
    }


CSTATHLP.CPP

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//    PROGRAM:        CSTATHLP.CPP
//
//    PURPOSE:  Implements the  CStatusHelper class to assist managing the strings for the
//           status bar.  Here we provide menu ID to string ID mapping  services using a mapping
//           structure in the module's resources.  This code assumes that the status bar is a standard Windows
//           status bar control.
//
//    PLATFORMS:    Windows 95
//
//    SPECIAL INSTRUCTIONS: N/A
//

#include "fileview.h"

//
//   FUNCTION:     CStatusHelper::CStatusHelper
//
//   PURPOSE:  Constructor for the CStatusHelper class
//
//   PARAMETERS:
//  hWnd            HWND of the Status Bar control to work with.
//  hInst           HINSTANCE of the module we're in.
//
CStatusHelper::CStatusHelper(HWND hWnd, HINSTANCE hInst)
    {
    m_hWnd=hWnd;
    m_hInst=hInst;

    m_uIDCur=0xFFFFFFFF;    //Nothing shown yet.

    m_pST=NULL;
    m_pPMM=NULL;
    m_pSMM=NULL;
    m_hMemSMM=NULL;

    //With this FALSE, many other members are ignored.
    m_fMapped=FALSE;
    return;
    }
//
//   FUNCTION:  CStatusHelper::~CStatusHelper
//
//   PURPOSE: Destructor for the CStatusHelper class.
//
CStatusHelper::~CStatusHelper(void)
    {
    //Free up anything from MessageMap
    if (NULL!=m_pPMM)
        {
        LocalFree((HLOCAL)m_pPMM);
        m_pPMM=NULL;
        }

    if (NULL!=m_pST)
        {
        delete m_pST;
        m_pST=NULL;
        }

    if (NULL!=m_pSMM)
        {
        LocalFree((HLOCAL)m_pSMM);
        UnlockResource(m_hMemSMM);
        m_pSMM=NULL;
        }

    if (NULL!=m_hMemSMM)
        {
        FreeResource(m_hMemSMM);
        m_hMemSMM=NULL;
        }

    return;
    }
//
//   FUNCTION: CStatusHelper::MessageMap
//
//   PURPOSE:    Initializes a CStatusHelper for automated processing of
//  WM_MENUSELECT messages as well as setting up a list of messages
//  that we can display using identifiers instead of string
//  pointers.  See MenuSelect and MessageDisplay members.
//
//  PARAMETERS:
//  hWndOwner - HWND of the window owning menus we're interested in serving
//  uIDRMap - UINT identifying a resource mapping ID values to string ID values
//  idsMin - UINT specifying the lowest string ID to load
//  idsMax - UINT specifying the hightest string ID to load
//  cchMax - UINT maximum string length
//  uIDPopupMin - UINT of the lowest ID to assign to popup menus
//  uIDPopupMax - UINT of the highest ID to assign to popup menus
//  uIDStatic- UINT of the ID for the quiescent state message
//  uIDBlank - UINT of the ID for a blank message
//  uIDSysMenu - UINT of the ID for the system menu
//
//  RETURN VALUE:
//  BOOL     TRUE if the function was successful,
//                  FALSE otherwise.
//
//   COMMENTS:  This function is the initializer for the CStatusHelper class.
//  If it fails, then the caller should delete the object.
//
BOOL CStatusHelper::MessageMap(HWND hWndOwner, UINT uIDRMap
    , UINT idsMin, UINT idsMax, UINT cchMax, UINT uIDPopupMin
    , UINT uIDPopupMax, UINT uIDStatic, UINT uIDBlank, UINT uIDSysMenu)
    {
    HMENU           hMenu;
    HRSRC           hRes;
    UINT            i;
    USHORT          uID;
    DWORD           dwSize;
    PSTATMESSAGEMAP pSMM;

    // Check if we even got a valid window in the constructor
    // or if we've already been called.
    if (!IsWindow(m_hWnd) || m_fMapped)
        return FALSE;

    //Parameter validation
    if (idsMax < idsMin || uIDPopupMax < uIDPopupMin)
        return FALSE;

    //Cache away all this vital information
    m_hWndOwner  =hWndOwner;
    m_idsMin     =idsMin;
    m_idsMax     =idsMax;
    m_cMessages  =(USHORT)(idsMax-idsMin+1);

    m_uIDPopupMin=uIDPopupMin;
    m_uIDPopupMax=uIDPopupMax;
    m_cPopups    =(USHORT)(uIDPopupMax-uIDPopupMin+1);
    m_uIDStatic  =uIDStatic;
    m_uIDBlank   =uIDBlank;
    m_uIDSysMenu =uIDSysMenu;

    //Get a stringtable with all the messages
    m_pST=new CStringTable(m_hInst);

    if (NULL==m_pST)
        return FALSE;

    if (!m_pST->FInit(idsMin, idsMax, CCHSTATUSMSGMAX))
        return FALSE;

    //Load the STATMESSAGEMAP array from resources
    hRes=FindResource(m_hInst, MAKEINTRESOURCE(uIDRMap), RT_RCDATA);

    if (NULL==hRes)
        return FALSE;

    m_hMemSMM=LoadResource(m_hInst, hRes);

    if (NULL==m_hMemSMM)
        return FALSE;

    pSMM=(PSTATMESSAGEMAP)LockResource(m_hMemSMM);

    if (NULL==pSMM)
        return FALSE;

    // Now get the size of the resource
    dwSize = SizeofResource(m_hInst, hRes);

    // Resources are ReadOnly data so copy it to memory that can
    // be used
    m_pSMM = (PSTATMESSAGEMAP)LocalAlloc(LPTR, dwSize);
    if (NULL==m_pSMM)
        return FALSE;
    CopyMemory(m_pSMM, pSMM, dwSize);

    //Sort these for binary search lookup.
    Sort();

    //Allocate an array of POPUPMENUMAP structures
    m_pPMM=(PPOPUPMENUMAP)LocalAlloc(LPTR
        , sizeof(POPUPMENUMAP)*m_cPopups);

    if (NULL==m_pPMM)
        return FALSE;

    //Initialize the array mapping popup menus to specific IDs.
    uID=uIDPopupMin;
    hMenu=GetMenu(m_hWndOwner);

    for (i=0; i < m_cPopups; i++)
        {
        m_pPMM[i].hMenu=GetSubMenu(hMenu, i);
        m_pPMM[i].uID  =uID++;
        }

    //All done!
    m_fMapped=TRUE;
    return TRUE;
    }
//
//   FUNCTION: CStatusHelper::MenuSelect
//
//   PURPOSE:  Displays the appropriate message for whatever is in the
//  parameters of a WM_MENUSELECT message.  This can only be called
//  if StatStripMessageMap has already been called and must be used
//  with the same menu the owner window had at the time of that call.
//
//   PARAMETERS:
//  wItem           WORD identifying the selected item
//  wFlags          WORD specifying the type of menu item
//  hMenu           HMENU of the menu selected.
//
void CStatusHelper::MenuSelect(WORD wItem, WORD wFlags, HMENU hMenu)
    {
    USHORT          uID;

    if (!m_fMapped)
        return;

    //Case 1:  Menu was cancelled, display static string
    if (0==wItem && (WORD)0xFFFF==wFlags)
        uID=m_uIDStatic;
    else
        {
        //Case 2:  System menu selected by itself.
        if ((MF_POPUP & wFlags) && (MF_SYSMENU & wFlags))
            uID=m_uIDSysMenu;
        else
            {
             //  Case 3:  A popup menu was chosen:
             //  Find the ID for the popup menu index in wItem
            if (MF_POPUP & wFlags)
                uID=IDFromHMenu((HMENU)wItem);
            else
                //Case 4:  A menu item is selected
                if (0!=wItem)
                    uID=(UINT)wItem;
                else
                    //Case 5:  Nothing is selected (e.g. separator)
                    uID=m_uIDBlank;
           }
        }

    //Display the message
    MessageDisplay(uID);
    return;
    }
//
//   FUNCTION:       CStatusHelper::MessageDisplay
//
//   PURPOSE:  Displays the appropriate message for a given ID value.  This
//  can only be called if MessageMap has already been called.
//
//   PARAMETERS:
//     uID             UINT of the message to display.  This is not a string ID but an ID
//                       in the STATMESSAGEMAP structure that maps to a string ID.
//
void CStatusHelper::MessageDisplay(UINT uID)
    {
    UINT        idsMsg;

    if (!m_fMapped)
        return;

    //If we're already displaying this ID, nothing we need to do
    if (m_uIDCur==uID)
        return;

    //Go look up the string ID to display.
    idsMsg=IStringFromID(uID);

    //Display it.
    SetWindowText(m_hWnd, (*m_pST)[idsMsg]);
    return;
    }
//
//   FUNCTION:       CStatusHelper::Sort          (Private)
//
//   PURPOSE:   Performs a selection sort on the STATMESSAGEMAP array that we
//  load from resources.  Since we expect that the data is partially
//  sorted (we tend to place things in resources in groups of
//  seqential values), since the number of messages is usually
//  less than 200, and since we're usually doing this stuff on startup
//  (which takes a long time anyway), a cimple selection sort is a
//  better choice than a more complex qsort.
//
void CStatusHelper::Sort(void)
    {
    UINT            i, j, k;
    STATMESSAGEMAP  smm;

    for (j=0; j < (UINT)(m_cMessages-1); j++)
        {
        k=j;
        smm.uID   =m_pSMM[j].uID;
        smm.idsMsg=m_pSMM[j].idsMsg;

        for (i=j+1; i < (UINT)m_cMessages; i++)
            {
            if (m_pSMM[i].uID < smm.uID)
                {
                smm.uID   =m_pSMM[i].uID;
                smm.idsMsg=m_pSMM[i].idsMsg;
                k=i;
                }
            }

        smm.uID         =m_pSMM[j].uID;
        smm.idsMsg      =m_pSMM[j].idsMsg;
        m_pSMM[j].uID   =m_pSMM[k].uID;   ;
        m_pSMM[j].idsMsg=m_pSMM[k].idsMsg;;
        m_pSMM[k].uID   =smm.uID;
        m_pSMM[k].idsMsg=smm.idsMsg;
        }

    return;
    }
//
//   FUNCTION:       CStatusHelper::IDFromHMenu (Private)
//
//   PURPOSE:   Given a specific popup menu index, searches through m_pPMM for
//  a match and returns the ID associated with that menu.
//
//   PARAMETERS:
//       hMenu           HMENU to of he popup menu.
//
//   RETURN VALUE:
//       USHORT          ID associated with the menu handle.
//
USHORT CStatusHelper::IDFromHMenu(HMENU hMenu)
    {
    USHORT      uID=m_uIDBlank;      //Default is empty
    UINT        i;

    for (i=0; i < m_cPopups; i++)
        {
        if (m_pPMM[i].hMenu==hMenu)
            {
            uID=m_pPMM[i].uID;
            break;
            }
        }

    return uID;
    }
//
//   FUNCTION:     CStatusHelper::IStringFromID (Private)
//
//   PURPOSE:  Performs a binary search in a STATMESSAGEMAP array looking for
//  a specific item ID returning the string ID for that item.
//
//   PARAMETERS:
//        uID             USHORT item ID to locate.
//
//   RETURN VALUE:
//       UINT            String ID associated with wItem.
//
UINT CStatusHelper::IStringFromID(USHORT uID)
    {
    UINT        iLow =0;
    UINT        iHigh=m_cMessages-1;
    UINT        iMid;

    while (TRUE)
        {
        iMid=(iLow+iHigh) >> 1;

        if (uID < m_pSMM[iMid].uID)
            iHigh=iMid-1;
        else
            {
            if (uID > m_pSMM[iMid].uID)
                iLow=iMid+1;
            else
                break;    //Equality
            }

        if (iHigh < iLow)
            break;
        }

    return m_pSMM[iMid].idsMsg;
    }


RESOURCE.H

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//PROGRAM: RESOURCE.H
//
//PURPOSE: Definitions specifically pertaining to resources for a FileViewer.
//
//PLATFORMS:Windows 95
//
//SPECIAL INSTRUCTIONS: N/A
//
#ifndef _RESOURCE_H_
#define _RESOURCE_H_

//Resource identifiers
#define IDR_MENU                1
#define IDR_MENUVIEWPORT        2
#define IDR_ACCELERATORS        1
#define IDR_STATMESSAGEMAP      1
#define IDB_TOOLS               1
#define IDD_ABOUT               1

//Toolbar bitmaps indexes and other values
#define IDBT_OPENAS              0
#define IDBT_FONTSIZEINCREASE    1
#define IDBT_FONTSIZEDECREASE    2

#define CTBBUTTONS              4
#define CTBBITMAPS              3

//Menu command identifiers.
#define IDM_FILEOPENAS          100
#define IDM_FILEPAGESETUP       110
#define IDM_FILEPRINT           111
#define IDM_FILEEXIT            120

#define IDM_VIEWTOOLBAR         200
#define IDM_VIEWSTATUSBAR       201
#define IDM_VIEWFONT            210
#define IDM_VIEWFONTINCREASE    211     //Toolbar only
#define IDM_VIEWFONTDECREASE    212     //Toolbar only
#define IDM_VIEWREPLACE         223

#define IDM_HELPCONTENTS        300
#define IDM_HELPABOUT           301

//String identifiers.  Keep sequential for each group.

//Main window strings (messages, other variable strings).
#define IDS_MIN                 0

#define IDS_CLASSFRAME          (IDS_MIN+0)
#define IDS_CLASSVIEWPORT       (IDS_MIN+1)
#define IDS_CAPTION             (IDS_MIN+2)
#define IDS_DOCUMENTTYPE        (IDS_MIN+3)
#define IDS_NOHELP              (IDS_MIN+4)
#define IDS_TOOLTIPOPENAS       (IDS_MIN+5)
#define IDS_TOOLTIPFONTINC      (IDS_MIN+6)
#define IDS_TOOLTIPFONTDEC      (IDS_MIN+7)

#define IDS_MAX                 (IDS_MIN+7)

//Length of longest string in this stringtable.
#define CCHSTRINGMAX            40

//Status bar messages for a message map
#define IDS_STATUSMSGMIN        32

#define IDS_MSGEMPTY            (IDS_STATUSMSGMIN+0)
#define IDS_MSGREADY            (IDS_STATUSMSGMIN+1)
#define IDS_MSGSYS              (IDS_STATUSMSGMIN+2)
#define IDS_MSGSYSRESTORE       (IDS_STATUSMSGMIN+3)
#define IDS_MSGSYSMOVE          (IDS_STATUSMSGMIN+4)
#define IDS_MSGSYSSIZE          (IDS_STATUSMSGMIN+5)
#define IDS_MSGSYSMINIMIZE      (IDS_STATUSMSGMIN+6)
#define IDS_MSGSYSMAXIMIZE      (IDS_STATUSMSGMIN+7)
#define IDS_MSGSYSCLOSE         (IDS_STATUSMSGMIN+8)
#define IDS_MSGSYSTASKLIST      (IDS_STATUSMSGMIN+9)
#define IDS_MSGFILE             (IDS_STATUSMSGMIN+10)
#define IDS_MSGFILEOPENAS       (IDS_STATUSMSGMIN+11)
#define IDS_MSGFILEPAGESETUP    (IDS_STATUSMSGMIN+12)
#define IDS_MSGFILEPRINT        (IDS_STATUSMSGMIN+13)
#define IDS_MSGFILEEXIT         (IDS_STATUSMSGMIN+14)
#define IDS_MSGVIEW             (IDS_STATUSMSGMIN+15)
#define IDS_MSGVIEWTOOLBAR      (IDS_STATUSMSGMIN+16)
#define IDS_MSGVIEWSTATUSBAR    (IDS_STATUSMSGMIN+17)
#define IDS_MSGVIEWFONT         (IDS_STATUSMSGMIN+18)
#define IDS_MSGHELP             (IDS_STATUSMSGMIN+19)
#define IDS_MSGHELPCONTENTS     (IDS_STATUSMSGMIN+20)
#define IDS_MSGHELPABOUT        (IDS_STATUSMSGMIN+21)
#define IDS_MSGTIPFONTINC       (IDS_STATUSMSGMIN+22)
#define IDS_MSGTIPFONTDEC       (IDS_STATUSMSGMIN+23)
#define IDS_MSGCHOOSEOPEN       (IDS_STATUSMSGMIN+24)

#define IDS_STATUSMSGMAX        (IDS_STATUSMSGMIN+24)

#define CCHSTATUSMSGMAX         80

/*
 * IDs for status bar mapping of popup menus to string IDs.
 * Those for the menus defined between ID_MENUMIN and ID_MENUMAX
 * that we send to CStatusHelper::MessageMap must be sequential
 * as the popup menu hMenu's are mapped to these values by
 * sequential position, so be sure to define these values in
 * the same sequence as the menus themselves.
 */

#define ID_MSGEMPTY                         1000
#define ID_MSGREADY                         1001
#define ID_MENUSYS                          1002
#define ID_MENUFILE                         1003
#define ID_MENUVIEW                         1004
#define ID_MENUHELP                         1005
#define ID_TIPFONTINC                       1006
#define ID_TIPFONTDEC                       1007
#define ID_MSGCHOOSEOPEN                    1008

#define ID_MENUMIN                          ID_MENUFILE
#define ID_MENUMAX                          ID_MENUHELP

#endif //_RESOURCE_H_


README.TXT

File Parser

SUMMARY
=======

The FPARSER sample parses files with a particular extension.

The default file extension for this sample is TST. To use this file parser
for ASCII files of different extensions, the registry must be changed. The
registry entry for the new extension must be given a new key named
QuickViewAddOn under the subkey that contains our viewer class identifier.
Within this key, the registry should have an item named DllName, whose value
is the name of your DLL.

As an example, a filter whose file extension is .XXX, would need the
following reg file contents merged into the registry:

   HKEY_LOCAL_MACHINE\SOFTWARE\
   Classes\QuickView\.XXX\{F0F08735-0C36-101B-B086-0020AF07D0F4}
   \QuickViewAddOn

   DllName"="vsxxx.dll

This would enable a user to right-click on a file of extension .XXX and
have the vsxxx.dll file parser be used.

This approach will override any built-in file identification used by
SCC's DLLs, using the extension only to choose the specified file parser.

NOTE: No other identifier can be used in the above registry entry.

MORE INFORMATION
================

This sample provides a template for an ASCII file parser. It is configured
to work with the file extension .TST, but this can be changed by modifying
FPARSER.REG to the extension you wish.

Usage
-----

To use this file parser, use the following steps:

1. Import FPARSER.REG into your registry.

2. Set the key HKEY_LOCAL_MACHINE\SOFTWARE\SCC\Viewer Technology\MS1.
   Verify to a non-zero value. This will reset the list of parsers the next
   time a file is viewed.

3. Build VS_ASC.DLL.

4. Copy VS_ASC.DLL to <windows path>\SYSTEM\VIEWERS.

5. Rename or copy an ASCII file to a .TST extension.

6. From the Explorer, right-click the file.

7. Select Quick View.


ENTRY.H

/*
|These are #define that would have been put in vwdefs.h,
|but they need to be defined before vwdefs.h is included in
|vsctop.h so the filter writers can use them, especially
|VWPRT, in their own include file, which is included
|before vwdefs.h
*/

#define WINDOWS

#ifdef WIN32

#define VWPTR
#define VW_ENTRYSC__declspec(dllexport)
#define VW_ENTRYMOD__cdecl
#define VW_LOCALSC
#define VW_LOCALMOD__cdecl
#define VW_SEPARATE_DATA

#endif /*WIN32*/


SODEFS.H

/*-------------------------------------------------------------------
|OS Dependent Area
*/

#ifndef SODEFS_H
#define SODEFS_H

#ifdef WIN16
#define SO_ENTRYSC
#define SO_ENTRYMOD__export __far __cdecl
typedef unsigned longSOINT32U;
typedef signed longSOINT32S;
#endif /*WIN16*/

#ifdef WIN32
#define SO_ENTRYSC__declspec(dllexport)
#define SO_ENTRYMOD__cdecl
typedef unsigned longSOINT32U;
typedef signed longSOINT32S;
#define HUGE
#endif /*WIN32*/

/*-------------------------------------------------------------------
|OS Independent Area
*/

#define SO_BEGINTOKEN0xFF

#define SO_CHARATTR0
#define SO_SPECIALCHAR1
#define SO_CHARHEIGHT2
#define SO_BREAK3
#define SO_PARAALIGN4
#define SO_PARAINDENT5
#define SO_TABSTOPS6
#define SO_MARGINS7
#define SO_BEGINSUB8
#define SO_ENDSUB9

#define SO_CHARX12
#define SO_TAGBEGIN13
#define SO_TAGEND14

#define SO_PARASPACING15
#define SO_CHARFONTBYID 16
#define SO_CHARFONTBYNAME17

#define SO_TABLE18
#define SO_TABLEEND21

#define SO_GRAPHICOBJECT22

#define SO_GOTOPOSITION 23
#define SO_DRAWLINE24

#define SO_ENDOFCHUNK0xFE

#define SO_TEXTCELL10/* these two #defines should be in the chunker stuff */
#define SO_DATACELL11
#define SO_EMPTYCELL15

#define SO_CHTAB1
#define SO_CHUNKNOWN2
#define SO_CHSHYPHEN3
#define SO_CHHPAGE4
#define SO_CHHLINE5
#define SO_CHHSPACE6
#define SO_CHHHYPHEN7
#define SO_CHPAGENUMBER 8
#define SO_CHDATE9
#define SO_CHTIME10
#define SO_CHCELLTAB11

/* NOTE:  There is code in chunker.c that relies on the fact that */
/* the value of the largest #define for a character attribute will */
/* not exceed 15. */

#define SO_BOLD 2
#define SO_ITALIC3
#define SO_UNDERLINE4
#define SO_DUNDERLINE5
#define SO_SMALLCAPS6
#define SO_OUTLINE7
#define SO_SHADOW8
#define SO_CAPS 9
#define SO_SUBSCRIPT10
#define SO_SUPERSCRIPT11
#define SO_STRIKEOUT12
#define SO_WORDUNDERLINE13
#define SO_DOTUNDERLINE 14

#define SO_ON1
#define SO_OFF0

#define SO_YES1
#define SO_NO0

#define SO_STOP 1
#define SO_CONTINUE0

#define SO_ALIGNLEFT1
#define SO_ALIGNRIGHT2
#define SO_ALIGNCENTER3
#define SO_ALIGNJUSTIFY 4

#define SO_TABLEFT1
#define SO_TABRIGHT2
#define SO_TABCENTER3
#define SO_TABCHAR4
#define SO_TABEMPTY5

#define SO_HEADER1
#define SO_FOOTER2
#define SO_FOOTNOTE3
#define SO_COMMENT4

#define SO_LEFT 1
#define SO_RIGHT2
#define SO_BOTH 3

#define SO_WORDPROCESSOR120
#define SO_SPREADSHEET121
#define SO_DATABASE122

#define SO_PARAGRAPHS0
#define SO_CELLS1
#define SO_FIELDS2
#define SO_BITMAP4
#define SO_ARCHIVE5
#define SO_VECTOR6

#define SO_PARABREAK1
#define SO_EOFBREAK2
#define SO_SECTIONBREAK 3
#define SO_CELLBREAK4
#define SO_RECORDBREAK5
#define SO_SUBDOCBEGINBREAK6
#define SO_SUBDOCENDBREAK7
#define SO_SCANLINEBREAK8
#define SO_ARCHIVEBREAK 9
#define SO_VECTORBREAK10
#define SO_TABLEROWBREAK19
#define SO_TABLECELLBREAK20

#define SOERROR_GENERAL 0
#define SOERROR_BADFILE 1
#define SOERROR_EOF2

#define SO_CELLLEFT0
#define SO_CELLRIGHT1
#define SO_CELLCENTER2
#define SO_CELLFILL3

#define SO_CELLBOLD1
#define SO_CELLITALIC2
#define SO_CELLUNDERLINE4
#define SO_CELLSTRIKEOUT8

#define SO_CELLINT32S0
#define SO_CELLINT32U1
#define SO_CELLIEEE4I2
#define SO_CELLIEEE4M3
#define SO_CELLIEEE8I4
#define SO_CELLIEEE8M5
#define SO_CELLIEEE10I6
#define SO_CELLIEEE10M7
#define SO_CELLBCD8I8
#define SO_CELLEMPTY9
#define SO_CELLERROR10
#define SO_FIELDTEXTFIX 11
#define SO_FIELDTEXTVAR 12

#define SO_CELLNUMBER0
#define SO_CELLDOLLARS1
#define SO_CELLPERCENT2
#define SO_CELLEXPONENT 3
#define SO_CELLDECIMAL4
#define SO_CELLDATETIME 5
#define SO_CELLDATE6
#define SO_CELLTIME7
#define SO_CELLBOOL8

#define SO_CELLNEG_MINUS0
#define SO_CELLNEG_PARENBIT0
#define SO_CELLNEG_MINUSREDBIT1
#define SO_CELLNEG_PARENREDBIT2

#define SO_CELL1000SEP_NONE0
#define SO_CELL1000SEP_COMMABIT3

/* MASK = 0xF0, bits 4,5,6,7 */
#define SO_CELLMULT_MASK0xF0

#define SO_CELLMULT_10
#define SO_CELLMULT_010x10
#define SO_CELLMULT_50000x20
#define SO_CELLMULT_500 0x30
#define SO_CELLMULT_050x40
#define SO_CELLMULT_005 0x50
#define SO_CELLMULT_00050x60
#define SO_CELLMULT_000050x70
#define SO_CELLMULT_06250x80
#define SO_CELLMULT_0156250x90
#define SO_CELLMULT_00010xA0

#define SO_CELLDATESEP_SLASHBIT8
#define SO_CELLDATESEP_MINUSBIT9
#define SO_CELLDATESEP_PERIODBIT10
#define SO_CELLDATESEP_SPACEBIT11
#define SO_CELLDATESEP_NONE0

#define SO_CELLMONTH_FULLBIT12
#define SO_CELLMONTH_ABBREVBIT13
#define SO_CELLMONTH_NUMBERBIT14
#define SO_CELLMONTH_NONE0

#define SO_CELLYEAR_FULLBIT15L
#define SO_CELLYEAR_ABBREVBIT16L
#define SO_CELLYEAR_NONE0

#define SO_HHMMBITBIT17L
#define SO_HHMMSSBITBIT18L
#define SO_AMPMBITBIT19L
#define SO_HMSBITBIT20L

#define SO_CELLTIME_HHMM24SO_HHMMBIT
#define SO_CELLTIME_HHMMSS24SO_HHMMSSBIT
#define SO_CELLTIME_HHMMAM(SO_HHMMBIT | SO_AMPMBIT)
#define SO_CELLTIME_HHMMSSAM(SO_HHMMSSBIT | SO_AMPMBIT)
#define SO_CELLTIME_HHMMHMS(SO_HHMMBIT | SO_HMSBIT)
#define SO_CELLTIME_HHMMSSHMS(SO_HHMMSSBIT | SO_HMSBIT)
#define SO_CELLTIME_NONE0

#define SO_CELLDAY_NUMBERBIT21L
#define SO_CELLDAY_NONE 0

#define SO_CELLDAYOFWEEK_FULLBIT22L
#define SO_CELLDAYOFWEEK_ABBREV BIT23L
#define SO_CELLDAYOFWEEK_NONE0

/* The following values are stored in consecutive 3-bit pieces of an integer. */

/* MASK = 0x07 */
#define SO_CELLDAYOFWEEK_SHIFT0
#define SO_CELLDAYOFWEEK_MASK0x07
#define SO_CELLDAYOFWEEK_11
#define SO_CELLDAYOFWEEK_22
#define SO_CELLDAYOFWEEK_33
#define SO_CELLDAYOFWEEK_44
#define SO_CELLDAYOFWEEK_55

/* MASK = 0x38 */
#define SO_CELLMONTH_SHIFT3
#define SO_CELLMONTH_MASK0x38
#define SO_CELLMONTH_18/* (1<<3) */
#define SO_CELLMONTH_216/* (2<<3) */
#define SO_CELLMONTH_324/* (3<<3) */
#define SO_CELLMONTH_432/* (4<<3) */
#define SO_CELLMONTH_540/* (5<<3) */

/* MASK = 0x1C0 */
#define SO_CELLDAY_SHIFT6
#define SO_CELLDAY_MASK 0x1C0
#define SO_CELLDAY_164/* (1<<6) */
#define SO_CELLDAY_2128/* (2<<6) */
#define SO_CELLDAY_3192/* (3<<6) */
#define SO_CELLDAY_4256/* (4<<6) */
#define SO_CELLDAY_5320/* (5<<6) */

/* MASK = 0xE00 */
#define SO_CELLYEAR_SHIFT9
#define SO_CELLYEAR_MASK0xE00
#define SO_CELLYEAR_1512/* (1<<9) */
#define SO_CELLYEAR_21024/* (2<<9) */
#define SO_CELLYEAR_31536/* (3<<9) */
#define SO_CELLYEAR_42048/* (4<<9) */
#define SO_CELLYEAR_52560  /* (5<<9) */

/* MASK = 0x7000 */
#define SO_CELLTIME_SHIFT12
#define SO_CELLTIME_MASK0x7000
#define SO_CELLTIME_14096  /* (1<<12) */
#define SO_CELLTIME_28192  /* (2<<12) */
#define SO_CELLTIME_312288 /* (3<<12) */
#define SO_CELLTIME_416384 /* (4<<12) */
#define SO_CELLTIME_520480 /* (5<<12) */

/*
 | Character sets.
*/

#define SO_PC1
#define SO_WINDOWS3
#define SO_DBCS5

/*
| SOSetDateBase FLAGS
*/

#define SO_LOTUSHELL0x0001

/*
| SOGetInfo Ids
*/

#define SOINFO_COLUMNRANGE  1
#define SOINFO_STARTRECORD  2

/*
| SOPutCharX & SOPutSpecialCHarX FLAGS
*/

#define SO_COUNTBIT0x0001
#define SO_LIMITEDITBIT 0x0002
#define SO_HIDDENBIT0x0004

#define SO_COUNTSO_COUNTBIT
#define SO_NOCOUNTSO_LIMITEDITBIT
#define SO_LIMITEDITSO_LIMITEDITBIT
#define SO_DELETESO_COUNTBIT
#define SO_NODELETE0
#define SO_HIDDEN(SO_HIDDENBIT | SO_LIMITEDITBIT)
#define SO_NOHIDDEN0x0000

/*
| font family
*/

#define SO_FAMILYUNKNOWN1
#define SO_FAMILYROMAN2
#define SO_FAMILYSWISS3
#define SO_FAMILYMODERN 4
#define SO_FAMILYSCRIPT 5
#define SO_FAMILYDECORATIVE6
#define SO_FAMILYSYMBOL 7

#define SO_FAMILYWINDOWS0x1000

#define SO_CHARSET_SHIFTJIS0x2000
#define SO_CHARSET_HANGEUL0x4000
#define SO_CHARSET_CHINESEBIG50x8000

/*
| line spacing types
*/

#define SO_HEIGHTAUTO1
#define SO_HEIGHTATLEAST2
#define SO_HEIGHTEXACTLY3

/*
| position flags
*/
#define SOPOS_FROMLEFTEDGE0x01
#define SOPOS_FROMTOPEDGE0x02
#define SOPOS_FROMBASELINE0x04

typedef struct SOPAGEPOSITIONtag
{
DWORD dwFlags;
LONG lXOffset;
LONG lYOffset;
} SOPAGEPOSITION, VWPTR * PSOPAGEPOSITION;

/*
| Bitmap filter definitions
*/

/* wImageFlags */
#define SO_BOTTOMTOTOP0x0001
#define SO_GRAYSCALE0x0002
#define SO_BLACKANDWHITE0x0004
#define SO_COLORPALETTE 0x0008
#define SO_RGBCOLOR0x0010
#define SO_BGRCOLOR0x0020
#define SO_WHITEZERO0x0040

/*
| Archive filter definitions
*/

#define SO_ARCENDOFRECORD0xFFFF

#define SOARC_FILENAME1
#define SOARC_FILEDATE2
#define SOARC_FILETIME3
#define SOARC_FILEPATH4
#define SOARC_COMPRESSIONTYPE5
#define SOARC_FILECOMMENT6
#define SOARC_FILEMODDATE7
#define SOARC_FILEMODTIME8
#define SOARC_FILESIZE9
#define SOARC_FILECOMPRESSSIZE10
#define SOARC_FILECRC11
#define SOARC_BUFFERSIZE12
#define SOARC_CHECKSUM13
#define SOARC_FILEOS14
#define SOARC_ENCRYPTED 15

/*
| Vector graphics stuff.
*/

/*
| Macros
*/

#ifdef WINDOWS
#define SORGB(r,g,b) ((SOCOLORREF)(((BYTE)(r)|((DWORD)(BYTE)(g)<<8))|(((DWORD)(BYTE)(b))<<16)))
#endif

#define SOPALETTERGB(r,g,b)   (0x02000000L | SORGB(r,g,b))
#define SOPALETTEINDEX(i)     ((SOCOLORREF)(0x01000000L | (DWORD)(WORD)(i)))
#define SOANGLETENTHS(n) ((SOANGLE)(n))
#define SOSETRATIO(m,n) ((SORATIO)(((SORATIO)m<<16)|(SORATIO)(n)))

#define SOREDFROMRGB(rgb) (BYTE)(rgb&0x000000FF)
#define SOGREENFROMRGB(rgb) (BYTE)((rgb&0x0000FF00)>>8)
#define SOBLUEFROMRGB(rgb) (BYTE)((rgb&0x00FF0000)>>16)

typedef DWORD SOCOLORREF;
typedef SHORTSOANGLE;
typedef DWORD SORATIO;

typedef struct SORECTtag
{
SHORT left;
SHORT top;
SHORT right;
SHORT bottom;
} SORECT, VWPTR *PSORECT;

typedef struct SOPOINTtag
{
SHORT x;
SHORT y;
} SOPOINT, VWPTR *PSOPOINT;

#define SOLF_FACESIZE  32
typedef struct SOLOGFONTtag
{
    SHORT     lfHeight;
    SHORT     lfWidth;
    SHORT     lfEscapement;
    SHORT     lfOrientation;
    SHORT     lfWeight;
    BYTE    lfItalic;
    BYTE    lfUnderline;
    BYTE    lfStrikeOut;
    BYTE    lfCharSet;
    BYTE    lfOutPrecision;
    BYTE    lfClipPrecision;
    BYTE    lfQuality;
    BYTE    lfPitchAndFamily;
    CHAR    lfFaceName[SOLF_FACESIZE];
} SOLOGFONT, VWPTR *PSOLOGFONT;

/* lfWeight values */
#define SOLF_FW_NORMAL    400
#define SOLF_FW_BOLD700

/* lfCharSet */
#define SOLF_ANSI_CHARSET0
#define SOLF_SYMBOL_CHARSET2
#define SOLF_OEM_CHARSET255

/* lfOutPrecision */
#define SOLF_OUT_DEFAULT_PRECIS 0

/* lfClipPrecision */
#define SOLF_CLIP_DEFAULT_PRECIS0
#define SOLF_CLIP_LH_ANGLES0x10

/* lfQuality */
#define SOLF_DEFAULT_QUALITY0
#define SOLF_DRAFT_QUALITY1
#define SOLF_PROOF_QUALITY2

/* PitchAndFamily pitch values (low 4 bits) */
#define SOLF_DEFAULT_PITCH0x00
#define SOLF_FIXED_PITCH0x01
#define SOLF_VARIABLE_PITCH0x02

/* PitchAndFamily family values (high 4 bits) */
#define SOLF_FF_DECORATIVE0x00
#define SOLF_FF_DONTCARE0x10
#define SOLF_FF_MODERN0x20
#define SOLF_FF_ROMAN0x30
#define SOLF_FF_SCRIPT0x40
#define SOLF_FF_SWISS0x50

typedef struct SOLOGPENtag
{
SHORT loPenStyle;
SOPOINT loWidth;
SOCOLORREF loColor;
} SOLOGPEN, VWPTR *PSOLOGPEN;

/* Pen Styles */
#define SOPS_SOLID    0
#define SOPS_DASH1
#define SOPS_DOT      2
#define SOPS_DASHDOT 3
#define SOPS_DASHDOTDOT       4
#define SOPS_NULL    5
#define SOPS_INSIDEFRAME    6

typedef struct SOLOGBRUSHtag
{
WORD lbStyle;
SOCOLORREF lbColor;
SHORT lbHatch;
} SOLOGBRUSH, VWPTR *PSOLOGBRUSH;

/* Brush Styles */
#define SOBS_SOLID    0
#define SOBS_NULL    1
#define SOBS_HOLLOWSOBS_NULL
#define SOBS_HATCHED 2
#define SOBS_PATTERN 3
#define SOBS_INDEXED 4
#define SOBS_DIBPATTERN  5

/* Hatch Styles */
#define SOHS_HORIZONTAL       0
#define SOHS_VERTICAL    1
#define SOHS_FDIAGONAL      2
#define SOHS_BDIAGONAL      3
#define SOHS_CROSS 4
#define SOHS_DIAGCROSS      5

typedef struct SOVECTORHEADERtag
{
WORD wStructSize;
SORECT  BoundingRect;
WORD wHDpi;
WORD wVDpi;
WORD wImageFlags;
SOCOLORREF BkgColor;
} SOVECTORHEADER, VWPTR *PSOVECTORHEADER;

/* wImageFlags values */
#define SO_VECTORRGBCOLORBIT0
#define SO_VECTORCOLORPALETTEBIT1
#define SO_YISUPBIT2
#define SO_XISLEFTBIT3

typedef struct SOARCINFOtag
{
SORECT  Rect;
SOANGLE StartAngle;
SOANGLE EndAngle;
} SOARCINFO, VWPTR *PSOARCINFO;

typedef struct SOTEXTINRECTtag
{
SORECT  Rect;
WORD wFormat;
SHORT nTextLength;
} SOTEXTINRECT, VWPTR *PSOTEXTINRECT;

/* wFormat values */
#define SODT_TOP    0x0000
#define SODT_LEFT    0x0000
#define SODT_CENTER0x0001
#define SODT_RIGHT    0x0002
#define SODT_VCENTER 0x0004
#define SODT_BOTTOM0x0008
#define SODT_WORDBREAK 0x0010
#define SODT_SINGLELINE  0x0020
#define SODT_EXPANDTABS  0x0040
#define SODT_TABSTOP 0x0080
#define SODT_NOCLIP0x0100
#define SODT_EXTERNALLEADING  0x0200
#define SODT_CALCRECT 0x0400
#define SODT_NOPREFIX 0x0800
#define SODT_INTERNAL 0x1000

/* PolyFillMode values */
#define SOPF_ALTERNATE 1
#define SOPF_WINDING 2

/* DrawMode values */
#define SOR2_BLACK 1
#define SOR2_NOTMERGEPEN      2
#define SOR2_MASKNOTPEN       3
#define SOR2_NOTCOPYPEN       4
#define SOR2_MASKPENNOT       5
#define SOR2_NOT      6
#define SOR2_XORPEN  7
#define SOR2_NOTMASKPEN       8
#define SOR2_MASKPEN 9
#define SOR2_NOTXORPEN      10
#define SOR2_NOP      11
#define SOR2_MERGENOTPEN      12
#define SOR2_COPYPEN 13
#define SOR2_MERGEPENNOT      14
#define SOR2_MERGEPEN    15
#define SOR2_WHITE 16

typedef struct SOTEXTATPOINTtag
{
SOPOINT Point;
WORD wFormat;
SHORT nTextLength;
} SOTEXTATPOINT, VWPTR *PSOTEXTATPOINT;

typedef struct SOTEXTATARCANGLEtag
{
SOARCINFO ArcInfo;
WORD wFormat;
SHORT nTextLength;
} SOTEXTATARCANGLE, VWPTR *PSOTEXTATARCANGLE;

/* Text Alignment Options */
#define SOTA_NOUPDATECP   0x0000
#define SOTA_UPDATECP  0x0001
#define SOTA_LEFT 0x0000
#define SOTA_RIGHT 0x0002
#define SOTA_CENTER0x0006
#define SOTA_TOP0x0000
#define SOTA_BOTTOM0x0008
#define SOTA_BASELINE  0x0018

typedef structSOSPOLYINFOtag
{
WORD wFormat;
SHORT nPoints;
} SOPOLYINFO, VWPTR *PSOPOLYINFO;

/* Poly object types */
#define SOPT_POLYLINE1
#define SOPT_POLYGON2
#define SOPT_BEZIEROPEN 3
#define SOPT_BEZIERCLOSE4
#define SOPT_SPLINEOPEN 5
#define SOPT_SPLINECLOSE6
#define SOPT_CPPOLYLINE 7/* Current Point polys */
#define SOPT_CPPOLYGON8

/* Background mode values */
#define SOBK_TRANSPARENT1
#define SOBK_OPAQUE2

typedef structSOCPTEXTATPOINTtag
{
WORD wFormat;
SHORT nTextLength;
} SOCPTEXTATPOINT,VWPTR *PSOCPTEXTATPOINT;

typedef struct SOCPARCANGLEtag
{
SOPOINT Center;
SOANGLE SweepAngle;
} SOCPARCANGLE, VWPTR *PSOCPARCANGLE;

typedef struct SOCPPIEANGLEtag
{
SOPOINT Radius;
SOANGLE StartAngle;
SOANGLE SweepAngle;
} SOCPPIEANGLE, VWPTR *PSOCPPIEANGLE;

typedef struct SOTRANSFORMtag
{
WORD wTransformFlags;
SOPOINT Origin;
SHORT xOffset;
SHORT yOffset;
SORATIO xScale;
SORATIO yScale;
SORATIO xSkew;
SORATIO ySkew;
SOANGLE RotationAngle;
} SOTRANSFORM, VWPTR *PSOTRANSFORM;

/* Transformation flags */
#define SOTF_NOTRANSFORMBIT0
#define SOTF_ROTATEBIT1
#define SOTF_XOFFSETBIT2
#define SOTF_YOFFSETBIT3
#define SOTF_XSCALEBIT4
#define SOTF_YSCALEBIT5
#define SOTF_XSKEWBIT6
#define SOTF_YSKEWBIT7

/* Possible values for the clip mode */
#define SO_DONOTCLIP0
#define SO_CLIPTOPATH1

/* Possible values for the point relation attribute */
#define SOPR_ABSOLUTE0
#define SOPR_RELATIVE1

/* Possible values for the wDrawPathFlags */
#define SODP_STROKEBIT0
#define SODP_FILLBIT1

typedef struct SOGROUPINFOtag
{
WORD wStructSize;
SORECT  BoundingRect;
SHORT nTransforms;
} SOGROUPINFO, VWPTR *PSOGROUPINFO;

typedef struct SOPATHINFOtag
{
WORD wStructSize;
SORECT  BoundingRect;
SHORT nTransforms;
} SOPATHINFO, VWPTR *PSOPATHINFO;

#define SOFF_FIXUPBOUNDSBIT0

typedef struct SOFRAMEINFOtag
{
SORECT  BoundingRect;
WORD wFlags;
WORD OriginalWidth;
SOANGLE RotationAngle;
SOPOINT ReferencePoint;
} SOFRAMEINFO, VWPTR *PSOFRAMEINFO;

typedef struct SOMPARAINDENTStag
{
SHORT FirstLineIndent;
SHORT LeftIndent;
SHORT RightIndent;
} SOMPARAINDENTS, VWPTR *PSOMPARAINDENTS;

typedef struct SOMPARASPACINGtag
{
WORD LineSpaceAdjust;
WORD ParaSpaceAdjust;
} SOMPARASPACING, VWPTR *PSOMPARASPACING;

/* Maximum Points provided by a filter in one SOPOINTS object call */
#define SOMAXPOINTS128

/* SOVectorAttr Id's */
#define SO_SELECTFONT0x100
#define SO_SELECTPEN0x101
#define SO_SELECTBRUSH0x102
#define SO_POLYFILLMODE 0x103
#define SO_TEXTCHAREXTRA0x104
#define SO_DRAWMODE0x105
#define SO_TEXTCOLOR0x106
#define SO_BKMODE0x107
#define SO_BKCOLOR0x108
#define SO_OBJECTTRANSFORM0x109
#define SO_CLIPMODE0x10A
#define SO_POINTRELATION0x10B
#define SO_MPARAINDENT0x10C
#define SO_MPARAALIGN0x10D
#define SO_MPARASPACING 0x10E

/* SOVectorObject Id's */
#define SO_ARC0x300
#define SO_CHORD0x301
#define SO_TEXTINRECT0x302
#define SO_ELLIPSE0x303
#define SO_FLOODFILL0x304
#define SO_LINE 0x305
#define SO_PIE0x306
#define SO_STARTPOLY0x307
#define SO_ENDPOLY0x308
#define SO_RECTANGLE0x309
#define SO_BEGINPATH0x30A
#define SO_ENDPATH0x30B
#define SO_CLOSESUBPATH 0x30C
#define SO_DRAWPATH0x30D
#define SO_BEGINGROUP0x30E
#define SO_ENDGROUP0x30F
#define SO_ROUNDRECT0x310
#define SO_SETPIXEL0x311
#define SO_TEXTATPOINT0x312
#define SO_POINTS0x313
#define SO_ARCANGLE0x314
#define SO_CHORDANGLE0x315
#define SO_PIEANGLE0x316
#define SO_CPSET0x317
#define SO_CPLINE0x318
#define SO_CPRECTANGLE0x319
#define SO_CPTEXTATPOINT0x31A
#define SO_CPELLIPSE0x31B
#define SO_CPARCTRIPLE0x31C
#define SO_CPARCANGLE0x31D
#define SO_CPPIEANGLE0x31E
#define SO_ARCCLOCKWISE 0x31F
#define SO_ARCANGLECLOCKWISE0x320
#define SO_TEXTATARCANGLE0x321
#define SO_BEGINSYMBOL0x322
#define SO_ENDSYMBOL0x323
#define SO_BEGINTEXTFRAME0x324
#define SO_ENDTEXTFRAME 0x325
#define SO_TEXTINPARA0x326
#define SO_PARAEND0x327

#defineSO_ARCTRIPLE0x328
#defineSO_PIETRIPLE0x329
#defineSO_CHORDTRIPLE0x32A
#defineSO_ELLIPSERADII0x32B
#defineSO_ARCRADII0x32C
#defineSO_PIERADII0x32D
#defineSO_CHORDRADII0x32E

#define SO_VECTORENDOFCHUNK0xFFFF

/*
| SO Header info
*/
#define SOHDR_ARCNUMFILES1
#define SOHDR_ARCCREATEDATE2
#define SOHDR_ARCMODTIME3
#define SOHDR_ARCTYPE4
#define SOHDR_ARCOS5
#define SOHDR_ARCCOMMENT6
#define SOHDR_ARCVERSION7

/*
|  Bored-er definitions
*/

#define SO_BORDERSINGLE 0x0000// Default
#define SO_BORDERNONE0x0001
#define SO_BORDERDOUBLE 0x0002
#define SO_BORDERHAIRLINE0x0004
#define SO_BORDERTHICK0x0008
#define SO_BORDERSHADOW 0x0010
#define SO_BORDERDOTTED 0x0020
#define SO_BORDERPRIORITY0x0040

/*
| Table stuff.
*/
#define SO_MERGELEFT0x0001
#define SO_MERGERIGHT0x0002
#define SO_MERGEABOVE0x0004
#define SO_MERGEBELOW0x0008

typedef struct SOBORDERtag
{
WORD wWidth; // Width of border in twips
SOCOLORREF rgbColor; // Color of border
WORD wFlags; // Specifies the type of border and the edges to which it applies.

} SOBORDER, VWPTR * PSOBORDER;

typedef struct SOTABLECELLINFOtag
{
WORD wWidth; // Width of the cell, in twips
WORD wMerge; // Bitfield that indicates whether the cell should be merged with any of the cells around it.Possible flags are SO_MERGELEFT, SO_MERGERIGHT, SO_MERGEABOVE, and SO_MERGEBELOW.
WORD wShading; // Intensity value for background shading, in range of 0-255, where a value of zero indicates that there is no background shading.
SOBORDER LeftBorder; // Specifies left border
SOBORDER RightBorder; // Specifies right border
SOBORDER TopBorder; // Specifies top border
SOBORDER BottomBorder; // Specifies bottom border

} SOTABLECELLINFO, VWPTR * PSOTABLECELLINFO, HUGE * HPSOTABLECELLINFO;

/*
| SOTAB structure
*/

typedef struct SOTABtag
{
WORD wType;
WORD wChar;
WORD wLeader;
DWORD dwOffset;
} SOTAB, VWPTR * PSOTAB;

/*
| SOFILTERINFO structure
*/

typedef struct SOFILTERINFOtag
{
WORD wFilterType;
WORD wFilterCharSet;
CHAR szFilterName[32];
} SOFILTERINFO, VWPTR * PSOFILTERINFO;

/*
| SOCOLUMN structure
*/

typedef struct SOCOLUMNtag
{
WORD wStructSize;
LONG dwWidth;
CHAR szName[40];
} SOCOLUMN, VWPTR * PSOCOLUMN;

/*
| SOFIELD structure
*/

typedef struct SOFIELDtag
{
WORD wStructSize;
LONG dwWidth;
CHAR szName[40];
WORD wStorage;
WORD wDisplay;
DWORD dwSubDisplay;
WORD wPrecision;
WORD wAlignment;
} SOFIELD, VWPTR * PSOFIELD;

/*
| SOTEXTCELL structure
*/

typedef struct SOTEXTCELLtag
{
WORD wStructSize;
WORD wAlignment;
WORD wAttribute;
} SOTEXTCELL, VWPTR * PSOTEXTCELL;

/*
| SODATACELL structure
*/

typedef union SONUMBERUNIONtag
{
SOINT32S  Int32S;
SOINT32U  Int32U;
BYTE  IEEE4[4];
BYTE  IEEE8[8];
BYTE  IEEE10[10];
BYTE  BCD8[8];
} SONUMBERUNION, VWPTR * PSONUMBERUNION;

typedef struct SODATACELLtag
{
WORD wStructSize;
WORD wStorage;
WORD wDisplay;
DWORD dwSubDisplay;
WORD wPrecision;
WORD wAlignment;
WORD wAttribute;
/*
union
{
SOINT32S  Int32S;
SOINT32U  Int32U;
BYTE  IEEE4[4];
BYTE  IEEE8[8];
BYTE  IEEE10[10];
BYTE  BCD8[8];
} uStorage;
*/
SONUMBERUNIONuStorage;
} SODATACELL, VWPTR * PSODATACELL;

typedef VOID FAR * SOFILE;

/* Bitmap graphics header */
typedef struct tagSOBITMAPHEADER
{
WORD wStructSize;
WORD wImageFlags;
WORD wImageWidth;
WORD wImageLength;
WORD wTileWidth;
WORD wTileLength;
WORD wBitsPerPixel;
WORD wNPlanes;
WORD wHDpi;
WORD wVDpi;
/**
WORD wEncoding;
WORD wImageLeft;
WORD wImageTop;
WORD wHScreenSize;
WORD wVScreenSize;
**/
}SOBITMAPHEADER, VWPTR * PSOBITMAPHEADER;

/*
|Structure defined for the DO special function for ARCHIVE formats
*/

typedef struct tagSODOSPECIALARC
{
SOFILE  hOutFile;
HANDLE  hFileName;
LPSTR lpFileName;
WORD wOptFlags;
WORD wRecordNum;
DWORD dwIndex;
} SODOSPECIALARC, VWPTR * PSODOSPECIALARC;

/*
 |
 | Embedded objects
 |
*/

#define SO_MAINTAINASPECT0x0001// image aspect ratio is preserved
#define SO_CENTERIMAGE0x0002// if set, image is centered in its final rectangle.

#define SOOBJECT_GRAPHIC0x0001
#define SOOBJECT_OLE0x0002
#define SOOBJECT_GRAPHIC_AND_OLE0x0003

#define SOOBJECT_OLESTATIC0x0001

typedef struct SOOBJECTLOCtag
{
BOOL bLink;
BYTE szFile[144];
DWORD dwOffset;
DWORD dwLength;
} SOOBJECTLOC, VWPTR * PSOOBJECTLOC;

typedef struct SOGRAPHICtag
{
WORD wId;
DWORD dwOrgHeight;
DWORD dwOrgWidth;
LONG lCropTop;
LONG lCropLeft;
LONG lCropBottom;
LONG lCropRight;
DWORD dwFinalHeight;
DWORD dwFinalWidth;
SOBORDER soLeftBorder;
SOBORDER soTopBorder;
SOBORDER soBottomBorder;
SOBORDER soRightBorder;
DWORD dwFlags;
} SOGRAPHIC, VWPTR * PSOGRAPHIC;

typedef struct SOGRAPHICOBJECTtag
{
WORD wStructSize;
DWORD dwFlags;
DWORD dwType;
SOGRAPHIC soGraphic;
SOOBJECTLOC soGraphicLoc;
SOOBJECTLOC soOLELoc;
} SOGRAPHICOBJECT, VWPTR * PSOGRAPHICOBJECT;

#define SOPUTCHAR1
#define SOPUTCHARX2
#define SOPUTSPECIALCHARX3
#define SOPUTCHARATTR4
#define SOPUTCHARHEIGHT 5
#define SOPUTPARAALIGN6
#define SOPUTPARAINDENTS7
#define SOSTARTTABSTOPS 8
#define SOENDTABSTOPS9
#define SOPUTTABSTOP10
#define SOPUTMARGINS11
#define SOPUTSUBDOCINFO 12
#define SOPUTBREAK13
#define SOPUTDATACELL14
#define SOPUTTEXTCELL15
#define SOPUTMORETEXT16
#define SOPUTFIELD17
#define SOPUTVARFIELD18
#define SOPUTMOREVARFIELD19
#define SOSTARTCOLUMNINFO20
#define SOSTARTFIELDINFO21
#define SOPUTCOLUMNINFO 22
#define SOPUTFIELDINFO23
#define SOENDCOLUMNINFO 24
#define SOENDFIELDINFO25
#define SOPUTSECTIONTYPE26
#define SOPUTSECTIONNAME27
#define SOSETDATEBASE28
#define SOBAILOUT29
#define SOTAGBEGIN30
#define SOTAGEND31
#define SOGETINFO32
#define SOPUTSTRING33
#define SUUSERSAVEDATA34
#define SUUSERRETRIEVEDATA35
#define SOPUTBITMAPHEADER36
#define SOSTARTPALETTE37
#define SOPUTPALETTEENTRY38
#define SOENDPALETTE39
#define SOPUTSCANLINEDATA40
#define SOSTARTFONTTABLE41
#define SOPUTFONTTABLEENTRY42
#define SOENDFONTTABLE43
#define SOPUTCHARFONTBYID44
#define SOPUTCHARFONTBYNAME45
#define SOPUTPARASPACING46
#define SOPUTARCHIVEFIELD47
#define SOSTARTHDRINFO48
#define SOENDHDRINFO49
#define SOPUTHDRENTRY50
#define SOPUTVECTORHEADER51
#define SOVECTOROBJECT52
#define SOVECTORATTR53
#define SOBEGINTABLE54
#define SOENDTABLE55
#define SOPUTTABLEROWFORMAT56
#define SOPUTTABLECELLINFO57
#define SOPUTGRAPHICOBJECT58
#define SOGOTOPOSITION59
#define SODRAWLINE60

typedef struct SORTNStag
{
VOID (SO_ENTRYMOD * SOPutChar    )(WORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutCharX      )(WORD, WORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutSpecialCharX )(WORD, WORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutCharAttr     )(WORD, WORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutCharHeight   )(WORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutParaAlign    )(WORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutParaIndents  )(DWORD, DWORD, DWORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOStartTabStops   )(DWORD, DWORD);
VOID (SO_ENTRYMOD * SOEndTabStops     )(DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutTabStop     )(PSOTAB, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutMargins     )(DWORD, DWORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutSubdocInfo   )(WORD, WORD, DWORD, DWORD);
WORD (SO_ENTRYMOD * SOPutBreak      )(WORD, DWORD, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutTextCell     )(PSOTEXTCELL,WORD,CHAR VWPTR *,WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutMoreText     )(WORD,CHAR VWPTR *,WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutDataCell     )(PSODATACELL,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutField      )(VOID VWPTR *,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutVarField     )(VOID VWPTR *,WORD,WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutMoreVarField )(VOID VWPTR *,WORD,WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOStartColumnInfo )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOStartFieldInfo  )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutColumnInfo   )(PSOCOLUMN,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutFieldInfo    )(PSOFIELD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOEndColumnInfo   )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOEndFieldInfo    )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutSectionType  )(WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutSectionName  )(CHAR VWPTR *,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOSetDateBase     )(DWORD,WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOBailOut    )(WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOTagBegin      )(DWORD,DWORD,DWORD);

VOID (SO_ENTRYMOD * SOTagEnd  )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOGetInfo    )(WORD,VOID VWPTR *,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutString     )(LPSTR, WORD, DWORD, DWORD);
WORD (SO_ENTRYMOD * SUUserSaveData    )(VOID VWPTR *,DWORD,DWORD);
WORD (SO_ENTRYMOD * SUUserRetrieveData)(WORD,VOID VWPTR *,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutBitmapHeader )(PSOBITMAPHEADER, DWORD, DWORD);
VOID (SO_ENTRYMOD * SOStartPalette    )(DWORD, DWORD);
VOID (SO_ENTRYMOD * SOPutPaletteEntry )(CHAR,CHAR,CHAR,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOEndPalette     )(DWORD,DWORD);
WORD (SO_ENTRYMOD * SOPutScanLineData )(VOID VWPTR *,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOStartFontTable )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutFontTableEntry)(DWORD,WORD,LPSTR,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOEndFontTable )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutCharFontById )(DWORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutCharFontByName)(WORD,LPSTR,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutParaSpacing )(WORD,DWORD,DWORD,DWORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutArchiveField )(WORD,WORD,LPSTR,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOStartHdrInfo )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOEndHdrInfo )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutHdrEntry )(LPSTR,LPSTR,WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutVectorHeader )(PSOVECTORHEADER,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOVectorObject )(SHORT,WORD,VOID VWPTR *,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOVectorAttr )(SHORT,WORD,VOID VWPTR *,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOBeginTable )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOEndTable  )(DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutTableRowFormat)(LONG,WORD,WORD,WORD,WORD,WORD,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutTableCellInfo  )(PSOTABLECELLINFO,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOPutGraphicObject  )(PSOGRAPHICOBJECT,DWORD,DWORD);
VOID (SO_ENTRYMOD * SOGoToPosition )(PSOPAGEPOSITION,DWORD,DWORD);
VOID (SO_ENTRYMOD * SODrawLine  )(PSOPAGEPOSITION,SOCOLORREF,WORD,DWORD,DWORD,DWORD,DWORD);
} SORTNS;

#endif /*SO_DEFS.H*/


VS_ASC.C

#include "vsp_asc.h"
#include "vsctop.h"
#include "vs_asc.pro"

#define EOF (-1)

/************************** ROUTINES *****************************************/

/******************************************************************************
*ASC_INIT      *
*Initialize the data union data structure   *
******************************************************************************/
VW_ENTRYSC  SHORT  VW_ENTRYMODVwStreamOpenFunc (fp, FileId, FileName, FilterInfo, hProc)
SOFILEfp;
SHORTFileId;
BYTE VWPTR*FileName;
SOFILTERINFOVWPTR*FilterInfo;
HPROChProc;
{
charchread[10];
SHORTch;
SHORTchCount;
SHORTdone;
SHORTlocText;

if ( FilterInfo != NULL )
{
FilterInfo->wFilterCharSet = SO_PC;
strcpy ( FilterInfo->szFilterName, VwStreamIdName[0].FileDescription );
}

Proc.AscSave.SeekSpot = 0L;
Proc.AscSave.lastchar = 0;
Proc.bFileIsText = FALSE;

Proc.hFile = _lopen ( (LPCSTR) FileName, OF_READ | OF_SHARE_DENY_WRITE );
if (Proc.hFile == HFILE_ERROR)
return -1;

_llseek (Proc.hFile, 0L, 0);

chCount= 0;
done= FALSE;
locText= TRUE;

do {
if (chCount >= 512)
break;

if (0==_lread (Proc.hFile, chread, 1) )
ch=EOF;
else
ch=chread[0];

chCount ++;
switch (ch)
{
case EOF:
done=TRUE;
break;

case 0x0d:// CR
case 0x08:// Backspace
case 0x0a:// NL
case 0x09:// TAB
case 0x0c:// NEW PAGE
case 0x1a:   // ASCII EOF
case 0x00:// NULL
case 0xc6:// 850 - a tilde
case 0xc7: // 850 - A tilde
break;

default:
if ( (ch>=0x20) && (ch<=0x7F) )
break;
if ( ch >= 0x80 && ch <= 0xA5 )
break;
if ( ch >= 0xD0 && ch <= 0xD8 )
break;
if ( ch >= 0xE0 && ch <= 0xEF )
break;
if ( ch >= 0xB5 && ch <= 0xB7 )
break;
locText = FALSE;
done = TRUE;
}

}while (done == FALSE);

if (locText)
Proc.bFileIsText = TRUE;

_llseek (Proc.hFile, 0L, 0);

return ( 0 );
}

VW_ENTRYSC VOID VW_ENTRYMOD VwStreamCloseFunc(SOFILE hFile, HPROC hProc)
{
_lclose ( Proc.hFile );
}

VW_ENTRYSC SHORT VW_ENTRYMOD VwStreamSeekFunc(hFile,hProc)
SOFILEhFile;
HPROChProc;
{
return((SHORT)_llseek(Proc.hFile,Proc.VwStreamSaveName.SeekSpot,0));
}

VW_ENTRYSC SHORT VW_ENTRYMOD VwStreamTellFunc(hFile,hProc)
SOFILEhFile;
HPROChProc;
{
Proc.VwStreamSaveName.SeekSpot = _llseek(Proc.hFile,0L, 1);
return(0);
}

/******************************************************************************
*ASC_SECTION_FUNC      *
******************************************************************************/
VW_ENTRYSC  SHORT  VW_ENTRYMODVwStreamSectionFunc (fp, hProc)
SOFILEfp;
HPROChProc;
{
SOPutSectionType ( SO_PARAGRAPHS, hProc );
return(0);
}

VW_ENTRYSC  SHORT  VW_ENTRYMODVwStreamReadFunc (fp, hProc)
SOFILEfp;
HPROChProc;
{
SHORTch;
char  chread[10];
SHORTchCount;
SHORTchTabs;
SHORTtype, done;

type = SO_PARABREAK;

do {
chCount= 0;
chTabs= 0;
done= FALSE;

do {
if (0==_lread (Proc.hFile, chread, 1) )
ch=EOF;
else
ch=chread[0];

if ((ch >= 0x20) && (ch <= 0x7e))
{
chCount++;
SOPutChar ( ch, hProc );
}
else if (ch == 0x09)
{
chCount++;
chTabs++;
SOPutSpecialCharX ( SO_CHTAB, SO_COUNT, hProc );
}
else if (ch == 0x0c)
{
chCount++;
SOPutSpecialCharX ( SO_CHHPAGE, SO_COUNT, hProc );
}
else if (ch == EOF)
{
type = SO_EOFBREAK;
done = TRUE;
}
else if (ch == 0x0a)
{
if (Proc.AscSave.lastchar != 0x0d)
{
done = TRUE;
}
else
{
ch = 0x00;
}
}
else if (ch == 0x0d)
{
if (Proc.AscSave.lastchar != 0x0a)
{
done = TRUE;
}
else
{
ch = 0x00;
}
}
else if (Proc.bFileIsText)
{
// Put out logical extended TEXT characters ...
if ( ( ch >= 0x80 && ch <= 0xA5 ) ||
  ( ch >= 0xD0 && ch <= 0xD8 ) ||
  ( ch >= 0xB5 && ch <= 0xB7 ) ||
  ( ch == 0xC6) ||
  ( ch == 0xC7) ||
  ( ch >= 0xE0 && ch <= 0xEF ) )

{
chCount++;
SOPutChar ( ch, hProc );
}
}

if (chCount > 2048 || chTabs > 10)
done = TRUE;

Proc.AscSave.lastchar = ch;

} while ( !done );

} while ( SOPutBreak (type, (LONG) NULL, hProc) != SO_STOP );

return ( 0 );

}  /** end of file **/


VS_ASC.H

typedefstructview_asc_save
{
LONGSeekSpot;
// DWORDdwNumLines;
SHORTlastchar;

} VIEW_ASC_SAVE;

typedefstructview_asc_data
{
VIEW_ASC_SAVEAscSave;
SHORThFile;
BOOLbFileIsText;
} VIEW_ASC_DATA;


VSCTOP.H

#ifdef WIN32
#define WINDOWS
#endif

#ifdef _WINDOWS /* MS C 7.0 predefined */
#ifndef WIN32
#define WINDOWS
#define WIN16
#endif
#endif

#ifdef WINDOWS
#pragma message("Windows")
#endif

#ifdef WIN16
#pragma message("Win16")
#endif

#ifdef WIN32
#pragma message("Win32")
#endif

/*
| Include windows and scc definitions
*/

#include "entry.h"
#include "stdlib.h"
#include <windows.h>

typedef signed char  *LPCHAR;
typedef signed short  *LPSHORT;

typedef DWORD HIOFILE;
#define HUGE

#define xseek _llseek
#define xtell _ltell
#define xopen _lopen
#define xclose _lclose
#define xread  _lread

// #include "sccfi.h"

/*
|
|   Typedef for FI information and get structure
|
*/

typedef struct FIINFOtag
   {
   WORD      fiId;/* FI Id */
   WORD      fiFlags;    /* flags for the Id */
   BYTE      fiName[26];    /* name of the Id, for example "WordPerfect 5.0" */
   } FIINFO;

typedef struct FIGETSTRUCTtag
   {
   HANDLE   gsRes;
   WORD     gsIndex;
   } FIGETSTRUCT;

#include "sodefs.h"

#ifdef WINDOWS
#define DECLSOVOID (SO_ENTRYMOD *)()
#endif /*WINDOWS*/

/*
|Include viewer's data types and typedef HPROC
*/

#include VwInclude

typedef struct PROCtag
{
VwStreamDynamicTypeVwData;
SORTNSSoRtns;
DWORDUser1;
DWORDUser2;
LONGlTellOffset;
} NEAR * HPROC;

#include "vwdefs.h"

/*
|Do functions prototypes for Vw functions
*/

externFILTER_DESCVwStreamIdName[];

#ifdef VwStreamStaticType
externVwStreamStaticType VwStreamStaticName;
#endif

#ifdef VwStreamOpenFunc
#undef VwStreamOpenFunc
#define VwStreamOpenFuncVwStreamOpen
VW_ENTRYSC SHORT VW_ENTRYMODVwStreamOpenFunc(SOFILE, SHORT, BYTE VWPTR *, SOFILTERINFO VWPTR * ,HPROC);
#endif

#ifdef VwStreamCloseFunc

#undef VwStreamCloseFunc
#define VwStreamCloseFuncVwStreamClose
VW_ENTRYSC VOID VW_ENTRYMODVwStreamCloseFunc(SOFILE, HPROC);

#else

#undef VwStreamCloseFunc
#define VwStreamCloseFuncVwStreamClose
VW_ENTRYSC VOID VW_ENTRYMODVwStreamCloseFunc(SOFILE, HPROC);
VW_ENTRYSC VOID VW_ENTRYMOD VwStreamCloseFunc(hFile,hProc)
SOFILEhFile;
HPROChProc;
{
}

#endif

#ifdef VwStreamSectionFunc
#undef VwStreamSectionFunc
#define VwStreamSectionFuncVwStreamSection
VW_ENTRYSC SHORT VW_ENTRYMODVwStreamSectionFunc(SOFILE, HPROC);
#endif

#ifdef VwStreamReadFunc
#undef VwStreamReadFunc
#define VwStreamReadFuncVwStreamRead
VW_ENTRYSC SHORT VW_ENTRYMODVwStreamReadFunc(SOFILE, HPROC);
#endif

#ifdef VwStreamReadRecordFunc

#undef VwStreamReadRecordFunc
#define VwStreamReadRecordFuncVwStreamReadRecord
VW_ENTRYSC VOID VW_ENTRYMODVwStreamReadRecordFunc(SOFILE, DWORD, HPROC);

#else

#define VwStreamReadRecordFuncVwStreamReadRecord
VW_ENTRYSC VOID VW_ENTRYMOD    VwStreamReadRecordFunc(SOFILE, DWORD, HPROC);
VW_ENTRYSC VOID VW_ENTRYMOD    VwStreamReadRecordFunc(hFile,dwData,hProc)
SOFILEhFile;
DWORDdwData;
HPROChProc;
{
}

#endif

#ifdef VwDoSpecialFunc

#undef VwDoSpecialFunc
#define VwDoSpecialFuncVwDoSpecial
VW_ENTRYSC VOID VW_ENTRYMODVwDoSpecialFunc(SOFILE,DWORD,DWORD,DWORD,DWORD,DWORD,HPROC);

#else

#define VwDoSpecialFuncVwDoSpecial
VW_ENTRYSC VOID VW_ENTRYMODVwDoSpecialFunc(SOFILE,DWORD,DWORD,DWORD,DWORD,DWORD,HPROC);
VW_ENTRYSC VOID VW_ENTRYMOD VwDoSpecialFunc(hFile, dw1,dw2,dw3,dw4,dw5,hProc)
SOFILEhFile;
DWORDdw1;
DWORDdw2;
DWORDdw3;
DWORDdw4;
DWORDdw5;
HPROChProc;
{
}
#endif

#ifdef VwStreamTellFunc
#undef VwStreamTellFunc
#define VwStreamTellFuncVwStreamTell
VW_ENTRYSC SHORT VW_ENTRYMODVwStreamTellFunc(SOFILE, HPROC);
#endif

#ifdef VwStreamSeekFunc
#undef VwStreamSeekFunc
#define VwStreamSeekFuncVwStreamSeek
VW_ENTRYSC SHORT VW_ENTRYMODVwStreamSeekFunc(SOFILE, HPROC);
#endif

#ifdef VwGetInfoFunc
#undef VwGetInfoFunc
#define VwGetInfoFuncVwGetInfo
VW_ENTRYSC BOOL VW_ENTRYMODVwGetInfoFunc(VOID VWPTR *, WORD, HPROC);
#endif

#ifdef VwGetRtnsFunc
#undef VwGetRtnsFunc
#define VwGetRtnsFuncVwGetRtns
VW_ENTRYSC VOID VW_ENTRYMOD VwGetRtnsFunc(VWRTNS VWPTR *, WORD);
#endif

#ifdef VwGetDataFunc
#undef VwGetDataFunc
#define VwGetDataFuncVwGetData
VW_ENTRYSC VOID VW_ENTRYMODVwGetDataFunc(VwStreamSaveType VWPTR *, HPROC);
#endif

#ifdef VwSetDataFunc
#undef VwSetDataFunc
#define VwSetDataFuncVwSetData
VW_ENTRYSC VOID VW_ENTRYMODVwSetDataFunc(VwStreamSaveType VWPTR *, HPROC);
#endif

#ifdef VwStreamSectionType

#ifdef VwGetSectionDataFunc
#undef VwGetSectionDataFunc
#define VwGetSectionDataFuncVwGetSectionData
VW_ENTRYSC VOID VW_ENTRYMODVwGetSectionDataFunc(VwStreamSectionType VWPTR *, HPROC);
#endif

#ifdef VwSetSectionDataFunc
#undef VwSetSectionDataFunc
#define VwSetSectionDataFuncVwSetSectionData
VW_ENTRYSC VOID VW_ENTRYMODVwSetSectionDataFunc(VwStreamSectionType VWPTR *, HPROC);
#endif

#else

#ifdef VwGetSectionDataFunc
#undef VwGetSectionDataFunc
#endif
#define VwGetSectionDataFuncVwGetSectionData
VW_ENTRYSC VOID VW_ENTRYMODVwGetSectionDataFunc(VOID VWPTR *, HPROC);

#ifdef VwSetSectionDataFunc
#undef VwSetSectionDataFunc
#endif
#define VwSetSectionDataFuncVwSetSectionData
VW_ENTRYSC VOID VW_ENTRYMODVwSetSectionDataFunc(VOID VWPTR *, HPROC);

#endif

#ifdef VwLocalUpFunc
#undef VwLocalUpFunc
#define VwLocalUpFuncVwLocalUp
VW_ENTRYSC VOID VW_ENTRYMODVwLocalUpFunc(HIOFILE, SOFILE VWPTR *, HPROC);
#endif

#ifdef VwLocalDownFunc
#undef VwLocalDownFunc
#define VwLocalDownFuncVwLocalDown
VW_ENTRYSC VOID VW_ENTRYMODVwLocalDownFunc(SOFILE, HIOFILE VWPTR *, HPROC);
#endif

#ifdef VwAllocProcFunc
#undef VwAllocProcFunc
#define VwAllocProcFuncVwAllocProc
VW_ENTRYSC HPROC VW_ENTRYMODVwAllocProcFunc(VOID);
#endif

#ifdef VwFreeProcFunc
#undef VwFreeProcFunc
#define VwFreeProcFuncVwFreeProc
VW_ENTRYSC VOID VW_ENTRYMODVwFreeProcFunc(HPROC);
#endif

#define VwSetUserFuncVwSetUser
VW_ENTRYSC VOID VW_ENTRYMOD VwSetUserFunc(DWORD,DWORD,HPROC);

#define VwSetSoRtnFuncVwSetSoRtn
VW_ENTRYSC VOID VW_ENTRYMOD VwSetSoRtnFunc(SHORT, DECLSO, HPROC);

#ifdef VwStreamStaticType
#define InitVwStreamStaticName
#endif

#define Proc (hProc->VwData)

/*
| Make some standard library functions available
*/

#ifdef WIN16

void far * far _cdecl _fmemmove(void far *, const void far *,unsigned int);
void far * far _cdecl _fmemcpy(void far *, const void far *,unsigned int);
void far * far _cdecl _fmemset(void far *, int, unsigned int);
int _cdecl atoi(const char *);
long _cdecl atol(const char *);
int Vatoi(char far *);
long Vatol(char far *);

#define strcmplstrcmp
#define strcmpilstrcmpi
#define strcpylstrcpy
#define strcatlstrcat
#define strlenlstrlen
#define memcpy_fmemcpy
#define memset_fmemset
#define memmove_fmemmove

int Vatoi(pStr)
char far * pStr;
{
static char locStr[20];

lstrcpy(locStr,pStr);
return(atoi(locStr));
}

long Vatol(pStr)
char far * pStr;
{
static char locStr[20];

lstrcpy(locStr,pStr);
return(atol(locStr));
}

#define atoi(s)Vatoi((char far *) s)
#define atol(s)Vatol((char far *) s)

#define itoa(num,str,x)_itoa(num,str,x)
#define ltoa(num,str,x)_ltoa(num,str,x)

#define SUAlloc(size,hProc)GlobalAlloc(GMEM_MOVEABLE,size)
#define SUFree(handle,hProc)GlobalFree(handle)
#define SULock(handle,hProc)((VOID FAR *) GlobalLock(handle))
#define SUUnlock(handle,hProc)GlobalUnlock(handle)
#define SUReAlloc(handle,size,hProc)GlobalReAlloc(handle,size,0)

#define SUSeekEntry(hF,hProc)

#endif /*WIN16*/

#ifdef WIN32

#define SUAlloc(size,hProc)GlobalAlloc(GMEM_MOVEABLE,size)
#define SUFree(handle,hProc)GlobalFree(handle)
#define SULock(handle,hProc)((VOID FAR *) GlobalLock(handle))
#define SUUnlock(handle,hProc)GlobalUnlock(handle)
#define SUReAlloc(handle,size,hProc)GlobalReAlloc(handle,size,0)

#define SUSeekEntry(hF,hProc)

#endif /*WIN32*/

#ifdef WINDOWS

VW_ENTRYSC HPROC VW_ENTRYMOD VwAllocProcFunc()
{
HANDLEhData;

if ((hData = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,sizeof(struct PROCtag))) == NULL)
return(NULL);
else
return((HPROC)LocalLock(hData));
}

VW_ENTRYSC VOID VW_ENTRYMOD VwFreeProcFunc(hProc)
HPROChProc;
{
HANDLEhData;

#ifdef WIN16
if ((hData = LocalHandle((WORD)(VOID NEAR *)hProc)) != NULL)
LocalFree(hData);
#endif /*WIN16*/

#ifdef WIN32
if ((hData = LocalHandle((LPVOID)hProc)) != NULL)
LocalFree(hData);
#endif /*WIN32*/

}

#endif /*WINDOWS*/

VW_ENTRYSC BOOL VW_ENTRYMOD VwGetInfoFunc(pInfo,wInfoId,hProc)
VOID VWPTR *pInfo;
WORDwInfoId;
HPROChProc;
{
BOOLlocRet;
WORDlocIndex;

locRet = TRUE;

switch (wInfoId)
{
case VWINFO_CHECK:
*(DWORD VWPTR *)pInfo = 0x12345678;
break;

case VWINFO_SAVESIZE:
#ifdef VwStreamSaveType
*(WORD VWPTR *)pInfo = sizeof(VwStreamSaveType);
#else
*(WORD VWPTR *)pInfo = 0;
#endif
break;

case VWINFO_DYNAMICSIZE:
#ifdef VwStreamDynamicType
*(WORD VWPTR *)pInfo = sizeof(VwStreamDynamicType);
#else
*(WORD VWPTR *)pInfo = 0;
#endif
break;

case VWINFO_STATICSIZE:
#ifdef VwStreamStaticType
*(WORD VWPTR *)pInfo = sizeof(VwStreamStaticType);
#else
*(WORD VWPTR *)pInfo = 0;
#endif
break;

case VWINFO_SPECLEVEL:
*(WORD VWPTR *)pInfo = 400;
break;

case VWINFO_FILTERLEVEL:
*(WORD VWPTR *)pInfo = 410;
break;

case VWINFO_IDCOUNT:
*(WORD VWPTR *)pInfo = VwStreamIdCount;
break;

case VWINFO_IDS:
for (locIndex = 0; locIndex < VwStreamIdCount; locIndex++)
{
((WORD VWPTR *)pInfo)[locIndex] = VwStreamIdName[locIndex].FilterId;
}
break;

case VWINFO_DATE:
strcpy((BYTE VWPTR *)pInfo,__DATE__);
break;

case VWINFO_TIME:
strcpy((BYTE VWPTR *)pInfo,__TIME__);
break;

case VWINFO_USERSAVESIZE:
#ifdef VwStreamUserSaveType
*(WORD VWPTR *)pInfo = sizeof(VwStreamUserSaveType);
#else
*(WORD VWPTR *)pInfo = 0;
#endif
break;

case VWINFO_SECTIONSAVESIZE:
#ifdef VwStreamSectionType
*(WORD VWPTR *)pInfo = sizeof(VwStreamSectionType);
#else
*(WORD VWPTR *)pInfo = 0;
#endif
break;

default:
locRet = FALSE;
break;
}

return(locRet);
}

VW_ENTRYSC VOID VW_ENTRYMOD VwGetDataFunc(pSaveData,hProc)
VwStreamSaveType VWPTR *pSaveData;
HPROChProc;
{
*pSaveData = Proc.VwStreamSaveName;
}

VW_ENTRYSC VOID VW_ENTRYMOD VwSetDataFunc(pSaveData,hProc)
VwStreamSaveType VWPTR *pSaveData;
HPROChProc;
{
Proc.VwStreamSaveName = *pSaveData;
}

#ifdef VwStreamSectionType

VW_ENTRYSC VOID VW_ENTRYMOD VwGetSectionDataFunc(pSectionData,hProc)
VwStreamSectionType VWPTR *pSectionData;
HPROChProc;
{
*pSectionData = Proc.VwStreamSectionName;
}

VW_ENTRYSC VOID VW_ENTRYMOD VwSetSectionDataFunc(pSectionData,hProc)
VwStreamSectionType VWPTR *pSectionData;
HPROChProc;
{
Proc.VwStreamSectionName = *pSectionData;
}

#else

VW_ENTRYSC VOID VW_ENTRYMOD VwGetSectionDataFunc(pSectionData,hProc)
VOID VWPTR *pSectionData;
HPROChProc;
{
}

VW_ENTRYSC VOID VW_ENTRYMOD VwSetSectionDataFunc(pSectionData,hProc)
VOID VWPTR *pSectionData;
HPROChProc;
{
}

#endif

VOID FAR SODummy()
{
}

VW_ENTRYSC VOID VW_ENTRYMOD VwLocalUpFunc(hFile,phFile,hProc)
HIOFILEhFile;
SOFILE VWPTR *phFile;
HPROChProc;
{
*phFile = (SOFILE)hFile;

#ifdef WINDOWS
#define CAST(VOID FAR *)
#define CAST2(VOID FAR *)
#endif /*WINDOWS*/

hProc->SoRtns.SOPutChar = CAST SODummy;
hProc->SoRtns.SOPutCharX = CAST SODummy;
hProc->SoRtns.SOPutSpecialCharX = CAST SODummy;
hProc->SoRtns.SOPutCharAttr = CAST SODummy;
hProc->SoRtns.SOPutCharHeight = CAST SODummy;
hProc->SoRtns.SOPutParaAlign = CAST SODummy;
hProc->SoRtns.SOPutParaIndents = CAST SODummy;
hProc->SoRtns.SOStartTabStops = CAST SODummy;
hProc->SoRtns.SOEndTabStops = CAST SODummy;
hProc->SoRtns.SOPutTabStop = CAST SODummy;
hProc->SoRtns.SOPutMargins = CAST SODummy;
hProc->SoRtns.SOPutSubdocInfo = CAST SODummy;
hProc->SoRtns.SOPutBreak = CAST2 SODummy;
hProc->SoRtns.SOPutDataCell = CAST SODummy;
hProc->SoRtns.SOPutTextCell = CAST SODummy;
hProc->SoRtns.SOPutMoreText = CAST SODummy;
hProc->SoRtns.SOPutField = CAST SODummy;
hProc->SoRtns.SOPutVarField = CAST SODummy;
hProc->SoRtns.SOPutMoreVarField = CAST SODummy;
hProc->SoRtns.SOStartColumnInfo = CAST SODummy;
hProc->SoRtns.SOStartFieldInfo = CAST SODummy;
hProc->SoRtns.SOPutColumnInfo = CAST SODummy;
hProc->SoRtns.SOPutFieldInfo = CAST SODummy;
hProc->SoRtns.SOEndColumnInfo = CAST SODummy;
hProc->SoRtns.SOEndFieldInfo = CAST SODummy;
hProc->SoRtns.SOPutSectionType = CAST SODummy;
hProc->SoRtns.SOPutSectionName = CAST SODummy;
hProc->SoRtns.SOSetDateBase = CAST SODummy;
hProc->SoRtns.SOBailOut = CAST SODummy;
hProc->SoRtns.SOTagBegin = CAST SODummy;
hProc->SoRtns.SOTagEnd = CAST SODummy;
hProc->SoRtns.SOGetInfo = CAST SODummy;
hProc->SoRtns.SOPutString = CAST SODummy;
hProc->SoRtns.SUUserSaveData = CAST2 SODummy;
hProc->SoRtns.SUUserRetrieveData = CAST2 SODummy;
hProc->SoRtns.SOPutBitmapHeader  = CAST SODummy;
hProc->SoRtns.SOStartPalette  = CAST SODummy;
hProc->SoRtns.SOPutPaletteEntry  = CAST SODummy;
hProc->SoRtns.SOEndPalette = CAST SODummy;
hProc->SoRtns.SOPutScanLineData = CAST2 SODummy;
hProc->SoRtns.SOStartFontTable = CAST SODummy;
hProc->SoRtns.SOPutFontTableEntry = CAST SODummy;
hProc->SoRtns.SOEndFontTable = CAST SODummy;
hProc->SoRtns.SOPutCharFontById = CAST SODummy;
hProc->SoRtns.SOPutCharFontByName = CAST SODummy;
hProc->SoRtns.SOPutParaSpacing = CAST SODummy;
hProc->SoRtns.SOPutArchiveField = CAST SODummy;
hProc->SoRtns.SOStartHdrInfo = CAST SODummy;
hProc->SoRtns.SOEndHdrInfo = CAST SODummy;
hProc->SoRtns.SOPutHdrEntry = CAST SODummy;
hProc->SoRtns.SOPutVectorHeader = CAST SODummy;
hProc->SoRtns.SOVectorObject = CAST SODummy;
hProc->SoRtns.SOVectorAttr = CAST SODummy;
hProc->SoRtns.SOBeginTable = CAST SODummy;
hProc->SoRtns.SOEndTable = CAST SODummy;
hProc->SoRtns.SOPutTableRowFormat = CAST SODummy;
hProc->SoRtns.SOPutTableCellInfo = CAST SODummy;
hProc->SoRtns.SOPutGraphicObject = CAST SODummy;
hProc->SoRtns.SOGoToPosition = CAST SODummy;
hProc->SoRtns.SODrawLine = CAST SODummy;

#undef CAST
}

VW_ENTRYSC VOID VW_ENTRYMOD VwLocalDownFunc(hFile,phFile,hProc)
SOFILEhFile;
HIOFILE VWPTR *phFile;
HPROChProc;
{
*phFile = (HIOFILE)hFile;
}

#ifdef WINDOWS
#define CAST(VOID FAR *)
#define CAST2(VOID FAR *)
#define CASTIO(VOID FAR *)
#define SO_PFUNCSO_ENTRYMOD *
#endif /*WINDOWS*/

VW_ENTRYSC VOID VW_ENTRYMOD VwSetSoRtnFunc(wRtn,pRtn,hProc)
SHORTwRtn;
VOID(SO_PFUNC pRtn)();
HPROChProc;
{
if (pRtn == NULL)
pRtn = SODummy;

switch (wRtn)
{
case SOPUTCHAR:
hProc->SoRtns.SOPutChar = CAST pRtn;
break;
case SOPUTCHARX:
hProc->SoRtns.SOPutCharX = CAST pRtn;
break;
case SOPUTSPECIALCHARX:
hProc->SoRtns.SOPutSpecialCharX = CAST pRtn;
break;
case SOPUTCHARATTR:
hProc->SoRtns.SOPutCharAttr = CAST pRtn;
break;
case SOPUTCHARHEIGHT:
hProc->SoRtns.SOPutCharHeight = CAST pRtn;
break;
case SOPUTPARAALIGN:
hProc->SoRtns.SOPutParaAlign = CAST pRtn;
break;
case SOPUTPARAINDENTS:
hProc->SoRtns.SOPutParaIndents = CAST pRtn;
break;
case SOSTARTTABSTOPS:
hProc->SoRtns.SOStartTabStops = CAST pRtn;
break;
case SOENDTABSTOPS:
hProc->SoRtns.SOEndTabStops = CAST pRtn;
break;
case SOPUTTABSTOP:
hProc->SoRtns.SOPutTabStop = CAST pRtn;
break;
case SOPUTMARGINS:
hProc->SoRtns.SOPutMargins = CAST pRtn;
break;
case SOPUTSUBDOCINFO:
hProc->SoRtns.SOPutSubdocInfo = CAST pRtn;
break;
case SOPUTBREAK:
hProc->SoRtns.SOPutBreak = CAST2 pRtn;
break;
caseSOPUTDATACELL:
hProc->SoRtns.SOPutDataCell = CAST pRtn;
break;
caseSOPUTTEXTCELL:
hProc->SoRtns.SOPutTextCell = CAST pRtn;
break;
caseSOPUTMORETEXT:
hProc->SoRtns.SOPutMoreText = CAST pRtn;
break;
caseSOPUTFIELD:
hProc->SoRtns.SOPutField = CAST pRtn;
break;
caseSOPUTVARFIELD:
hProc->SoRtns.SOPutVarField = CAST pRtn;
break;
caseSOPUTMOREVARFIELD:
hProc->SoRtns.SOPutMoreVarField = CAST pRtn;
break;
caseSOSTARTCOLUMNINFO:
hProc->SoRtns.SOStartColumnInfo = CAST pRtn;
break;
caseSOSTARTFIELDINFO:
hProc->SoRtns.SOStartFieldInfo = CAST pRtn;
break;
caseSOPUTCOLUMNINFO:
hProc->SoRtns.SOPutColumnInfo = CAST pRtn;
break;
caseSOPUTFIELDINFO:
hProc->SoRtns.SOPutFieldInfo = CAST pRtn;
break;
caseSOENDCOLUMNINFO:
hProc->SoRtns.SOEndColumnInfo = CAST pRtn;
break;
caseSOENDFIELDINFO:
hProc->SoRtns.SOEndFieldInfo = CAST pRtn;
break;
caseSOPUTSECTIONTYPE:
hProc->SoRtns.SOPutSectionType = CAST pRtn;
break;
caseSOPUTSECTIONNAME:
hProc->SoRtns.SOPutSectionName = CAST pRtn;
break;
caseSOSETDATEBASE:
hProc->SoRtns.SOSetDateBase = CAST pRtn;
break;
caseSOBAILOUT:
hProc->SoRtns.SOBailOut = CAST pRtn;
break;
caseSOTAGBEGIN:
hProc->SoRtns.SOTagBegin = CAST pRtn;
break;
caseSOTAGEND:
hProc->SoRtns.SOTagEnd = CAST pRtn;
break;
caseSOGETINFO:
hProc->SoRtns.SOGetInfo = CAST pRtn;
break;
caseSOPUTSTRING:
hProc->SoRtns.SOPutString = CAST pRtn;
break;
caseSUUSERSAVEDATA:
hProc->SoRtns.SUUserSaveData = CAST2 pRtn;
break;
caseSUUSERRETRIEVEDATA:
hProc->SoRtns.SUUserRetrieveData = CAST2 pRtn;
break;
caseSOPUTBITMAPHEADER:
hProc->SoRtns.SOPutBitmapHeader = CAST pRtn;
break;
caseSOSTARTPALETTE:
hProc->SoRtns.SOStartPalette  = CAST pRtn;
break;
caseSOPUTPALETTEENTRY:
hProc->SoRtns.SOPutPaletteEntry = CAST pRtn;
break;
caseSOENDPALETTE:
hProc->SoRtns.SOEndPalette = CAST pRtn;
break;
caseSOPUTSCANLINEDATA:
hProc->SoRtns.SOPutScanLineData = CAST2 pRtn;
break;
caseSOSTARTFONTTABLE:
hProc->SoRtns.SOStartFontTable = CAST pRtn;
break;
caseSOPUTFONTTABLEENTRY:
hProc->SoRtns.SOPutFontTableEntry = CAST pRtn;
break;
caseSOENDFONTTABLE:
hProc->SoRtns.SOEndFontTable = CAST pRtn;
break;
caseSOPUTCHARFONTBYID:
hProc->SoRtns.SOPutCharFontById = CAST pRtn;
break;
caseSOPUTCHARFONTBYNAME:
hProc->SoRtns.SOPutCharFontByName = CAST pRtn;
break;
caseSOPUTPARASPACING:
hProc->SoRtns.SOPutParaSpacing = CAST pRtn;
break;
caseSOPUTARCHIVEFIELD:
hProc->SoRtns.SOPutArchiveField = CAST pRtn;
break;
caseSOSTARTHDRINFO:
hProc->SoRtns.SOStartHdrInfo = CAST pRtn;
break;
caseSOENDHDRINFO:
hProc->SoRtns.SOEndHdrInfo = CAST pRtn;
break;
caseSOPUTHDRENTRY:
hProc->SoRtns.SOPutHdrEntry = CAST pRtn;
break;
caseSOPUTVECTORHEADER:
hProc->SoRtns.SOPutVectorHeader = CAST pRtn;
break;
caseSOVECTOROBJECT:
hProc->SoRtns.SOVectorObject = CAST pRtn;
break;
caseSOVECTORATTR:
hProc->SoRtns.SOVectorAttr = CAST pRtn;
break;
case SOBEGINTABLE:
hProc->SoRtns.SOBeginTable = CAST pRtn;
break;
case SOENDTABLE:
hProc->SoRtns.SOEndTable = CAST pRtn;
break;
case SOPUTTABLEROWFORMAT:
hProc->SoRtns.SOPutTableRowFormat = CAST pRtn;
break;
case SOPUTTABLECELLINFO:
hProc->SoRtns.SOPutTableCellInfo = CAST pRtn;
break;
case SOPUTGRAPHICOBJECT:
hProc->SoRtns.SOPutGraphicObject = CAST pRtn;
break;
case SOGOTOPOSITION:
hProc->SoRtns.SOGoToPosition = CAST pRtn;
break;
case SODRAWLINE:
hProc->SoRtns.SODrawLine = CAST pRtn;
break;
}

#undef CAST
#undef CAST2
#undef CASTIO
}

VW_ENTRYSC VOID VW_ENTRYMOD VwSetUserFunc(dwUser1,dwUser2,hProc)
DWORDdwUser1;
DWORDdwUser2;
HPROChProc;
{
hProc->User1 = dwUser1;
hProc->User2 = dwUser2;
}

VW_ENTRYSC VOID VW_ENTRYMOD VwGetRtnsFunc(pRtns,wLastRtn)
VWRTNS VWPTR * pRtns;
WORD  wLastRtn;
{

#ifdef WINDOWS
#define FUNCTIONNAME(func) func
#define CAST(VOID FAR *)
#define CASTHPROC (VOID FAR *)
#endif /*WINDOWS*/

#ifdef VwStreamOpenFunc
pRtns->StreamOpen = FUNCTIONNAME(VwStreamOpen);
#else
pRtns->StreamOpen = NULL;
#endif

#ifdef VwStreamSectionFunc
pRtns->StreamSection = FUNCTIONNAME(VwStreamSection);
#else
pRtns->StreamSection = NULL;
#endif

#ifdef VwStreamReadFunc
pRtns->StreamRead = FUNCTIONNAME(VwStreamRead);
#else
pRtns->StreamRead = NULL;
#endif

#ifdef VwStreamTellFunc
pRtns->StreamTell = FUNCTIONNAME(VwStreamTell);
#else
pRtns->StreamTell = NULL;
#endif

#ifdef VwStreamSeekFunc
pRtns->StreamSeek= FUNCTIONNAME(VwStreamSeek);
#else
pRtns->StreamSeek= NULL;
#endif

#ifdef VwGetInfoFunc
pRtns->GetInfo = FUNCTIONNAME(VwGetInfo);
#else
pRtns->GetInfo = NULL;
#endif

#ifdef VwGetDataFunc
pRtns->GetData = CAST FUNCTIONNAME(VwGetData);
#else
pRtns->GetData = NULL;
#endif

#ifdef VwSetDataFunc
pRtns->SetData = CAST FUNCTIONNAME(VwSetData);
#else
pRtns->SetData = NULL;
#endif

#ifdef VwLocalUpFunc
pRtns->LocalUp = FUNCTIONNAME(VwLocalUp);
#else
pRtns->LocalUp = NULL;
#endif

#ifdef VwLocalDownFunc
pRtns->LocalDown = FUNCTIONNAME(VwLocalDown);
#else
pRtns->LocalDown = NULL;
#endif

#ifdef VwAllocProcFunc
pRtns->AllocProc =  CASTHPROC FUNCTIONNAME(VwAllocProc);
#else
pRtns->AllocProc = NULL;
#endif

#ifdef VwFreeProcFunc
pRtns->FreeProc = FUNCTIONNAME(VwFreeProc);
#else
pRtns->FreeProc = NULL;
#endif

#ifdef VwSetUserFunc
pRtns->SetUser = FUNCTIONNAME(VwSetUser);
#else
pRtns->SetUser = NULL;
#endif

#ifdef VwSetSoRtnFunc
pRtns->SetSoRtn = FUNCTIONNAME(VwSetSoRtn);
#else
pRtns->SetSoRtn = NULL;
#endif

#ifdef VwStreamCloseFunc
pRtns->StreamClose = FUNCTIONNAME(VwStreamClose);
#else
pRtns->StreamClose = NULL;
#endif

#ifdef VwStreamSectionType
#ifdef VwGetSectionDataFunc
pRtns->GetSectionData = CAST FUNCTIONNAME(VwGetSectionData);
#else
pRtns->GetSectionData = NULL;
#endif
#else
pRtns->GetSectionData = NULL;
#endif

#ifdef VwStreamSectionType
#ifdef VwSetSectionDataFunc
pRtns->SetSectionData = CAST FUNCTIONNAME(VwSetSectionData);
#else
pRtns->SetSectionData = NULL;
#endif
#else
pRtns->SetSectionData = NULL;
#endif

#ifdef VwStreamReadRecordFunc
pRtns->StreamReadRecord = FUNCTIONNAME(VwStreamReadRecord);
#else
pRtns->StreamReadRecord = NULL;
#endif

if (wLastRtn >= VWRTN_DOSPECIAL)
{
#ifdef VwDoSpecialFunc
pRtns->DoSpecial = FUNCTIONNAME(VwDoSpecial);
#else
pRtns->DoSpecial = NULL;
#endif
}

#undef CAST
#undef FUNCTIONNAME
#undef CASTHPROC
}

/*
| Filter macro for bitmap filters
*/

#defineSetRGBPixel(pDest,pRGB){*(BYTE VWPTR *)pDest++=((BYTE VWPTR *)pRGB)[2];*(BYTE VWPTR *)pDest++=((BYTE VWPTR *)pRGB)[1];*(BYTE VWPTR *)pDest++=*((BYTE VWPTR *)pRGB);}

/*
|SO routine macros
*/

#define SOPutChar(a,hP)      (hP->SoRtns.SOPutChar)(a,hP->User1,hP->User2)
#define SOPutCharX(a,b,hP) (hP->SoRtns.SOPutCharX)(a,b,hP->User1,hP->User2)
#define SOPutSpecialCharX(a,b,hP)     (hP->SoRtns.SOPutSpecialCharX)(a,b,hP->User1,hP->User2)
#define SOPutCharAttr(a,b,hP)     (hP->SoRtns.SOPutCharAttr)(a,b,hP->User1,hP->User2)
#define SOPutCharHeight(a,hP)     (hP->SoRtns.SOPutCharHeight)(a,hP->User1,hP->User2)
#define SOPutParaAlign(a,hP)   (hP->SoRtns.SOPutParaAlign)(a,hP->User1,hP->User2)
#define SOPutParaIndents(a,b,c,hP)(hP->SoRtns.SOPutParaIndents)(a,b,c,hP->User1,hP->User2)
#define SOStartTabStops(hP)(hP->SoRtns.SOStartTabStops)(hP->User1,hP->User2)
#define SOEndTabStops(hP)(hP->SoRtns.SOEndTabStops)(hP->User1,hP->User2)
#define SOPutTabStop(a,hP) (hP->SoRtns.SOPutTabStop)(a,hP->User1,hP->User2)
#define SOPutMargins(a,b,hP)   (hP->SoRtns.SOPutMargins)(a,b,hP->User1,hP->User2)
#define SOPutBreak(a,b,hP) (hP->SoRtns.SOPutBreak)(a,(DWORD)(b),hP->User1,hP->User2)
#define SOPutSubdocInfo(a,b,hP)      (hP->SoRtns.SOPutSubdocInfo)(a,b,hP->User1,hP->User2)
#define SOPutTextCell(a,b,c,d,hP)     (hP->SoRtns.SOPutTextCell)(a,b,c,d,hP->User1,hP->User2)
#define SOPutMoreText(a,b,c,hP)      (hP->SoRtns.SOPutMoreText)(a,b,c,hP->User1,hP->User2)
#define SOPutDataCell(a,hP)(hP->SoRtns.SOPutDataCell)(a,hP->User1,hP->User2)
#define SOPutField(a,hP)     (hP->SoRtns.SOPutField)(a,hP->User1,hP->User2)
#define SOPutVarField(a,b,c,hP)      (hP->SoRtns.SOPutVarField)(a,b,c,hP->User1,hP->User2)
#define SOPutMoreVarField(a,b,c,hP) (hP->SoRtns.SOPutMoreVarField)(a,b,c,hP->User1,hP->User2)
#define SOStartColumnInfo(hP)     (hP->SoRtns.SOStartColumnInfo)(hP->User1,hP->User2)
#define SOStartFieldInfo(hP)   (hP->SoRtns.SOStartFieldInfo)(hP->User1,hP->User2)
#define SOPutColumnInfo(a,hP)     (hP->SoRtns.SOPutColumnInfo)(a,hP->User1,hP->User2)
#define SOPutFieldInfo(a,hP)   (hP->SoRtns.SOPutFieldInfo)(a,hP->User1,hP->User2)
#define SOEndColumnInfo(hP)(hP->SoRtns.SOEndColumnInfo)(hP->User1,hP->User2)
#define SOEndFieldInfo(hP) (hP->SoRtns.SOEndFieldInfo)(hP->User1,hP->User2)
#define SOPutSectionType(a,hP)     (hP->SoRtns.SOPutSectionType)(a,hP->User1,hP->User2)
#define SOPutSectionName(a,hP)     (hP->SoRtns.SOPutSectionName)(a,hP->User1,hP->User2)
#define SOSetDateBase(a,b,hP)     (hP->SoRtns.SOSetDateBase)(a,b,hP->User1,hP->User2)
#define SOBailOut(a,hP)      (hP->SoRtns.SOBailOut)(a,hP->User1,hP->User2)
#define SOTagBegin(a,hP)     (hP->SoRtns.SOTagBegin)(a,hP->User1,hP->User2)
#define SOTagEnd(hP)     (hP->SoRtns.SOTagEnd)(hP->User1,hP->User2)
#define SOGetInfo(a,b,hP)(hP->SoRtns.SOGetInfo)(a,b,hP->User1,hP->User2)
#define SUUserSaveData(a,hP)   (hP->SoRtns.SUUserSaveData)(a,hP->User1,hP->User2)
#define SUUserRetrieveData(a,b,hP)(hP->SoRtns.SUUserRetrieveData)(a,b,hP->User1,hP->User2)
#define SOPutBitmapHeader(a,hP)      (hP->SoRtns.SOPutBitmapHeader)(a,hP->User1,hP->User2)
#define SOStartPalette(hP) (hP->SoRtns.SOStartPalette)(hP->User1,hP->User2)
#define SOPutPaletteEntry(a,b,c,hP) (hP->SoRtns.SOPutPaletteEntry)(a,b,c,hP->User1,hP->User2)
#define SOEndPalette(hP)     (hP->SoRtns.SOEndPalette)(hP->User1,hP->User2)
#define SOPutScanLineData(a,hP)      (hP->SoRtns.SOPutScanLineData)(a,hP->User1,hP->User2)
#define SOPutCharFontById(a,hP)      (hP->SoRtns.SOPutCharFontById)(a,hP->User1,hP->User2)
#define SOPutCharFontByName(a,b,hP) (hP->SoRtns.SOPutCharFontByName)(a,b,hP->User1,hP->User2)
#define SOPutParaSpacing(a,b,c,d,hP)(hP->SoRtns.SOPutParaSpacing)(a,b,c,d,hP->User1,hP->User2)
#define SOStartFontTable(hP)   (hP->SoRtns.SOStartFontTable)(hP->User1,hP->User2)
#define SOPutFontTableEntry(a,b,c,hP)   (hP->SoRtns.SOPutFontTableEntry)(a,b,c,hP->User1,hP->User2)
#define SOEndFontTable(hP) (hP->SoRtns.SOEndFontTable)(hP->User1,hP->User2)
#define SOPutArchiveField(a,b,c,hP) (hP->SoRtns.SOPutArchiveField)(a,b,c,hP->User1,hP->User2)
#define SOStartHdrInfo(hP) (hP->SoRtns.SOStartHdrInfo)(hP->User1,hP->User2)
#define SOEndHdrInfo(hP)     (hP->SoRtns.SOEndHdrInfo)(hP->User1,hP->User2)
#define SOPutHdrEntry(a,b,c,hP)      (hP->SoRtns.SOPutHdrEntry)(a,b,c,hP->User1,hP->User2)
#define SOPutVectorHeader(a,hP)      (hP->SoRtns.SOPutVectorHeader)(a,hP->User1,hP->User2)
#define SOVectorObject(a,b,c,hP)     (hP->SoRtns.SOVectorObject)(a,b,c,hP->User1,hP->User2)
#define SOVectorAttr(a,b,c,hP)     (hP->SoRtns.SOVectorAttr)(a,b,c,hP->User1,hP->User2)
#define SOBeginTable(hP)     (hP->SoRtns.SOBeginTable)(hP->User1,hP->User2)
#define SOEndTable(hP)     (hP->SoRtns.SOEndTable)(hP->User1,hP->User2)
#define SOPutTableRowFormat(a,b,c,d,e,f,hP)  (hP->SoRtns.SOPutTableRowFormat)(a,b,c,d,e,f,hP->User1,hP->User2)
#define SOPutTableCellInfo(a,hP)     (hP->SoRtns.SOPutTableCellInfo)(a,hP->User1,hP->User2)
#define SOPutGraphicObject(a,hP)     (hP->SoRtns.SOPutGraphicObject)(a,hP->User1,hP->User2)
#define SOGoToPosition(a,hP)   (hP->SoRtns.SOGoToPosition)(a,hP->User1,hP->User2)
#define SODrawLine(a,b,c,d,e,hP)     (hP->SoRtns.SODrawLine)(a,b,c,d,e,hP->User1,hP->User2)


VSD_ASC.C

#include "vsp_asc.h"
#include "vsdtop.h"

#ifndef VW_SEPARATE_DATA
SHORTNTypesPerFilter = 1;
#endif

#define FI_EXTERNAL1997// Always use this particular value.

FILTER_DESCVwStreamIdName[VwStreamIdCount] =
{
{ FI_EXTERNAL, SO_WORDPROCESSOR, "Your Format Name Here!", },
};

#ifndef VW_SEPARATE_DATA
SHORTInitDataOffset = 0; /*sizeof (VwStreamDynamicType) - sizeof (VwStreamStaticType);*/
#endif

/*
VwStreamStaticType VwStreamStaticName =
{
};
*/

#ifndef VW_SEPARATE_DATA
charFillerData [ sizeof ( union all_structs ) /*- sizeof ( VwStreamStaticType )*/ + 2 ] = {0};
#endif


VSDTOP.H

#ifdef WIN32
#define WINDOWS
#endif

#ifdef _WINDOWS /* MS C 7.0 predefined */
#ifndef WIN32
#define WINDOWS
#define WIN16
#endif
#endif

#ifdef WINDOWS
#pragma message("Windows")
#endif

#ifdef WIN16
#pragma message("Win16")
#endif

#ifdef WIN32
#pragma message("Win32")
#endif

/*
| Include windows and scc definitions
*/

#include "entry.h"
#include <stdlib.h>
#include <windows.h>

typedef signed char  *LPCHAR;
typedef signed short  *LPSHORT;

typedef DWORD HIOFILE;

/*
|
|   Typedef for FI information and get structure
|
*/

typedef struct FIINFOtag
   {
   WORD      fiId;/* FI Id */
   WORD      fiFlags;    /* flags for the Id */
   BYTE      fiName[26];    /* name of the Id, for example "WordPerfect 5.0" */
   } FIINFO;

typedef struct FIGETSTRUCTtag
   {
   HANDLE   gsRes;
   WORD     gsIndex;
   } FIGETSTRUCT;

__declspec(dllexport) WORD __cdecl FIIdFile(DWORD,LPSTR,DWORD,WORD  *);
__declspec(dllexport) WORD __cdecl FIIdHandle(HIOFILE,WORD  *);
__declspec(dllexport) void __cdecl FIGetFirstId(FIGETSTRUCT  *, FIINFO  *);
__declspec(dllexport) void __cdecl FIGetNextId(FIGETSTRUCT  *, FIINFO  *);

#include "sodefs.h"

typedef void  * HPROC;

typedef struct fdesc
{
SHORT FilterId;
CHAR FilterType;
CHAR FileDescription[40];
} FILTER_DESC;

typedef struct VWINFOtag
{
DWORD VwCheck;
WORD VwSaveSize;
WORD VwDynamicSize;
WORD VwStaticSize;
WORD VwSpecLevel;
WORD VwFilterLevel;
WORD VwIdCount;
WORD VwIds[15];
BYTE VwDate[12];
BYTE VwTime[10];
WORD VwUserSaveSize;
} VWINFO;

typedef struct VWRTNStag
{
SHORT (__cdecl * StreamOpen)(SOFILE, SHORT, BYTE  *, SOFILTERINFO  * ,HPROC);
SHORT (__cdecl * StreamSection)(SOFILE,HPROC);
SHORT (__cdecl * StreamRead)(SOFILE, HPROC);
SHORT (__cdecl * StreamTell)(SOFILE, HPROC);
SHORT (__cdecl * StreamSeek)(SOFILE, HPROC);
BOOL (__cdecl * GetInfo)(void  *, WORD, HPROC);
void (__cdecl * GetData)(void  *, HPROC);
void (__cdecl * SetData)(void  *, HPROC);
void (__cdecl * LocalUp)(HIOFILE, SOFILE  *, HPROC);
void (__cdecl * LocalDown)(SOFILE, HIOFILE  *, HPROC);
HPROC (__cdecl * AllocProc)(void);
void (__cdecl * FreeProc)(HPROC);
void (__cdecl * SetUser)(DWORD,DWORD,HPROC);
void (__cdecl * SetSoRtn)(SHORT, void (__cdecl *)(), HPROC);
void (__cdecl * StreamClose)(SOFILE, HPROC);
void (__cdecl * GetSectionData)(void  *, HPROC);
void (__cdecl * SetSectionData)(void  *, HPROC);
void (__cdecl * StreamReadRecord)(SOFILE, DWORD, HPROC);
void (__cdecl * DoSpecial)(SOFILE, DWORD, DWORD, DWORD, DWORD, DWORD, HPROC);
} VWRTNS;


VSP_ASC.H

/* #define VwStreamStaticTypeVIEW_ASC_INIT */
#define VwStreamDynamicTypeVIEW_ASC_DATA
#define VwStreamSaveTypeVIEW_ASC_SAVE

/* #define VwStreamStaticNameAscInit */
#define VwStreamDynamicNameAscData
#define VwStreamSaveNameAscSave

#define VwStreamIdNameVwAscOverlayId
#define VwStreamIdCount1

/* #define VwStreamUserSaveTypeVIEW_ASC_USER_SAVE */
#define VwStreamGenSeekNameSeekSpot

#define VwInclude"vs_asc.h"

#define VwStreamOpenFuncasc_stream_open
#define VwStreamCloseFuncasc_stream_close
#define VwStreamSeekFuncasc_stream_seek
#define VwStreamTellFuncasc_stream_tell
#define VwStreamReadFuncasc_stream_read
#define VwStreamSectionFuncasc_stream_section

#define VwGetInfoFuncasc_getinfo
#define VwGetRtnsFuncasc_getrtns
#define VwGetDataFuncasc_getdata
#define VwSetDataFuncasc_setdata
#define VwAllocProcFuncasc_alloc_proc
#define VwFreeProcFuncasc_free_proc
#define VwLocalUpFuncasc_local_up
#define VwLocalDownFuncasc_local_down


VWDEFS.H

#ifndef VWDEFS_H
#define VWDEFS_H

typedef struct fdesc
{
SHORT FilterId;
CHAR FilterType;
CHAR FileDescription[40];
} FILTER_DESC;

typedef struct VWINFOtag
{
DWORD VwCheck;
WORD VwSaveSize;
WORD VwDynamicSize;
WORD VwStaticSize;
WORD VwSpecLevel;
WORD VwFilterLevel;
WORD VwIdCount;
WORD VwIds[15];
BYTE VwDate[12];
BYTE VwTime[10];
WORD VwUserSaveSize;
} VWINFO;

#define VWINFO_SAVESIZE 1/* WORD */
#define VWINFO_DYNAMICSIZE2/* WORD */
#define VWINFO_STATICSIZE3/* WORD */
#define VWINFO_SPECLEVEL4/* WORD */
#define VWINFO_FILTERLEVEL5/* WORD */
#define VWINFO_IDCOUNT6/* WORD */
#define VWINFO_IDS7/* Array of IDCOUNT WORDs */
#define VWINFO_DATE8/* string (max 12 bytes) */
#define VWINFO_TIME9/* string (max 10 bytes) */
#define VWINFO_USERSAVESIZE10/* WORD */
#define VWINFO_SECTIONSAVESIZE11/* WORD */
#define VWINFO_CHECK12/* DWORD */

#ifdef WINDOWS
#define VWFUNC_ELEMENTVW_ENTRYMOD *
#endif

typedef struct VWRTNStag
{
SHORT (VWFUNC_ELEMENT StreamOpen)(SOFILE, SHORT, BYTE VWPTR *, SOFILTERINFO VWPTR * ,HPROC);
SHORT (VWFUNC_ELEMENT StreamSection)(SOFILE,HPROC);
SHORT (VWFUNC_ELEMENT StreamRead)(SOFILE, HPROC);
SHORT (VWFUNC_ELEMENT StreamTell)(SOFILE, HPROC);
SHORT (VWFUNC_ELEMENT StreamSeek)(SOFILE, HPROC);
BOOL (VWFUNC_ELEMENT GetInfo)(VOID VWPTR *, WORD, HPROC);
VOID (VWFUNC_ELEMENT GetData)(VOID VWPTR *, HPROC);
VOID (VWFUNC_ELEMENT SetData)(VOID VWPTR *, HPROC);
VOID (VWFUNC_ELEMENT LocalUp)(HIOFILE, SOFILE VWPTR *, HPROC);
VOID (VWFUNC_ELEMENT LocalDown)(SOFILE, HIOFILE VWPTR *, HPROC);
HPROC (VWFUNC_ELEMENT AllocProc)(VOID);
VOID (VWFUNC_ELEMENT FreeProc)(HPROC);
VOID (VWFUNC_ELEMENT SetUser)(DWORD,DWORD,HPROC);
VOID (VWFUNC_ELEMENT SetSoRtn)(SHORT, VOID (SO_ENTRYMOD *)(), HPROC);
VOID (VWFUNC_ELEMENT StreamClose)(SOFILE, HPROC);
VOID (VWFUNC_ELEMENT GetSectionData)(VOID VWPTR *, HPROC);
VOID (VWFUNC_ELEMENT SetSectionData)(VOID VWPTR *, HPROC);
VOID (VWFUNC_ELEMENT StreamReadRecord)(SOFILE, DWORD, HPROC);
VOID (VWFUNC_ELEMENT DoSpecial)(SOFILE, DWORD, DWORD, DWORD, DWORD, DWORD, HPROC);
} VWRTNS;

#define VWRTN_STREAMOPEN1
#define VWRTN_STREAMSECTION2
#define VWRTN_STREAMREAD3
#define VWRTN_STREAMTELL4
#define VWRTN_STREAMSEEK5
#define VWRTN_GETINFO6
#define VWRTN_GETDATA7
#define VWRTN_SETDATA8
#define VWRTN_LOCALUP9
#define VWRTN_LOCALDOWN 10
#define VWRTN_ALLOCPROC 11
#define VWRTN_FREEPROC12
#define VWRTN_SETUSER13
#define VWRTN_SETSORTN14
#define VWRTN_STREAMCLOSE15
#define VWRTN_GETSECTIONDATA16
#define VWRTN_SETSECTIONDATA17
#define VWRTN_STREAMREADRECORD18
#define VWRTN_DOSPECIAL 19

/*
| Error returns on the Open routine
*/

#define VWERR_OK0/* file is OK */
#define VWERR_BADFILE-2/* corrupt or unreadable file */
#define VWERR_EMPTYFILE -3/* empty file */
#define VWERR_PROTECTEDFILE-4/* password protected or encrypted file */
#define VWERR_SUPFILEOPENFAILS-5/* secondary file open failed */
#define VWERR_ALLOCFAILS-6/* memory allocation fails */
#define VWERR_TYPENOTSUPPORTED-7/* this flavor of the file type is not supported */

#endif /*VWDEFS_H*/


CLSFACT.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ClsFact.cpp

   Description:   Implements CClassFactory.

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "ClsFact.h"

/**************************************************************************
   private function prototypes
**************************************************************************/

/**************************************************************************
   global variables
**************************************************************************/

///////////////////////////////////////////////////////////////////////////
//
// IClassFactory implementation
//

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

   CClassFactory::CClassFactory

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

CClassFactory::CClassFactory()
{
m_ObjRefCount = 1;
g_DllRefCount++;
}

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

   CClassFactory::~CClassFactory

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

CClassFactory::~CClassFactory()
{
g_DllRefCount--;
}

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

   CClassFactory::QueryInterface

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

STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

else if(IsEqualIID(riid, IID_IClassFactory))
   {
   *ppReturn = (IClassFactory*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CClassFactory::AddRef

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

STDMETHODIMP_(DWORD) CClassFactory::AddRef()
{
return ++m_ObjRefCount;
}

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

   CClassFactory::Release

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

STDMETHODIMP_(DWORD) CClassFactory::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

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

   CClassFactory::CreateInstance

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

STDMETHODIMP CClassFactory::CreateInstance(  LPUNKNOWN pUnknown,
                                             REFIID riid,
                                             LPVOID *ppObject)
{
*ppObject = NULL;

if(pUnknown != NULL)
   return CLASS_E_NOAGGREGATION;

//add implementation specific code here

CShellFolder *pShellFolder = new CShellFolder(NULL, NULL);
if(NULL == pShellFolder)
   return E_OUTOFMEMORY;

//get the QueryInterface return for our return value
HRESULT hResult = pShellFolder->QueryInterface(riid, ppObject);

//call Release to decement the ref count
pShellFolder->Release();

//return the result from QueryInterface
return hResult;
}

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

   CClassFactory::LockServer

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

STDMETHODIMP CClassFactory::LockServer(BOOL)
{
return E_NOTIMPL;
}


CLSFACT.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ClsFact.h

   Description:   CClassFactory definitions.

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

#ifndef CLASSFACTORY_H
#define CLASSFACTORY_H

#include <windows.h>
#include "ShlFldr.h"
#include "ShlView.h"

/**************************************************************************
   global variables
**************************************************************************/

extern HINSTANCE  g_hInst;

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

   CClassFactory class definition

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

class CClassFactory : public IClassFactory
{
protected:
   DWORD m_ObjRefCount;

public:
   CClassFactory();
   ~CClassFactory();

   //IUnknown methods
   STDMETHODIMP QueryInterface(REFIID, LPVOID*);
   STDMETHODIMP_(DWORD) AddRef();
   STDMETHODIMP_(DWORD) Release();

   //IClassFactory methods
   STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID*);
   STDMETHODIMP LockServer(BOOL);
};

#endif   //CLASSFACTORY_H


CONTMENU.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ContMenu.cpp

   Description:   CContextMenu implementation.

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "ContMenu.h"

/**************************************************************************
   global variables
**************************************************************************/

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

   CContextMenu::CContextMenu()

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

CContextMenu::CContextMenu(CShellFolder *pSFParent, LPCITEMIDLIST *aPidls, UINT uItemCount)
{
m_pSFParent = pSFParent;
if(m_pSFParent)
   m_pSFParent->AddRef();

m_ObjRefCount = 1;
g_DllRefCount++;

m_aPidls = NULL;
SHGetMalloc(&m_pMalloc);
if(!m_pMalloc)
   {
   delete this;
   return;
   }

m_pPidlMgr = new CPidlMgr();

AllocPidlTable(uItemCount);
if(m_aPidls)
   {
   FillPidlTable(aPidls, uItemCount);
   }

m_fAllValues = 1;
UINT  u;
for(u = 0; u < uItemCount; u++)
   {
   m_fAllValues &= (m_pPidlMgr->IsValue(aPidls[u]) ? 1 : 0);
   }
}

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

   CContextMenu::~CContextMenu()

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

CContextMenu::~CContextMenu()
{
if(m_pSFParent)
   m_pSFParent->Release();

g_DllRefCount--;

//make sure the pidl is freed
if(m_aPidls && m_pMalloc)
   {
   FreePidlTable();
   }

if(m_pPidlMgr)
   delete m_pPidlMgr;

if(m_pMalloc)
   m_pMalloc->Release();
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

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

   CContextMenu::QueryInterface

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

STDMETHODIMP CContextMenu::QueryInterface(   REFIID riid,
                                                   LPVOID FAR * ppReturn)
{
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = (LPUNKNOWN)(LPCONTEXTMENU)this;
   m_ObjRefCount++;
   return ResultFromScode(S_OK);
   }

if(IsEqualIID(riid, IID_IContextMenu))
   {
   *ppReturn = (LPCONTEXTMENU)this;
   m_ObjRefCount++;
   return ResultFromScode(S_OK);
   }

if(IsEqualIID(riid, IID_IShellExtInit))
   {
   *ppReturn = (LPSHELLEXTINIT)this;
   m_ObjRefCount++;
   return ResultFromScode(S_OK);
   }

*ppReturn = NULL;
return ResultFromScode(E_NOINTERFACE);
}

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

   CContextMenu::AddRef

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

STDMETHODIMP_(DWORD) CContextMenu::AddRef()
{
return ++m_ObjRefCount;
}

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

   CContextMenu::Release

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

STDMETHODIMP_(DWORD) CContextMenu::Release()
{
if(--m_ObjRefCount == 0)
   delete this;

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IContextMenu Implementation
//

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

   CContextMenu::QueryContextMenu()

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

STDMETHODIMP CContextMenu::QueryContextMenu( HMENU hMenu,
                                                   UINT indexMenu,
                                                   UINT idCmdFirst,
                                                   UINT idCmdLast,
                                                   UINT uFlags)
{
if(!(CMF_DEFAULTONLY & uFlags))
   {
   MENUITEMINFO   mii;

   if(!m_fAllValues)
      {
      ZeroMemory(&mii, sizeof(mii));
      mii.cbSize = sizeof(mii);
      mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
      mii.wID = idCmdFirst + IDM_EXPLORE;
      mii.fType = MFT_STRING;
      mii.dwTypeData = TEXT("&Explore");
      mii.fState = MFS_ENABLED | MFS_DEFAULT;
      InsertMenuItem(   hMenu,
                        indexMenu++,
                        TRUE,
                        &mii);

      ZeroMemory(&mii, sizeof(mii));
      mii.cbSize = sizeof(mii);
      mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
      mii.wID = idCmdFirst + IDM_OPEN;
      mii.fType = MFT_STRING;
      mii.dwTypeData = TEXT("&Open");
      mii.fState = MFS_ENABLED;
      InsertMenuItem(   hMenu,
                        indexMenu++,
                        TRUE,
                        &mii);
      }
   return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_LAST + 1));
   }

return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}

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

   CContextMenu::InvokeCommand()

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

STDMETHODIMP CContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
if(HIWORD(lpcmi->lpVerb))
   {
   //the command is being sent via a verb
   return NOERROR;
   }

if(LOWORD(lpcmi->lpVerb) > IDM_LAST)
   return ResultFromScode(E_INVALIDARG);

switch(LOWORD(lpcmi->lpVerb))
   {
   case IDM_EXPLORE:
   case IDM_OPEN:
      {
      LPITEMIDLIST      pidlFQ;
      SHELLEXECUTEINFO  sei;

      /*
      Find the first item in the list that is not a value. These commands
      should never be invoked if there isn't at least one key item in the list.
      */
      int   i;
      for(i = 0; m_aPidls[i]; i++)
         {
         if(!m_pPidlMgr->IsValue(m_aPidls[i]))
            break;
         }

      pidlFQ = m_pPidlMgr->Concatenate(m_pSFParent->m_pidlFQ, m_aPidls[i]);

      ZeroMemory(&sei, sizeof(sei));
      sei.cbSize = sizeof(sei);
      sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
      sei.lpIDList = pidlFQ;
      sei.lpClass = TEXT("folder");
      sei.hwnd = lpcmi->hwnd;

      if(LOWORD(lpcmi->lpVerb) == IDM_EXPLORE)
         sei.lpVerb = TEXT("explore");
      else
         sei.lpVerb = TEXT("open");

      ShellExecuteEx(&sei);

      m_pPidlMgr->Delete(pidlFQ);
      }
      break;

   }

return NOERROR;
}

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

   CContextMenu::GetCommandString()

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

STDMETHODIMP CContextMenu::GetCommandString( UINT idCommand,
                                                   UINT uFlags,
                                                   LPUINT lpReserved,
                                                   LPSTR lpszName,
                                                   UINT uMaxNameLen)
{
HRESULT  hr = E_INVALIDARG;

switch(uFlags)
   {
   case GCS_HELPTEXT:
      switch(idCommand)
         {
         case 0:
            lstrcpy(lpszName, "Display the file name.");
            hr = NOERROR;
            break;
         }
      break;

   case GCS_VERB:
      switch(idCommand)
         {
         case 0:
            lstrcpy(lpszName, "display");
            hr = NOERROR;
            break;
         }
      break;

   case GCS_VALIDATE:
      hr = NOERROR;
      break;
   }

return hr;
}

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

   CContextMenu::AllocPidlTable()

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

BOOL CContextMenu::AllocPidlTable(DWORD dwEntries)
{
//add one for NULL terminator
dwEntries++;

m_aPidls = (LPITEMIDLIST*)m_pMalloc->Alloc(dwEntries * sizeof(LPITEMIDLIST));

if(m_aPidls)
   {
   //set all of the entries to NULL
   ZeroMemory(m_aPidls, dwEntries * sizeof(LPITEMIDLIST));
   }

return (m_aPidls != NULL);
}

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

   CContextMenu::FreePidlTable()

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

void CContextMenu::FreePidlTable(void)
{
if(m_aPidls && m_pPidlMgr)
   {
   int   i;
   for(i = 0; m_aPidls[i]; i++)
      m_pPidlMgr->Delete(m_aPidls[i]);

   m_pMalloc->Free(m_aPidls);

   m_aPidls = NULL;
   }
}

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

   CContextMenu::FillPidlTable()

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

BOOL CContextMenu::FillPidlTable(LPCITEMIDLIST *aPidls, UINT uItemCount)
{
if(m_aPidls)
   {
   if(m_pPidlMgr)
      {
      UINT  i;
      for(i = 0; i < uItemCount; i++)
         {
         m_aPidls[i] = m_pPidlMgr->Copy(aPidls[i]);
         }
      return TRUE;
      }
   }

return FALSE;
}


CONTMENU.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ContMenu.h

   Description:   CContextMenu definitions.

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

#ifndef CONTMENU_H
#define CONTMENU_H

#include <windows.h>
#include <shlobj.h>
#include "ShlFldr.h"
#include "PidlMgr.h"

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

   CContextMenu class definition

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

class CContextMenu : public IContextMenu
{
protected:
   DWORD          m_ObjRefCount;
   LPITEMIDLIST   *m_aPidls;
   IMalloc        *m_pMalloc;
   CPidlMgr       *m_pPidlMgr;
   CShellFolder   *m_pSFParent;
   BOOL           m_fAllValues;

public:
   CContextMenu(CShellFolder*, LPCITEMIDLIST*, UINT);
   ~CContextMenu();

   //IUnknown methods
   STDMETHODIMP QueryInterface(REFIID, LPVOID FAR *);
   STDMETHODIMP_(DWORD) AddRef();
   STDMETHODIMP_(DWORD) Release();

   //IContextMenu methods
   STDMETHODIMP QueryContextMenu(HMENU, UINT, UINT, UINT, UINT);
   STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO);
   STDMETHODIMP GetCommandString(UINT, UINT, LPUINT, LPSTR, UINT);

private:
   BOOL AllocPidlTable(DWORD);
   void FreePidlTable(void);
   BOOL FillPidlTable(LPCITEMIDLIST*, UINT);
};

#define IDM_EXPLORE  0
#define IDM_OPEN     1

#define IDM_LAST     IDM_OPEN

#endif// CONTMENU_H


DOCKWNDW.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          DockWndw.cpp

   Description:   Implements CDockingWindow

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "DockWndw.h"

#if (_WIN32_IE >= 0x0400)

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

   CDockingWindow::CDockingWindow()

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

CDockingWindow::CDockingWindow(CShellView *pView, HWND hwndCommand)
{
m_pView = pView;
if(m_pView)
   {
   m_pView->AddRef();
   }
else
   {
   delete this;
   return;
   }

m_pSite = NULL;

m_hWnd = NULL;

m_hwndCommand = hwndCommand;

m_bFocus = FALSE;

m_rcDisplay.left = 0;
m_rcDisplay.top = 0;
m_rcDisplay.right = 0;
m_rcDisplay.bottom = 0;

m_ObjRefCount = 1;
g_DllRefCount++;
}

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

   CDockingWindow::~CDockingWindow()

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

CDockingWindow::~CDockingWindow()
{
if(m_pView)
   {
   m_pView->Release();
   }

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

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

   CDockingWindow::QueryInterface

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

STDMETHODIMP CDockingWindow::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IOleWindow
else if(IsEqualIID(riid, IID_IOleWindow))
   {
   *ppReturn = (IOleWindow*)this;
   }

//IDockingWindow
else if(IsEqualIID(riid, IID_IDockingWindow))
   {
   *ppReturn = (IDockingWindow*)this;
   }

//IInputObject
else if(IsEqualIID(riid, IID_IInputObject))
   {
   *ppReturn = (IInputObject*)this;
   }

//IObjectWithSite
else if(IsEqualIID(riid, IID_IObjectWithSite))
   {
   *ppReturn = (IObjectWithSite*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CDockingWindow::AddRef

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

STDMETHODIMP_(DWORD) CDockingWindow::AddRef()
{
return ++m_ObjRefCount;
}

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

   CDockingWindow::Release

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

STDMETHODIMP_(DWORD) CDockingWindow::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IOleWindow Implementation
//

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

   CDockingWindow::GetWindow()

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

STDMETHODIMP CDockingWindow::GetWindow(HWND *phWnd)
{
*phWnd = m_hWnd;

return S_OK;
}

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

   CDockingWindow::ContextSensitiveHelp()

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

STDMETHODIMP CDockingWindow::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IDockingWindow Implementation
//

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

   CDockingWindow::ShowDW()

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

STDMETHODIMP CDockingWindow::ShowDW(BOOL fShow)
{
//if our window doesn't exist yet, create it now
if(!m_hWnd && m_pSite)
   {
   HRESULT  hr;

   //if our window class has not been registered, then do so
   WNDCLASS wc;
   if(!GetClassInfo(g_hInst, SHTB_CLASS_NAME, &wc))
      {
      ZeroMemory(&wc, sizeof(wc));
      wc.style          = CS_HREDRAW | CS_VREDRAW;
      wc.lpfnWndProc    = (WNDPROC)WndProc;
      wc.cbClsExtra     = 0;
      wc.cbWndExtra     = 0;
      wc.hInstance      = g_hInst;
      wc.hIcon          = NULL;
      wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground  = (HBRUSH)CreateSolidBrush(RGB(192, 0, 0));
      wc.lpszMenuName   = NULL;
      wc.lpszClassName  = SHTB_CLASS_NAME;

      if(!RegisterClass(&wc))
         {
         //if this fails, CreateWindow will fail, so don't sweat it
         }
      }

   //get our parent window
   hr = m_pSite->GetWindow(&m_hwndParent);
   if(SUCCEEDED(hr))
      {
      RECT  rc;

      GetClientRect(m_hwndParent, &rc);

      //create the window - use zero for pos and size - it will get positioned and sized below
      CreateWindowEx(   WS_EX_CLIENTEDGE,
                        SHTB_CLASS_NAME,
                        NULL,
                        WS_CHILD | WS_CLIPSIBLINGS,
                        0,
                        0,
                        0,
                        0,
                        m_hwndParent,
                        NULL,
                        g_hInst,
                        (LPVOID)this);

      if(!m_hWnd)
         return E_FAIL;

      NegotiateBorderSpace(m_pSite);
      }
   }

if(m_hWnd && m_pSite)
   {
   BORDERWIDTHS   bwRequest = {0, 0, 0, 0};

   if(fShow)
      {
      //position the window - m_rcDisplay was set up in NegotiateBorderSpace
      MoveWindow( m_hWnd,
                  m_rcDisplay.left,
                  m_rcDisplay.top,
                  m_rcDisplay.right,
                  m_rcDisplay.bottom,
                  TRUE);

      //show our window
      ShowWindow(m_hWnd, SW_SHOW);
      }
   else
      {
      //hide our window
      ShowWindow(m_hWnd, SW_HIDE);

      //release our border space - bwRequest was initialized above
      m_pSite->SetBorderSpaceDW((IDockingWindow*)this, &bwRequest);
      }
   }

return S_OK;
}

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

   CDockingWindow::CloseDW()

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

STDMETHODIMP CDockingWindow::CloseDW(DWORD dwReserved)
{
if(m_pSite)
   {
   ShowDW(FALSE);

   if(IsWindow(m_hWnd))
      DestroyWindow(m_hWnd);

   m_pSite->Release();
   m_pSite = NULL;
   }

return S_OK;
}

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

   CDockingWindow::ResizeBorderDW()

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

STDMETHODIMP CDockingWindow::ResizeBorderDW(LPCRECT prcBorder, IUnknown* punkSite, BOOL fReserved)
{
IDockingWindowSite   *pSite = NULL;

//if punkSite is not NULL, use it to negotiate our border space
if(punkSite)
   {
   if(FAILED(punkSite->QueryInterface(IID_IDockingWindowSite, (LPVOID*)&pSite)))
      {
      return E_FAIL;
      }
   }
else if(m_pSite)
   {
   pSite = m_pSite;
   pSite->AddRef();
   }

if(pSite)
   {
   HRESULT  hr = E_FAIL;

   if(NegotiateBorderSpace(pSite, prcBorder))
      {
      //updated our window's position
      ShowDW(TRUE);
      hr = S_OK;
      }

   pSite->Release();
   return hr;
   }
return E_FAIL;
}

///////////////////////////////////////////////////////////////////////////
//
// IInputObject Implementation
//

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

   CDockingWindow::UIActivateIO()

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

STDMETHODIMP CDockingWindow::UIActivateIO(BOOL fActivate, LPMSG pMsg)
{
if(fActivate)
   SetFocus(m_hWnd);

return S_OK;
}

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

   CDockingWindow::HasFocusIO()

   If this window or one of its decendants has the focus, return S_OK. Return
   S_FALSE if we don't have the focus.

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

STDMETHODIMP CDockingWindow::HasFocusIO(void)
{
if(m_bFocus)
   return S_OK;

return S_FALSE;
}

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

   CDockingWindow::TranslateAcceleratorIO()

   If the accelerator is translated, return S_OK or S_FALSE otherwise.

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

STDMETHODIMP CDockingWindow::TranslateAcceleratorIO(LPMSG pMsg)
{
return S_FALSE;
}

///////////////////////////////////////////////////////////////////////////
//
// IObjectWithSite implementations
//

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

   CDockingWindow::SetSite()

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

STDMETHODIMP CDockingWindow::SetSite(IUnknown* punkSite)
{
if(m_pSite)
   {
   m_pSite->Release();
   m_pSite = NULL;
   }

//If punkSite is not NULL, we are connecting to the site.
if(punkSite)
   {
   if(SUCCEEDED(punkSite->QueryInterface(IID_IDockingWindowSite, (LPVOID*)&m_pSite)))
      {
      return S_OK;
      }
   }

return S_OK;
}

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

   CDockingWindow::GetSite()

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

STDMETHODIMP CDockingWindow::GetSite(REFIID riid, LPVOID *ppvReturn)
{
*ppvReturn = NULL;

if(m_pSite)
   return m_pSite->QueryInterface(riid, ppvReturn);

return E_FAIL;
}

///////////////////////////////////////////////////////////////////////////
//
// private method implementations
//

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

   CDockingWindow::WndProc()

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

LRESULT CALLBACK CDockingWindow::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
CDockingWindow  *pThis = (CDockingWindow*)GetWindowLong(hWnd, GWL_USERDATA);

switch (uMessage)
   {
   case WM_NCCREATE:
      {
      LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
      pThis = (CDockingWindow*)(lpcs->lpCreateParams);
      SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);

      //set the window handle
      pThis->m_hWnd = hWnd;
      }
      break;

   case WM_PAINT:
      return pThis->OnPaint();

   case WM_COMMAND:
      return pThis->OnCommand(wParam, lParam);

   case WM_SETFOCUS:
      return pThis->OnSetFocus();

   case WM_KILLFOCUS:
      return pThis->OnKillFocus();
   }

return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

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

   CDockingWindow::OnPaint()

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

LRESULT CDockingWindow::OnPaint(void)
{
PAINTSTRUCT ps;
RECT        rc;

BeginPaint(m_hWnd, &ps);

GetClientRect(m_hWnd, &rc);
SetTextColor(ps.hdc, RGB(0, 0, 0));
SetBkMode(ps.hdc, TRANSPARENT);
DrawText(ps.hdc, TEXT("IDockingWindow"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(m_hWnd, &ps);

return 0;
}

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

   CDockingWindow::OnCommand()

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

LRESULT CDockingWindow::OnCommand(WPARAM wParam, LPARAM lParam)
{
/*
Since this is a toolbar object, we need to forward all WM_COMMAND messages to
our assigned command window.
*/
return SendMessage(m_hwndCommand, WM_COMMAND, wParam, lParam);
}

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

   CDockingWindow::FocusChange()

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

void CDockingWindow::FocusChange(BOOL bFocus)
{
m_bFocus = bFocus;

//inform the input object site that the focus has changed
IInputObjectSite  *pios;

if(SUCCEEDED(m_pSite->QueryInterface(IID_IInputObjectSite, (LPVOID*)&pios)))
   {
   pios->OnFocusChangeIS((IDockingWindow*)this, bFocus);
   pios->Release();
   }
}

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

   CDockingWindow::OnSetFocus()

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

LRESULT CDockingWindow::OnSetFocus(void)
{
FocusChange(TRUE);

m_pView->OnActivate(SVUIA_ACTIVATE_FOCUS);

return 0;
}

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

   CDockingWindow::OnKillFocus()

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

LRESULT CDockingWindow::OnKillFocus(void)
{
FocusChange(FALSE);

m_pView->OnDeactivate();

return 0;
}

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

   CDockingWindow::NegotiateBorderSpace()

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

BOOL CDockingWindow::NegotiateBorderSpace(IDockingWindowSite *pSite, LPCRECT prcBorder)
{
HRESULT        hr;
BORDERWIDTHS   bwRequest = {0, 0, 0, 0};

bwRequest.top = TOOLBAR_HEIGHT;

hr = pSite->RequestBorderSpaceDW((IDockingWindow*)this, &bwRequest);
if(SUCCEEDED(hr))
   {
   hr = pSite->SetBorderSpaceDW((IDockingWindow*)this, &bwRequest);
   if(SUCCEEDED(hr))
      {
      RECT  rcTemp;

      if(!prcBorder)
         {
         hr = pSite->GetBorderDW((IDockingWindow*)this, &rcTemp);
         if(SUCCEEDED(hr))
            {
            prcBorder = &rcTemp;
            }
         }

      if(prcBorder)
         {
         //set up our display rectangle
         m_rcDisplay.left = prcBorder->left;
         m_rcDisplay.top = prcBorder->top;
         m_rcDisplay.right = prcBorder->right - prcBorder->left;
         m_rcDisplay.bottom = bwRequest.top;

         return TRUE;
         }
      }
   }

return FALSE;
}

#endif   //(_WIN32_IE >= 0x0400)


DOCKWNDW.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          DockWndw.h

   Description:   CDockingWindow definitions.

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

#include <windows.h>
#include <shlobj.h>

#if (_WIN32_IE < 0x0400)
#define CDockingWindow  IUnknown

#else //(_WIN32_IE >= 0x0400)

#include "ShlView.h"

#ifndef DOCKINGWINDOW_H
#define DOCKINGWINDOW_H

#define SHTB_CLASS_NAME (TEXT("RegViewToolbarClass"))

/**************************************************************************
   global variables
**************************************************************************/

extern HINSTANCE  g_hInst;
extern UINT       g_DllRefCount;

#define TOOLBAR_HEIGHT  50

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

   CDockingWindow class definition

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

class CDockingWindow : public IDockingWindow, public IInputObject, public IObjectWithSite
{
protected:
   DWORD m_ObjRefCount;

public:
   CDockingWindow(CShellView*, HWND);
   ~CDockingWindow();

   //IUnknown methods
   STDMETHODIMP QueryInterface(REFIID, LPVOID*);
   STDMETHODIMP_(DWORD) AddRef();
   STDMETHODIMP_(DWORD) Release();

   //IOleWindow methods
   STDMETHOD (GetWindow) (HWND*);
   STDMETHOD (ContextSensitiveHelp) (BOOL);

   //IDockingWindow methods
   STDMETHOD (ShowDW) (BOOL fShow);
   STDMETHOD (CloseDW) (DWORD dwReserved);
   STDMETHOD (ResizeBorderDW) (LPCRECT prcBorder, IUnknown* punkToolbarSite, BOOL fReserved);

   //IInputObject methods
   STDMETHOD (UIActivateIO) (BOOL, LPMSG);
   STDMETHOD (HasFocusIO) (void);
   STDMETHOD (TranslateAcceleratorIO) (LPMSG);

   //IObjectWithSite methods
   STDMETHOD (SetSite) (IUnknown*);
   STDMETHOD (GetSite) (REFIID, LPVOID*);

private:
BOOL NegotiateBorderSpace(IDockingWindowSite*, LPCRECT prcBorder = NULL);
RECT m_rcDisplay;
BOOL m_bFocus;
HWND m_hwndParent;
HWND m_hWnd;
HWND m_hwndCommand;
   CShellView *m_pView;
   IDockingWindowSite *m_pSite;

private:
void FocusChange(BOOL);
   LRESULT OnKillFocus(void);
LRESULT OnSetFocus(void);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
LRESULT OnPaint(void);
LRESULT OnCommand(WPARAM wParam, LPARAM lParam);
};

#endif   //DOCKINGWINDOW_H

#endif   //(_WIN32_IE >= 0x0400)


ENUMIDL.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          EnumIDL.cpp

   Description:   Implements IEnumIDList.

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "EnumIDL.h"
#include "ShlFldr.h"

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

   CEnumIDList::CEnumIDList

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

CEnumIDList::CEnumIDList(HKEY hKeyRoot, LPCTSTR lpszSubKey, DWORD dwFlags, HRESULT *pResult)
{
if(pResult)
   *pResult = S_OK;

m_pFirst = m_pLast = m_pCurrent = NULL;

m_pPidlMgr = new CPidlMgr();
if(!m_pPidlMgr)
   {
   if(pResult)
      *pResult = E_OUTOFMEMORY;

   delete this;
   return;
   }

//get the shell's IMalloc pointer
//we'll keep this until we get destroyed
if(FAILED(SHGetMalloc(&m_pMalloc)))
   {
   if(pResult)
      *pResult = E_OUTOFMEMORY;

   delete this;
   return;
   }

if(!CreateEnumList(hKeyRoot, lpszSubKey, dwFlags))
   {
   if(pResult)
      *pResult = E_OUTOFMEMORY;

   delete this;
   return;
   }

m_ObjRefCount = 1;

g_DllRefCount++;
}

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

   CEnumIDList::~CEnumIDList

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

CEnumIDList::~CEnumIDList()
{
DeleteList();

if(m_pMalloc)
   m_pMalloc->Release();

if(m_pPidlMgr)
   delete m_pPidlMgr;

g_DllRefCount--;
}

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

   CEnumIDList::QueryInterface

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

STDMETHODIMP CEnumIDList::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IEnumIDList
else if(IsEqualIID(riid, IID_IEnumIDList))
   {
   *ppReturn = (IEnumIDList*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CEnumIDList::AddRef

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

STDMETHODIMP_(DWORD) CEnumIDList::AddRef()
{
return ++m_ObjRefCount;
}

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

   CEnumIDList::Release

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

STDMETHODIMP_(DWORD) CEnumIDList::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

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

   CEnumIDList::Next()

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

STDMETHODIMP CEnumIDList::Next(DWORD dwElements, LPITEMIDLIST apidl[], LPDWORD pdwFetched)
{
DWORD    dwIndex;
HRESULT  hr = S_OK;

if(dwElements > 1 && !pdwFetched)
   return E_INVALIDARG;

for(dwIndex = 0; dwIndex < dwElements; dwIndex++)
   {
   //is this the last item in the list?
   if(!m_pCurrent)
      {
      hr =  S_FALSE;
      break;
      }

   apidl[dwIndex] = m_pPidlMgr->Copy(m_pCurrent->pidl);

   m_pCurrent = m_pCurrent->pNext;
   }

if(pdwFetched)
   *pdwFetched = dwIndex;

return hr;
}

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

   CEnumIDList::Skip()

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

STDMETHODIMP CEnumIDList::Skip(DWORD dwSkip)
{
DWORD    dwIndex;
HRESULT  hr = S_OK;

for(dwIndex = 0; dwIndex < dwSkip; dwIndex++)
   {
   //is this the last item in the list?
   if(!m_pCurrent)
      {
      hr = S_FALSE;
      break;
      }

   m_pCurrent = m_pCurrent->pNext;
   }

return hr;
}

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

   CEnumIDList::Reset()

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

STDMETHODIMP CEnumIDList::Reset(void)
{
m_pCurrent = m_pFirst;

return S_OK;
}

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

   CEnumIDList::Clone()

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

STDMETHODIMP CEnumIDList::Clone(LPENUMIDLIST *ppEnum)
{
return E_NOTIMPL;
}

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

   CEnumIDList::CreateEnumList()

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

BOOL CEnumIDList::CreateEnumList(HKEY hKeyRoot, LPCTSTR lpszSubKey, DWORD dwFlags)
{
LPITEMIDLIST   pidl;
DWORD          dwIndex;

//enumerate the folders (keys)
if(dwFlags & SHCONTF_FOLDERS)
   {
   /*
   special case - we can't enumerate the root level keys (HKEY_CLASSES_ROOT, etc.),
   so if both of these are NULL, then we need to fake an enumeration of those.
   */
   if((!hKeyRoot) && (!lpszSubKey))
      {
      HKEY  ahKey[] = { HKEY_CLASSES_ROOT,
                        HKEY_CURRENT_USER,
                        HKEY_LOCAL_MACHINE,
                        HKEY_USERS,
                        HKEY_PERFORMANCE_DATA,
                        HKEY_CURRENT_CONFIG,
                        HKEY_DYN_DATA,
                        (HKEY)-1};

      for(dwIndex = 0; ahKey[dwIndex] != (HKEY)-1; dwIndex++)
         {
         //only add the root keys that actually exists on this system
         if(RootKeyExists(ahKey[dwIndex]))
            {
            //create the pidl for this item
            pidl = m_pPidlMgr->CreateRootKey(ahKey[dwIndex]);
            if(pidl)
               {
               if(!AddToEnumList(pidl))
                  return FALSE;
               }
            else
               {
               return FALSE;
               }
            }
         }
      }
   else
      {
      TCHAR    szKey[REGSTR_MAX_VALUE_LENGTH];

      dwIndex = 0;

      //get the next item
      while(GetKeyName(hKeyRoot, lpszSubKey, dwIndex++, szKey, sizeof(szKey)))
         {
         //create the pidl for this item
         pidl = m_pPidlMgr->CreateSubKey(szKey);
         if(pidl)
            {
            if(!AddToEnumList(pidl))
               return FALSE;
            }
         else
            {
            return FALSE;
            }
         }
      }
   }

//enumerate the non-folder items (values)
if(dwFlags & SHCONTF_NONFOLDERS)
   {
   TCHAR    szValue[REGSTR_MAX_VALUE_LENGTH];

   dwIndex = 0;

   /*
   There is a special case here. All keys have a default value, but if the
   default value is empty, then it won't be enumerated. If it has data, then
   it will be enumerated but the name will be NULL.

   To work around this, we will add a default item here and then not add it
   if it shows up in the enumeration in the loop below.
   */
   //create the default item
   pidl = m_pPidlMgr->CreateValue(TEXT(""));
   if(pidl)
      {
      if(!AddToEnumList(pidl))
         return FALSE;
      }
   else
      {
      return FALSE;
      }

   //get the items
   while(GetValueName(hKeyRoot, lpszSubKey, dwIndex++, szValue, sizeof(szValue)))
      {
      //don't add a default value that may have shown up in the enumeration
      if(!szValue[0])
         continue;

      //create the pidl for this item
      pidl = m_pPidlMgr->CreateValue(szValue);

      if(pidl)
         {
         if(!AddToEnumList(pidl))
            return FALSE;
         }
      else
         {
         return FALSE;
         }
      }
   }

return TRUE;
}

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

   CEnumIDList::AddToEnumList()

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

BOOL CEnumIDList::AddToEnumList(LPITEMIDLIST pidl)
{
LPENUMLIST  pNew;

pNew = (LPENUMLIST)m_pMalloc->Alloc(sizeof(ENUMLIST));

if(pNew)
   {
   //set the next pointer
   pNew->pNext = NULL;
   pNew->pidl = pidl;

   //is this the first item in the list?
   if(!m_pFirst)
      {
      m_pFirst = pNew;
      m_pCurrent = m_pFirst;
      }

   if(m_pLast)
      {
      //add the new item to the end of the list
      m_pLast->pNext = pNew;
      }

   //update the last item pointer
   m_pLast = pNew;

   return TRUE;
   }

return FALSE;
}

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

   CEnumIDList::DeleteList()

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

BOOL CEnumIDList::DeleteList(void)
{
LPENUMLIST  pDelete;

while(m_pFirst)
   {
   pDelete = m_pFirst;
   m_pFirst = pDelete->pNext;

   //free the pidl
   //m_pMalloc->Free(pDelete->pidl);
   m_pPidlMgr->Delete(pDelete->pidl);

   //free the list item
   m_pMalloc->Free(pDelete);
   }

m_pFirst = m_pLast = m_pCurrent = NULL;

return TRUE;
}


ENUMIDL.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          EnumIDL.h

   Description:   CEnumIDList definitions.

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

#ifndef ENUMIDLIST_H
#define ENUMIDLIST_H

#include <windows.h>
#include <shlobj.h>

#include "PidlMgr.h"
#include "Utility.h"

/**************************************************************************
   structure defintions
**************************************************************************/

typedef struct tagENUMLIST
   {
   struct tagENUMLIST   *pNext;
   LPITEMIDLIST         pidl;
   }ENUMLIST, FAR *LPENUMLIST;

/**************************************************************************
   global variables
**************************************************************************/

extern HINSTANCE  g_hInst;
extern UINT       g_DllRefCount;

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

   CEnumIDList class definition

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

class CEnumIDList : public IEnumIDList
{
protected:
   DWORD m_ObjRefCount;

public:
   CEnumIDList(HKEY, LPCTSTR, DWORD, HRESULT*);
   ~CEnumIDList();

   //IUnknown methods
   STDMETHOD (QueryInterface)(REFIID, LPVOID*);
   STDMETHOD_ (DWORD, AddRef)();
   STDMETHOD_ (DWORD, Release)();

   //IEnumIDList
   STDMETHOD (Next) (DWORD, LPITEMIDLIST*, LPDWORD);
   STDMETHOD (Skip) (DWORD);
   STDMETHOD (Reset) (void);
   STDMETHOD (Clone) (LPENUMIDLIST*);

private:
   BOOL CreateEnumList(HKEY, LPCTSTR, DWORD);
   BOOL AddToEnumList(LPITEMIDLIST);
   BOOL DeleteList(void);

private:
   LPMALLOC m_pMalloc;
   LPENUMLIST m_pFirst;
   LPENUMLIST m_pLast;
   LPENUMLIST m_pCurrent;
   LPPIDLMGR  m_pPidlMgr;
};

#endif   //ENUMIDLIST_H


EXTRICON.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ExtrIcon.cpp

   Description:   Implements CExtractIcon.

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "ExtrIcon.h"

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

   CExtractIcon::CExtractIcon()

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

CExtractIcon::CExtractIcon(LPCITEMIDLIST pidl)
{
m_pPidlMgr = new CPidlMgr();
if(!m_pPidlMgr)
   {
   delete this;
   return;
   }

m_pidl = m_pPidlMgr->Copy(pidl);

m_ObjRefCount = 1;

g_DllRefCount++;
}

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

   CExtractIcon::~CExtractIcon()

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

CExtractIcon::~CExtractIcon()
{
if(m_pidl)
   {
   m_pPidlMgr->Delete(m_pidl);
   m_pidl = NULL;
   }

if(m_pPidlMgr)
   {
   delete m_pPidlMgr;
   }

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

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

   CExtractIcon::QueryInterface

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

STDMETHODIMP CExtractIcon::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IExtractIcon
else if(IsEqualIID(riid, IID_IExtractIcon))
   {
   *ppReturn = (IExtractIcon*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CExtractIcon::AddRef

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

STDMETHODIMP_(DWORD) CExtractIcon::AddRef()
{
return ++m_ObjRefCount;
}

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

   CExtractIcon::Release

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

STDMETHODIMP_(DWORD) CExtractIcon::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IExtractIcon Implementation
//

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

   CExtractIcon::GetIconLocation()

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

STDMETHODIMP CExtractIcon::GetIconLocation(  UINT uFlags,
                                             LPTSTR szIconFile,
                                             UINT cchMax,
                                             LPINT piIndex,
                                             LPUINT puFlags)
{
//tell the shell to always call Extract
*puFlags = GIL_NOTFILENAME;

//the pidl is either a value or a folder, so find out which it is
if(m_pPidlMgr->IsValue(m_pPidlMgr->GetLastItem(m_pidl)))
   {
   //its a value
   DWORD dwType;

   //get the data type to determine which icon to get
   m_pPidlMgr->GetValueType(m_pidl, &dwType);

   switch(dwType)
      {
      case REG_BINARY:
      case REG_DWORD:
      case REG_DWORD_BIG_ENDIAN:
         *piIndex = ICON_INDEX_BINARY;
         break;

      default:
         *piIndex = ICON_INDEX_STRING;
         break;
         }
   }
else
   {
   //its a key
   if(uFlags & GIL_OPENICON)
      {
      *piIndex = ICON_INDEX_FOLDEROPEN;  //tell Extract to return the open icon
      }
   else
      {
      *piIndex = ICON_INDEX_FOLDER;  //tell Extract to return the open icon
      }
   }

return S_OK;
}

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

   CExtractIcon::Extract()

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

STDMETHODIMP CExtractIcon::Extract( LPCTSTR pszFile,
                                    UINT nIconIndex,
                                    HICON *phiconLarge,
                                    HICON *phiconSmall,
                                    UINT nIconSize)
{
*phiconLarge = ImageList_GetIcon(g_himlLarge, nIconIndex, ILD_TRANSPARENT);
*phiconSmall = ImageList_GetIcon(g_himlSmall, nIconIndex, ILD_TRANSPARENT);

/*
if(nIconIndex)
   {
   *phiconLarge = ImageList_GetIcon(g_himlLarge, ICON_INDEX_FOLDEROPEN, ILD_TRANSPARENT);
   *phiconSmall = ImageList_GetIcon(g_himlSmall, ICON_INDEX_FOLDEROPEN, ILD_TRANSPARENT);
   }
else
   {
   *phiconLarge = ImageList_GetIcon(g_himlLarge, ICON_INDEX_FOLDER, ILD_TRANSPARENT);
   *phiconSmall = ImageList_GetIcon(g_himlSmall, ICON_INDEX_FOLDER, ILD_TRANSPARENT);
   }
*/
return S_OK;
}


EXTRICON.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          ExtrIcon.h

   Description:   CExtractIcon definitions.

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

#ifndef EXTRACTICON_H
#define EXTRACTICON_H

/**************************************************************************
   #include statements
**************************************************************************/

#include <windows.h>
#include <shlobj.h>

#include "PidlMgr.h"

/**************************************************************************
   global variables
**************************************************************************/

extern HINSTANCE  g_hInst;
extern UINT       g_DllRefCount;
extern HIMAGELIST g_himlLarge;
extern HIMAGELIST g_himlSmall;

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

   CExtractIcon class definition

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

class CExtractIcon : public IExtractIcon
{
protected:
   DWORD m_ObjRefCount;

public:
   CExtractIcon(LPCITEMIDLIST);
   ~CExtractIcon();

   //IUnknown methods
   STDMETHOD (QueryInterface) (REFIID riid, LPVOID * ppvObj);
   STDMETHOD_ (ULONG, AddRef) (void);
   STDMETHOD_ (ULONG, Release) (void);

   //IExtractIcon methods
   STDMETHOD (GetIconLocation) (UINT, LPTSTR, UINT, LPINT, LPUINT);
   STDMETHOD (Extract) (LPCTSTR, UINT, HICON*, HICON*, UINT);

private:
LPITEMIDLIST m_pidl;
   LPPIDLMGR m_pPidlMgr;
};

#define ICON_INDEX_BINARY     0
#define ICON_INDEX_STRING     1
#define ICON_INDEX_FOLDER     2
#define ICON_INDEX_FOLDEROPEN 3

#endif   //EXTRACTICON_H


GUID.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          Guid.h

   Description:   Private GUID definition.

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

// {4778AFE0-2289-11d0-8AEC-00A0C90C9246}
DEFINE_GUID(CLSID_RegViewNameSpace, 0x4778AFE0, 0x2289, 0x11D0, 0x8A, 0xEC, 0x0, 0xA0, 0xC9, 0xC, 0x92, 0x46);
#define CLSID_CmdGrp CLSID_RegViewNameSpace


INFOTIP.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          InfoTip.cpp

   Description:   Implements CQueryInfo.

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

#include "InfoTip.h"

#if (_WIN32_IE >= 0x0400)

/**************************************************************************
   #include statements
**************************************************************************/

#include "Guid.h"
#include "Utility.h"

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

   CQueryInfo::CQueryInfo()

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

CQueryInfo::CQueryInfo(LPCITEMIDLIST pidl)
{
m_pPidlMgr = new CPidlMgr();
if(!m_pPidlMgr)
   {
   delete this;
   return;
   }

//get the shell's IMalloc pointer
//we'll keep this until we get destroyed
if(FAILED(SHGetMalloc(&m_pMalloc)))
   {
   delete this;
   return;
   }

m_pidl = m_pPidlMgr->Copy(pidl);

m_ObjRefCount = 1;

g_DllRefCount++;
}

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

   CQueryInfo::~CQueryInfo()

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

CQueryInfo::~CQueryInfo()
{
if(m_pidl)
   {
   m_pPidlMgr->Delete(m_pidl);
   m_pidl = NULL;
   }

if(m_pMalloc)
   {
   m_pMalloc->Release();
   }

if(m_pPidlMgr)
   {
   delete m_pPidlMgr;
   }

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

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

   CQueryInfo::QueryInterface

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

STDMETHODIMP CQueryInfo::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IQueryInfo
else if(IsEqualIID(riid, IID_IQueryInfo))
   {
   *ppReturn = (IQueryInfo*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

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

   CQueryInfo::AddRef

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

STDMETHODIMP_(DWORD) CQueryInfo::AddRef()
{
return ++m_ObjRefCount;
}

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

   CQueryInfo::Release

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

STDMETHODIMP_(DWORD) CQueryInfo::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IQueryInfo Implementation
//

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

   CQueryInfo::GetInfoTip()

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

STDMETHODIMP CQueryInfo::GetInfoTip(DWORD dwFlags, WCHAR **ppwszTip)
{
TCHAR szTipText[MAX_PATH];
int   cchOleStr;

*ppwszTip = NULL;

//get the entire text for the item
m_pPidlMgr->GetPidlPath(m_pidl, szTipText, sizeof(szTipText));

//get the number of characters required
cchOleStr = lstrlen(szTipText) + 1;

//allocate the wide character string
*ppwszTip = (LPWSTR)m_pMalloc->Alloc(cchOleStr * sizeof(WCHAR));
if(!*ppwszTip)
   return E_OUTOFMEMORY;

LocalToWideChar(*ppwszTip, szTipText, cchOleStr);

return S_OK;
}

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

   CQueryInfo::GetInfoFlags()

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

STDMETHODIMP CQueryInfo::GetInfoFlags(LPDWORD pdwFlags)
{
return E_NOTIMPL;
}

#endif   //(_WIN32_IE >= 0x0400)


INFOTIP.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          InfoTip.h

   Description:   CQueryInfo definitions.

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

#include <windows.h>
#include <shlobj.h>

#if (_WIN32_IE < 0x0400)
#define CQueryInfo  IUnknown

#else //(_WIN32_IE >= 0x0400)

#ifndef INFOTIP_H
#define INFOTIP_H

/**************************************************************************
   #include statements
**************************************************************************/

#include "PidlMgr.h"

/**************************************************************************
   global variables
**************************************************************************/

extern HINSTANCE  g_hInst;
extern UINT       g_DllRefCount;

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

   CQueryInfo class definition

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

class CQueryInfo : public IQueryInfo
{
protected:
   DWORD m_ObjRefCount;

public:
   CQueryInfo(LPCITEMIDLIST);
   ~CQueryInfo();

   //IUnknown methods
   STDMETHOD (QueryInterface) (REFIID riid, LPVOID * ppvObj);
   STDMETHOD_ (ULONG, AddRef) (void);
   STDMETHOD_ (ULONG, Release) (void);

   //IQueryInfo methods
   STDMETHOD(GetInfoTip)(DWORD dwFlags, WCHAR **ppwszTip);
   STDMETHOD(GetInfoFlags)(LPDWORD pdwFlags);

private:
LPITEMIDLIST m_pidl;
LPMALLOC m_pMalloc;
   LPPIDLMGR m_pPidlMgr;
};

#endif   //INFOTIP_H

#endif   //(_WIN32_IE >= 0x0400)


PIDLMGR.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

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

   File:          PidlMgr.cpp

   Description:   Implements CPidlMgr.

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

/**************************************************************************
   #include statements
**************************************************************************/

#include "PidlMgr.h"
#include "ShlFldr.h"
#include "resource.h"

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

   CPidlMgr::CPidlMgr

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

CPidlMgr::CPidlMgr()
{
//get the shell's IMalloc pointer
//we'll keep this until we get destroyed
if(FAILED(SHGetMalloc(&m_pMalloc)))
   {
   delete this;
   }

g_DllRefCount++;
}

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

   CPidlMgr::~CPidlMgr

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

CPidlMgr::~CPidlMgr()
{
if(m_pMalloc)
   m_pMalloc->Release();

g_DllRefCount--;
}

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

   CPidlMgr::Create(DWORD, LPVOID, DWORD)

   Creates a new PIDL

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

LPITEMIDLIST CPidlMgr::Create(PIDLTYPE type, LPVOID pIn, USHORT uInSize)
{
LPITEMIDLIST   pidlOut;
USHORT         uSize;

pidlOut = NULL;

/*
Calculate the size. This consists of the ITEMIDLIST, the PIDL structure plus
the size of the data. We subtract the size of an HKEY because that is included
in uInSize.
*/
uSize = sizeof(ITEMIDLIST) + (sizeof(PIDLDATA) - sizeof(HKEY)) + uInSize;

/*
Allocate the memory, adding an additional ITEMIDLIST for the NULL terminating
ID List.
*/
pidlOut = (LPITEMIDLIST)m_pMalloc->Alloc(uSize + sizeof(ITEMIDLIST));

if(pidlOut)
   {
   LPITEMIDLIST   pidlTemp = pidlOut;
   LPPIDLDATA     pData;

   //set the size of this item
   pidlTemp->mkid.cb = uSize;

   //set the data for this item
   pData = GetDataPointer(pidlTemp);
   pData->type = type;
   switch(type)
      {
      case PT_ROOTKEY:
         pData->hRootKey = *(HKEY*)pIn;
         break;

      case PT_SUBKEY:
      case PT_VALUE:
         CopyMemory(pData->szText, pIn, uInSize);
         break;
      }

   //set the NULL terminator to 0
   pidlTemp = GetNextItem(pidlTemp);
   pidlTemp->mkid.cb = 0;
   pidlTemp->mkid.abID[0] = 0;
   }

return pidlOut;
}

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

   CPidlMgr::Delete(HKEY)

   Deletes a PIDL

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

void CPidlMgr::Delete(LPITEMIDLIST pidl)
{
m_pMalloc->Free(pidl);
}

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

   CPidlMgr::CreateRootKey()

   Creates a root key PIDL

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

LPITEMIDLIST CPidlMgr::CreateRootKey(HKEY hKeyRoot)
{
return Create(PT_ROOTKEY, (LPVOID)&hKeyRoot, sizeof(hKeyRoot));
}

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

   CPidlMgr::CreateSubKey()

   Creates a sub key PIDL

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

LPITEMIDLIST CPidlMgr::CreateSubKey(LPCTSTR lpszNew)
{
return Create(PT_SUBKEY, (LPVOID)lpszNew, (lstrlen(lpszNew) + 1) * sizeof(TCHAR));
}

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

   CPidlMgr::CreateValue()

   Creates a value PIDL

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

LPITEMIDLIST CPidlMgr::CreateValue(LPCTSTR lpszNew)
{
return Create(PT_VALUE, (LPVOID)lpszNew, (lstrlen(lpszNew) + 1) * sizeof(TCHAR));
}

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

   CPidlMgr::GetNextItem()

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

LPITEMIDLIST CPidlMgr::GetNextItem(LPCITEMIDLIST pidl)
{
if(pidl)
   return (LPITEMIDLIST)(LPBYTE)(((LPBYTE)pidl) + pidl->mkid.cb);
else
   return (NULL);
}

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

   CPidlMgr::GetSize()

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

UINT CPidlMgr::GetSize(LPCITEMIDLIST pidl)
{
UINT cbTotal = 0;
LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl;

if(pidlTemp)
   {
   while(pidlTemp->mkid.cb)
      {
      cbTotal += pidlTemp->mkid.cb;
      pidlTemp = GetNextItem(pidlTemp);
      }

   //add the size of the NULL terminating ITEMIDLIST
   cbTotal += sizeof(ITEMIDLIST);
   }

return (cbTotal);
}

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

   CPidlMgr::GetData(PIDLTYPE, LPCITEMIDLIST, LPVOID, USHORT)

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

DWORD CPidlMgr::GetData(PIDLTYPE type, LPCITEMIDLIST pidl, LPVOID pOut, USHORT uOutSize)
{
if(!pidl)
   return 0;

LPPIDLDATA  pData = GetDataPointer(pidl);
DWORD       dwReturn = 0;

//copy the data
switch(type)
   {
   case PT_ROOTKEY:
      if(uOutSize < sizeof(HKEY))
         return 0;

      if(PT_ROOTKEY != pData->type)
         return 0;

      *(HKEY*)pOut = pData->hRootKey;
      dwReturn = sizeof(pData->hRootKey);
      break;

   case PT_SUBKEY:
   case PT_VALUE:
   case PT_TEXT:
      *(LPTSTR)pOut = 0;
      lstrcpyn((LPTSTR)pOut, pData->szText, uOutSize);
      dwReturn = lstrlen((LPTSTR)pOut);
      break;
   }

return dwReturn;
}

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

   CPidlMgr::GetRootKey()

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

BOOL CPidlMgr::GetRootKey(LPCITEMIDLIST pidl, HKEY *phKeyRoot)
{
return (BOOL)GetData(PT_ROOTKEY, pidl, (LPVOID)phKeyRoot, sizeof(HKEY));
}

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

   CPidlMgr::GetLastItem()

   Gets the last item in the list

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

LPITEMIDLIST CPidlMgr::GetLastItem(LPCITEMIDLIST pidl)
{
LPITEMIDLIST   pidlLast = NULL;

//get the PIDL of the last item in the list
if(pidl)
   {
   while(pidl->mkid.cb)
      {
      pidlLast = (LPITEMIDLIST)pidl;
      pidl = GetNextItem(pidl);
      }
   }

return pidlLast;
}

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

   CPidlMgr::GetItemText()

   Gets the text for only this item

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

DWORD CPidlMgr::GetItemText(LPCITEMIDLIST pidl, LPTSTR lpszText, USHORT uSize)
{
//if this is a root key, it needs to be handled specially
if(IsRootKey(pidl))
   {
   HKEY  hKey;

   if(!GetRootKey(pidl, &hKey))
      return 0;

   return GetRootKeyText(hKey, lpszText, uSize);
   }

return GetData(PT_TEXT, pidl, (LPVOID)lpszText, uSize);
}

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

   CPidlMgr::GetSubKeyText()

   Creates a sub key string from a PIDL, filtering out the root key and
   value, if either is present.

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

DWORD CPidlMgr::GetSubKeyText(LPCITEMIDLIST pidl, LPTSTR lpszSubKey, DWORD dwSize)
{
if(!pidl)
   return 0;

LPITEMIDLIST   pidlTemp;
DWORD          dwCopied = 0;

/*
This may be a list of items, so if the first item is a key rather than a
string, skip the first item because it contains the root key.
*/
if(IsRootKey(pidl))
   pidlTemp = GetNextItem(pidl);
else
   pidlTemp = (LPITEMIDLIST)pidl;

//if this is NULL, return the required size of the buffer
if(!lpszSubKey)
   {
   while(pidlTemp->mkid.cb)
      {
      LPPIDLDATA  pData = GetDataPointer(pidlTemp);

      //add the length of this item plus one for the backslash
      dwCopied += lstrlen(pData->szText) + 1;

      pidlTemp = GetNextItem(pidlTemp);
      }

   //add one for the NULL terminator
   return dwCopied + 1;
   }

*lpszSubKey = 0;

while(pidlTemp->mkid.cb && (dwCopied < dwSize))
   {
   LPPIDLDATA  pData = GetDataPointer(pidlTemp);

   //if this item is a value, then skip it and finish
   if(PT_VALUE == pData->type)
      break;

   lstrcat(lpszSubKey, pData->szText);
   lstrcat(lpszSubKey, TEXT("\\"));

   dwCopied += lstrlen(pData->szText) + 1;

   pidlTemp = GetNextItem(pidlTemp);
   }

//remove the last backslash if necessary
if(dwCopied)
   {
   if(*(lpszSubKey + lstrlen(lpszSubKey) - 1) == '\\')
      {
      *(lpszSubKey + lstrlen(lpszSubKey) - 1) = 0;
      dwCopied--;
      }
   }

return dwCopied;
}

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

   CPidlMgr::GetValueText()

   Gets the text for the last item in the list

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

DWORD CPidlMgr::GetValueText(LPCITEMIDLIST pidl, LPTSTR lpszValue, DWORD dwSize)
{
if(!pidl)
   return 0;

LPCITEMIDLIST  pidlTemp = pidl;
TCHAR          szText[MAX_PATH];

/*
This may be a list of items, so search through the list looking for an item
that is a value. There should be only one, and it should be the last one, but
we will assume it can be anywhere and only copy the first one.
*/
while(pidlTemp->mkid.cb && !IsValue(pidlTemp))
   {
   pidlTemp = GetNextItem(pidlTemp);
   }

//we didn't find a value pidl
if(!pidlTemp->mkid.cb)
   return 0;

//get the item's text
GetItemText(pidlTemp, szText, sizeof(szText));

//if this is NULL, return the required size of the buffer
if(!lpszValue)
   {
   return lstrlen(szText) + 1;
   }

lstrcpy(lpszValue, szText);

return lstrlen(lpszValue);
}

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

   CPidlMgr::Copy()

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

LPITEMIDLIST CPidlMgr::Copy(LPCITEMIDLIST pidlSource)
{
LPITEMIDLIST pidlTarget = NULL;
UINT cbSource = 0;

if(NULL == pidlSource)
   return (NULL);

// Allocate the new pidl
cbSource = GetSize(pidlSource);
pidlTarget = (LPITEMIDLIST)m_pMalloc->Alloc(cbSource);
if(!pidlTarget)
   return (NULL);

// Copy the source to the target
CopyMemory(pidlTarget, pidlSource, cbSource);

return pidlTarget;
}

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

   CPidlMgr::IsRootKey()

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

BOOL CPidlMgr::IsRootKey(LPCITEMIDLIST pidl)
{
LPPIDLDATA  pData = GetDataPointer(pidl);

return (PT_ROOTKEY == pData->type);
}

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

   CPidlMgr::IsSubKey()

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

BOOL CPidlMgr::IsSubKey(LPCITEMIDLIST pidl)
{
LPPIDLDATA  pData = GetDataPointer(pidl);

return (PT_SUBKEY == pData->type);
}

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

   CPidlMgr::IsValue()

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

BOOL CPidlMgr::IsValue(LPCITEMIDLIST pidl)
{
LPPIDLDATA  pData = GetDataPointer(pidl);

return (PT_VALUE == pData->type);
}

/**************************************************************************

   CPidlMgr::HasSubKeys()

**************************************************************************/

BOOL CPidlMgr::HasSubKeys(HKEY hKeyRoot, LPCTSTR pszSubKey, LPCITEMIDLIST pidl)
{
HKEY     hKey;
LONG     lResult = !ERROR_SUCCESS;
FILETIME ft;
LPTSTR   lpszTemp;

if(!pszSubKey)
   pszSubKey = TEXT("");

if(!hKeyRoot)
   GetRootKey(pidl, &hKeyRoot);

DWORD dwSize = GetSubKeyText(pidl, NULL, 0) + lstrlen(pszSubKey) + 2;

lpszTemp = new TCHAR[dwSize];
if(!lpszTemp)
   return FALSE;

lstrcpy(lpszTemp, pszSubKey);
if(*lpszTemp)
   lstrcat(lpszTemp, TEXT("\\"));
GetSubKeyText(pidl, lpszTemp + lstrlen(lpszTemp), dwSize - lstrlen(lpszTemp));

//open the specified key
lResult = RegOpenKeyEx( hKeyRoot,
                        lpszTemp,
                        0,
                        KEY_ENUMERATE_SUB_KEYS,
                        &hKey);

if(ERROR_SUCCESS == lResult)
   {
   TCHAR szTemp[MAX_PATH];
   DWORD dwSize = sizeof(szTemp);

   //try to get the specified subkey
   lResult = RegEnumKeyEx( hKey,
                           0,
                           szTemp,
                           &dwSize,
                           NULL,
                           NULL,
                           NULL,
                           &ft);

   RegCloseKey(hKey);
   }

delete lpszTemp;

return (BOOL)(ERROR_SUCCESS == lResult);
}

/**************************************************************************

   CPidlMgr::GetDataPointer()

**************************************************************************/

LPPIDLDATA CPidlMgr::GetDataPointer(LPCITEMIDLIST pidl)
{
if(!pidl)
   return NULL;

return (LPPIDLDATA)(pidl->mkid.abID);
}

/**************************************************************************

   CPidlMgr::GetValueType()

**************************************************************************/

BOOL CPidlMgr::GetValueType(  LPCITEMIDLIST pidlFQ,
                              LPDWORD pdwType)
{
BOOL  bReturn;
LPITEMIDLIST   pidlKey;
LPITEMIDLIST   pidlValue;

if(!SeparateKeyAndValue(pidlFQ, &pidlKey, &pidlValue))
   return 0;

bReturn = GetValueType(pidlKey, pidlValue, pdwType);

Delete(pidlKey);
Delete(pidlValue);

return bReturn;
}

/**************************************************************************

   CPidlMgr::GetValueType()

**************************************************************************/

BOOL CPidlMgr::GetValueType(  LPCITEMIDLIST pidlKey,
                              LPCITEMIDLIST pidlValue,
                              LPDWORD pdwType)
{
if(!pidlKey)
   return FALSE;

if(!pidlValue)
   return FALSE;

if(!pdwType)
   return FALSE;

HKEY     hKey,
         hRootKey;
LPTSTR   lpszSubKey,
         lpszValueName;
DWORD    dwNameSize;
LONG     lResult;

//get the root key
GetRootKey(pidlKey, &hRootKey);

//assemble the key string
dwNameSize = GetSubKeyText(pidlKey, NULL, 0);
lpszSubKey = new TCHAR[dwNameSize];
if(!lpszSubKey)
   return FALSE;
GetSubKeyText(pidlKey, lpszSubKey, dwNameSize);

//assemble the value name
dwNameSize = GetValueText(pidlValue, NULL, 0);
lpszValueName = new TCHAR[dwNameSize];
if(!lpszValueName)
   {
   delete lpszSubKey;
   return FALSE;
   }
GetValueText(pidlValue, lpszValueName, dwNameSize);

//open the key
lResult = RegOpenKeyEx( hRootKey,
                        lpszSubKey,
                        0,
                        KEY_QUERY_VALUE,
                        &hKey);

if(ERROR_SUCCESS != lResult)
   return FALSE;

//get the value type
lResult = RegQueryValueEx( hKey,
                           lpszValueName,
                           NULL,
                           pdwType,
                           NULL,
                           NULL);

RegCloseKey(hKey);

delete lpszSubKey;

delete lpszValueName;

return TRUE;
}

/**************************************************************************

   CPidlMgr::GetDataText()

**************************************************************************/

DWORD CPidlMgr::GetDataText(  LPCITEMIDLIST pidlFQ,
                              LPTSTR lpszOut,
                              DWORD dwOutSize)
{
DWORD dwReturn;
LPITEMIDLIST   pidlKey;
LPITEMIDLIST   pidlValue;

if(!SeparateKeyAndValue(pidlFQ, &pidlKey, &pidlValue))
   return 0;

dwReturn = GetDataText(pidlKey, pidlValue, lpszOut, dwOutSize);

Delete(pidlKey);
Delete(pidlValue);

return dwReturn;
}

/**************************************************************************

   CPidlMgr::GetDataText()

**************************************************************************/

DWORD CPidlMgr::GetDataText(  LPCITEMIDLIST pidlKey,
                              LPCITEMIDLIST pidlValue,
                              LPTSTR lpszOut,
                              DWORD dwOutSize)
{
HKEY     hKey,
         hRootKey;
LPTSTR   lpszSubKey,
         lpszValueName;
DWORD    dwNameSize,
         dwType,
         dwSize,
         dwReturn = 0;
LONG     lResult;
TCHAR    szData[MAX_PATH];

if(!lpszOut)
   return dwReturn;

if(!pidlKey)
   return dwReturn;

if(!pidlValue)
   return dwReturn;

//get the root key
GetRootKey(pidlKey, &hRootKey);

//assemble the key string
dwNameSize = GetSubKeyText(pidlKey, NULL, 0);
lpszSubKey = new TCHAR[dwNameSize];
if(!lpszSubKey)
   return dwReturn;
GetSubKeyText(pidlKey, lpszSubKey, dwNameSize);

//assemble the value name
dwNameSize = GetValueText(pidlValue, NULL, 0);
lpszValueName = new TCHAR[dwNameSize];
if(!lpszValueName)
   {
   delete lpszSubKey;
   return dwReturn;
   }
GetValueText(pidlValue, lpszValueName, dwNameSize);

//open the key
lResult = RegOpenKeyEx( hRootKey,
                        lpszSubKey,
                        0,
                        KEY_QUERY_VALUE,
                        &hKey);

if(ERROR_SUCCESS != lResult)
   return 0;

//get the value
dwSize = sizeof(szData);
lResult = RegQueryValueEx( hKey,
                           lpszValueName,
                           NULL,
                           &dwType,
                           (LPBYTE)szData,
                           &dwSize);

/*
If this fails, then there is no default value set, so go ahead and add the
empty default value.
*/
if(ERROR_SUCCESS == lResult)
   {
   //format the data
   switch(dwType)
      {
      case REG_BINARY:
      case REG_DWORD:
      case REG_DWORD_BIG_ENDIAN:
         {
         LPTSTR   lpszBuffer,
                  lpszTemp;
         LPBYTE   pData = (LPBYTE)szData;

         lpszBuffer = lpszTemp = new TCHAR[dwSize * sizeof(TCHAR) * 3 + sizeof(TCHAR)];

         if(lpszTemp)
            {
            DWORD dwTemp;

            for(dwTemp = 0; dwTemp < dwSize; dwTemp++)
               {
               wsprintf(lpszTemp, TEXT("%02x"), *(pData + dwTemp));
               lstrcat(lpszTemp, TEXT(" "));
               lpszTemp = lpszBuffer + lstrlen(lpszBuffer);
               }

            lstrcpyn(lpszOut, lpszBuffer, dwOutSize);

            delete lpszBuffer;
            }
         }
         break;

      default:
         lstrcpyn(lpszOut, szData, dwOutSize);
         break;
      }
   dwReturn = lstrlen(lpszOut);
   }

RegCloseKey(hKey);

delete lpszSubKey;

delete lpszValueName;

return dwReturn;
}

/**************************************************************************

   CPidlMgr::GetPidlPath()

   Create a string that includes the root key name, the sub key text and
   the value text.

**************************************************************************/

DWORD CPidlMgr::GetPidlPath(LPCITEMIDLIST pidl, LPTSTR lpszOut, DWORD dwOutSize)
{
LPTSTR   lpszTemp;

if(!lpszOut)
   return 0;

*lpszOut = 0;
lpszTemp = lpszOut;

//add the root key text if necessary
if(IsRootKey(pidl))
   {
   HKEY  hKey;

   GetRootKey(pidl, &hKey);
   dwOutSize -= GetRootKeyText(hKey, lpszOut, dwOutSize);
   }

//add a backslash if necessary
if(*lpszOut && (*(lpszOut + lstrlen(lpszOut) - 1) != '\\'))
   lstrcat(lpszOut, TEXT("\\"));

lpszTemp = lpszOut + lstrlen(lpszOut);

//add the subkey string
dwOutSize -= GetSubKeyText(pidl, lpszTemp, dwOutSize);

//add a backslash if necessary
if(*lpszOut && (*(lpszOut + lstrlen(lpszOut) - 1) != '\\'))
   lstrcat(lpszOut, TEXT("\\"));

lpszTemp = lpszOut + lstrlen(lpszOut);

//add the value string
GetValueText(pidl, lpszTemp, dwOutSize);

//remove the last backslash if necessary
if(*(lpszOut + lstrlen(lpszOut) - 1) == '\\')
   {
   *(lpszOut + lstrlen(lpszOut) - 1) = 0;
   }

return lstrlen(lpszOut);
}

/**************************************************************************

   CPidlMgr::Concatenate()

   Create a new PIDL by combining two existing PIDLs.

**************************************************************************/

LPITEMIDLIST CPidlMgr::Concatenate(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
LPITEMIDLIST   pidlNew;
UINT           cb1 = 0,
               cb2 = 0;

//are both of these NULL?
if(!pidl1 && !pidl2)
   return NULL;

//if pidl1 is NULL, just return a copy of pidl2
if(!pidl1)
   {
   pidlNew = Copy(pidl2);

   return pidlNew;
   }

//if pidl2 is NULL, just return a copy of pidl1
if(!pidl2)
   {
   pidlNew = Copy(pidl1);

   return pidlNew;
   }

cb1 = GetSize(pidl1) - sizeof(ITEMIDLIST);

cb2 = GetSize(pidl2);

//create the new PIDL
pidlNew = (LPITEMIDLIST)m_pMalloc->Alloc(cb1 + cb2);

if(pidlNew)
   {
   //copy the first PIDL
   CopyMemory(pidlNew, pidl1, cb1);

   //copy the second PIDL
   CopyMemory(((LPBYTE)pidlNew) + cb1, pidl2, cb2);
   }

return pidlNew;
}

/**************************************************************************

   CPidlMgr::SeparateKeyAndValue()

   Creates a separate key and value PIDL from a fully qualified PIDL.

**************************************************************************/

BOOL CPidlMgr::SeparateKeyAndValue( LPCITEMIDLIST pidlFQ,
                                    LPITEMIDLIST *ppidlKey,
                                    LPITEMIDLIST*ppidlValue)
{
if(!pidlFQ)
   return FALSE;

*ppidlValue = GetLastItem(pidlFQ);
if(!IsValue(*ppidlValue))
   return FALSE;

//make a copy of the value pidl
*ppidlValue = Copy(*ppidlValue);

//make a copy of the fully qualified PIDL
*ppidlKey = Copy(pidlFQ);

//truncate the last item of the key PIDL
LPITEMIDLIST   pidlTemp = GetLastItem(*ppidlKey);
pidlTemp->mkid.cb = 0;
pidlTemp->mkid.abID[0] = 0;

return TRUE;
}


PIDLMGR.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          PidlMgr.h

   Description:   CPidlMgr definitions.

**************************************************************************/

#ifndef PIDLMGR_H
#define PIDLMGR_H

#include <windows.h>
#include <shlobj.h>

/**************************************************************************
   data types
**************************************************************************/

typedef enum tagPIDLTYPE
   {
   PT_ROOTKEY = 0x00000000,
   PT_SUBKEY = 0x00000001,
   PT_VALUE = 0x00000002,
   PT_TEXT = PT_SUBKEY | PT_VALUE
   }PIDLTYPE;

typedef struct tagPIDLDATA
   {
   PIDLTYPE type;
   union{
      HKEY     hRootKey;
      TCHAR    szText[1];
      };
   }PIDLDATA, FAR *LPPIDLDATA;

/**************************************************************************
   global variables
**************************************************************************/

extern HINSTANCE  g_hInst;
extern UINT       g_DllRefCount;

/**************************************************************************

   CPidlMgr class definition

**************************************************************************/

class CPidlMgr
{
public:
   CPidlMgr();
   ~CPidlMgr();
   LPITEMIDLIST CreateRootKey(HKEY);
   LPITEMIDLIST CreateSubKey(LPCTSTR);
   LPITEMIDLIST CreateValue(LPCTSTR);
   void Delete(LPITEMIDLIST);
   LPITEMIDLIST GetNextItem(LPCITEMIDLIST);
   LPITEMIDLIST Copy(LPCITEMIDLIST);
   UINT GetSize(LPCITEMIDLIST);
   BOOL GetRootKey(LPCITEMIDLIST, HKEY*);
   LPITEMIDLIST GetLastItem(LPCITEMIDLIST);
   DWORD GetItemText(LPCITEMIDLIST, LPTSTR, USHORT);
   BOOL IsRootKey(LPCITEMIDLIST);
   BOOL IsSubKey(LPCITEMIDLIST);
   BOOL IsValue(LPCITEMIDLIST);
   BOOL HasSubKeys(HKEY, LPCTSTR, LPCITEMIDLIST);
   DWORD GetSubKeyText(LPCITEMIDLIST, LPTSTR, DWORD);
   DWORD GetValueText(LPCITEMIDLIST, LPTSTR, DWORD);

   BOOL GetValueType(LPCITEMIDLIST, LPDWORD);
   BOOL GetValueType(LPCITEMIDLIST, LPCITEMIDLIST, LPDWORD);

   DWORD GetDataText(LPCITEMIDLIST, LPTSTR, DWORD);
   DWORD GetDataText(LPCITEMIDLIST, LPCITEMIDLIST, LPTSTR, DWORD);

   DWORD GetPidlPath(LPCITEMIDLIST, LPTSTR, DWORD);
   LPITEMIDLIST Concatenate(LPCITEMIDLIST, LPCITEMIDLIST);

private:
   LPMALLOC m_pMalloc;

   LPITEMIDLIST Create(PIDLTYPE, LPVOID, USHORT);
   DWORD GetData(PIDLTYPE, LPCITEMIDLIST, LPVOID, USHORT);
   LPPIDLDATA GetDataPointer(LPCITEMIDLIST);
   BOOL SeparateKeyAndValue(LPCITEMIDLIST, LPITEMIDLIST*, LPITEMIDLIST*);
};

typedef CPidlMgr FAR *LPPIDLMGR;

#endif   //PIDLMGR_H


REGVIEW.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          RegView.cpp

   Description:   Contains DLLMain and standard OLE COM object creation stuff.

**************************************************************************/

/**************************************************************************
   #include statements
**************************************************************************/

#include "ShlView.h"
#include "ClsFact.h"
#include "Utility.h"
#include <olectl.h>

/**************************************************************************
   GUID stuff
**************************************************************************/

//this part is only done once
//if you need to use the GUID in another file, just include Guid.h
#pragma data_seg(".text")
#define INITGUID
#include <initguid.h>
#include <shlguid.h>
#include "Guid.h"
#pragma data_seg()

/**************************************************************************
   private function prototypes
**************************************************************************/

extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID);

/**************************************************************************
   global variables
**************************************************************************/

HINSTANCE   g_hInst;
UINT        g_DllRefCount;
int         g_nColumn1;
int         g_nColumn2;
BOOL        g_bViewKeys;
BOOL        g_bShowIDW;
HIMAGELIST  g_himlLarge;
HIMAGELIST  g_himlSmall;

/**************************************************************************

   DllMain

**************************************************************************/

extern "C" BOOL WINAPI DllMain(  HINSTANCE hInstance,
                                 DWORD dwReason,
                                 LPVOID lpReserved)
{
switch(dwReason)
   {
   case DLL_PROCESS_ATTACH:
      g_hInst = hInstance;

      GetGlobalSettings();

      //create the global image lists
      CreateImageLists();
      break;

   case DLL_PROCESS_DETACH:
      SaveGlobalSettings();

      //destroy the global image lists
      DestroyImageLists();
      break;
   }

return TRUE;
}

/**************************************************************************

   DllCanUnloadNow

**************************************************************************/

STDAPI DllCanUnloadNow(void)
{
return (g_DllRefCount ? S_FALSE : S_OK);
}

/**************************************************************************

   DllGetClassObject

**************************************************************************/

STDAPI DllGetClassObject(  REFCLSID rclsid,
                           REFIID riid,
                           LPVOID *ppReturn)
{
*ppReturn = NULL;

//if we don't support this classid, return the proper error code
if(!IsEqualCLSID(rclsid, CLSID_RegViewNameSpace))
   return CLASS_E_CLASSNOTAVAILABLE;

//create a CClassFactory object and check it for validity
CClassFactory *pClassFactory = new CClassFactory();
if(NULL == pClassFactory)
   return E_OUTOFMEMORY;

//get the QueryInterface return for our return value
HRESULT hResult = pClassFactory->QueryInterface(riid, ppReturn);

//call Release to decement the ref count - creating the object set it to one
//and QueryInterface incremented it - since its being used externally (not by
//us), we only want the ref count to be 1
pClassFactory->Release();

//return the result from QueryInterface
return hResult;
}

/**************************************************************************

   DllRegisterServer

**************************************************************************/

typedef struct{
   HKEY  hRootKey;
   LPTSTR lpszSubKey;
   LPTSTR lpszValueName;
   LPTSTR lpszData;
}REGSTRUCT, *LPREGSTRUCT;

STDAPI DllRegisterServer(void)
{
int      i;
HKEY     hKey;
LRESULT  lResult;
DWORD    dwDisp;
TCHAR    szSubKey[MAX_PATH];
TCHAR    szCLSID[MAX_PATH];
TCHAR    szModule[MAX_PATH];
LPWSTR   pwsz;

//get the CLSID in string form
StringFromIID(CLSID_RegViewNameSpace, &pwsz);

if(pwsz)
   {
   WideCharToLocal(szCLSID, pwsz, ARRAYSIZE(szCLSID));

   //free the string
   LPMALLOC pMalloc;
   CoGetMalloc(1, &pMalloc);
   if(pMalloc)
      {
      pMalloc->Free(pwsz);
      pMalloc->Release();
      }
   }

//get this DLL's path and file name
GetModuleFileName(g_hInst, szModule, ARRAYSIZE(szModule));

//register the CLSID entries
REGSTRUCT ClsidEntries[] = {  HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s"),                  NULL,                   TEXT("Registry View"),
                              HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s\\InprocServer32"),  NULL,                   TEXT("%s"),
                              HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s\\InprocServer32"),  TEXT("ThreadingModel"), TEXT("Apartment"),
                              HKEY_CLASSES_ROOT,   TEXT("CLSID\\%s\\DefaultIcon"),     NULL,                   TEXT("%s,0"),
                              NULL,                NULL,                               NULL,                   NULL};

for(i = 0; ClsidEntries[i].hRootKey; i++)
   {
   //Create the sub key string.
   wsprintf(szSubKey, ClsidEntries[i].lpszSubKey, szCLSID);

   lResult = RegCreateKeyEx(  ClsidEntries[i].hRootKey,
                              szSubKey,
                              0,
                              NULL,
                              REG_OPTION_NON_VOLATILE,
                              KEY_WRITE,
                              NULL,
                              &hKey,
                              &dwDisp);

   if(NOERROR == lResult)
      {
      TCHAR szData[MAX_PATH];

      //if necessary, create the value string
      wsprintf(szData, ClsidEntries[i].lpszData, szModule);

      lResult = RegSetValueEx(   hKey,
                                 ClsidEntries[i].lpszValueName,
                                 0,
                                 REG_SZ,
                                 (LPBYTE)szData,
                                 lstrlen(szData) + 1);

      RegCloseKey(hKey);
      }
   else
      return SELFREG_E_CLASS;
   }

//Register the default flags for the folder.

wsprintf(   szSubKey,
            TEXT("CLSID\\%s\\ShellFolder"),
            szCLSID);

lResult = RegCreateKeyEx(  HKEY_CLASSES_ROOT,
                           szSubKey,
                           0,
                           NULL,
                           REG_OPTION_NON_VOLATILE,
                           KEY_WRITE,
                           NULL,
                           &hKey,
                           &dwDisp);

if(NOERROR == lResult)
   {
   DWORD dwData = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;

   lResult = RegSetValueEx(   hKey,
                              TEXT("Attributes"),
                              0,
                              REG_BINARY,
                              (LPBYTE)&dwData,
                              sizeof(dwData));

   RegCloseKey(hKey);
   }
else
   return SELFREG_E_CLASS;

//Register the name space extension

/*
Create the sub key string. Change this from "...Desktop..." to
"...MyComputer..." if desired.
*/
wsprintf(   szSubKey,
            TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\%s"),
            szCLSID);

lResult = RegCreateKeyEx(  HKEY_LOCAL_MACHINE,
                           szSubKey,
                           0,
                           NULL,
                           REG_OPTION_NON_VOLATILE,
                           KEY_WRITE,
                           NULL,
                           &hKey,
                           &dwDisp);

if(NOERROR == lResult)
   {
   TCHAR szData[MAX_PATH];

   //Create the value string.
   lstrcpy(szData, TEXT("Registry View"));

   lResult = RegSetValueEx(   hKey,
                              NULL,
                              0,
                              REG_SZ,
                              (LPBYTE)szData,
                              lstrlen(szData) + 1);

   RegCloseKey(hKey);
   }
else
   return SELFREG_E_CLASS;

//If running on NT, register the extension as approved.
OSVERSIONINFO  osvi;

osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);

if(VER_PLATFORM_WIN32_NT == osvi.dwPlatformId)
   {
   lstrcpy( szSubKey, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"));

   lResult = RegCreateKeyEx(  HKEY_LOCAL_MACHINE,
                              szSubKey,
                              0,
                              NULL,
                              REG_OPTION_NON_VOLATILE,
                              KEY_WRITE,
                              NULL,
                              &hKey,
                              &dwDisp);

   if(NOERROR == lResult)
      {
      TCHAR szData[MAX_PATH];

      //Create the value string.
      lstrcpy(szData, TEXT("Registry View"));

      lResult = RegSetValueEx(   hKey,
                                 szCLSID,
                                 0,
                                 REG_SZ,
                                 (LPBYTE)szData,
                                 lstrlen(szData) + 1);

      RegCloseKey(hKey);
      }
   else
      return SELFREG_E_CLASS;
   }

return S_OK;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by RegView.rc
//
#define IDS_REGISTRY_TITLE              1
#define IDS_MI_REGISTRY                 2
#define IDS_MI_VIEW_KEYS                3
#define IDS_TB_VIEW_KEYS                4
#define IDS_MI_VIEW_IDW                 5
#define IDS_TB_VIEW_IDW                 6
#define IDS_COLUMN1                     7
#define IDS_COLUMN2                     8
#define IDS_DEFAULT                     9
#define IDS_NODATA                      10
#define IDS_KEY                         11
#define IDS_MI_FILEITEM                 12

#define IDI_REGVIEW                     100
#define IDI_BINARY                      101
#define IDI_FOLDER                      102
#define IDI_FOLDEROPEN                  103
#define IDI_STRING                      104

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        112
#define _APS_NEXT_COMMAND_VALUE         40002
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SHLFLDR.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          ShlFldr.cpp

   Description:   Implements CShellFolder.

**************************************************************************/

/**************************************************************************
   #include statements
**************************************************************************/

#include "ShlFldr.h"
#include "ShlView.h"
#include "InfoTip.h"
#include "ExtrIcon.h"
#include "ContMenu.h"
#include "Guid.h"
#include "resource.h"

/**************************************************************************
   global variables
**************************************************************************/

HKEY  g_ahKey[] = {  HKEY_CLASSES_ROOT,
                     HKEY_CURRENT_USER,
                     HKEY_LOCAL_MACHINE,
                     HKEY_USERS,
                     HKEY_PERFORMANCE_DATA,
                     HKEY_CURRENT_CONFIG,
                     HKEY_DYN_DATA,
                     (HKEY)-1};

/**************************************************************************

   CShellFolder::CShellFolder()

**************************************************************************/

CShellFolder::CShellFolder(CShellFolder *pParent, LPCITEMIDLIST pidl)
{
m_hKeyRoot = NULL;
m_lpszSubKey = NULL;

m_pSFParent = pParent;
if(m_pSFParent)
   m_pSFParent->AddRef();

m_pPidlMgr = new CPidlMgr();
if(!m_pPidlMgr)
   {
   delete this;
   return;
   }

//get the shell's IMalloc pointer
//we'll keep this until we get destroyed
if(FAILED(SHGetMalloc(&m_pMalloc)))
   {
   delete this;
   return;
   }

m_pidlRel = m_pPidlMgr->Copy(pidl);
m_pidlFQ = NULL;

if(m_pidlRel)
   {
   //determine if this PIDL has an HKEY or a sub key string
   if(m_pPidlMgr->IsRootKey(m_pidlRel))
      {
      //this is a root key
      m_pPidlMgr->GetRootKey(m_pidlRel, &m_hKeyRoot);
      }

   DWORD dwSize = m_pPidlMgr->GetSubKeyText(m_pidlRel, NULL, 0);
   m_lpszSubKey = new TCHAR[dwSize];
   if(m_lpszSubKey)
      {
      m_pPidlMgr->GetSubKeyText(m_pidlRel, m_lpszSubKey, dwSize);
      }
   }

m_ObjRefCount = 1;

g_DllRefCount++;
}

/**************************************************************************

   CShellFolder::~CShellFolder()

**************************************************************************/

CShellFolder::~CShellFolder()
{
if(m_pidlRel)
   {
   m_pPidlMgr->Delete(m_pidlRel);
   m_pidlRel = NULL;
   }

if(m_pidlFQ)
   {
   m_pPidlMgr->Delete(m_pidlFQ);
   m_pidlFQ = NULL;
   }

if(m_lpszSubKey)
   delete m_lpszSubKey;

if(m_pSFParent)
   m_pSFParent->Release();

if(m_pMalloc)
   {
   m_pMalloc->Release();
   }

if(m_pPidlMgr)
   {
   delete m_pPidlMgr;
   }

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

/**************************************************************************

   CShellFolder::QueryInterface

**************************************************************************/

STDMETHODIMP CShellFolder::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IPersistFolder
else if(IsEqualIID(riid, IID_IPersistFolder))
   {
   *ppReturn = (IPersistFolder*)this;
   }

//IShellFolder
else if(IsEqualIID(riid, IID_IShellFolder))
   {
   *ppReturn = (IShellFolder*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

/**************************************************************************

   CShellFolder::AddRef

**************************************************************************/

STDMETHODIMP_(DWORD) CShellFolder::AddRef()
{
return ++m_ObjRefCount;
}

/**************************************************************************

   CShellFolder::Release

**************************************************************************/

STDMETHODIMP_(DWORD) CShellFolder::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IPersist Implementation
//

/**************************************************************************

   CShellView::GetClassID()

**************************************************************************/

STDMETHODIMP CShellFolder::GetClassID(LPCLSID lpClassID)
{
*lpClassID = CLSID_RegViewNameSpace;

return S_OK;
}

///////////////////////////////////////////////////////////////////////////
//
// IPersistFolder Implementation
//

/**************************************************************************

   CShellView::Initialize()

**************************************************************************/

STDMETHODIMP CShellFolder::Initialize(LPCITEMIDLIST pidl)
{
if(m_pidlFQ)
   {
   m_pPidlMgr->Delete(m_pidlFQ);
   m_pidlFQ = NULL;
   }

m_pidlFQ = m_pPidlMgr->Copy(pidl);

return S_OK;
}

///////////////////////////////////////////////////////////////////////////
//
// IShellFolder Implementation
//

/**************************************************************************

   CShellFolder::BindToObject()

**************************************************************************/

STDMETHODIMP CShellFolder::BindToObject(  LPCITEMIDLIST pidl,
                                          LPBC pbcReserved,
                                          REFIID riid,
                                          LPVOID *ppvOut)
{
*ppvOut = NULL;

CShellFolder   *pShellFolder = new CShellFolder(this, pidl);
if(!pShellFolder)
   return E_OUTOFMEMORY;

LPITEMIDLIST   pidlFQ;

pidlFQ = m_pPidlMgr->Concatenate(m_pidlFQ, pidl);
pShellFolder->Initialize(pidlFQ);
m_pPidlMgr->Delete(pidlFQ);

HRESULT  hr = pShellFolder->QueryInterface(riid, ppvOut);

pShellFolder->Release();

return hr;
}

/**************************************************************************

   CShellFolder::BindToStorage()

**************************************************************************/

STDMETHODIMP CShellFolder::BindToStorage( LPCITEMIDLIST pidl,
                                          LPBC pbcReserved,
                                          REFIID riid,
                                          LPVOID *ppvOut)
{
*ppvOut = NULL;

return E_NOTIMPL;
}

/**************************************************************************

   CShellFolder::CompareIDs()

**************************************************************************/

STDMETHODIMP CShellFolder::CompareIDs( LPARAM lParam,
                                       LPCITEMIDLIST pidl1,
                                       LPCITEMIDLIST pidl2)
{
TCHAR szString1[MAX_PATH] = TEXT("");
TCHAR szString2[MAX_PATH] = TEXT("");
HKEY  hkey1 = NULL,
      hkey2 = NULL;
int   nReturn;

/*
Special case - If one of the items is a key and the other is a value, always
make the key come before the value.
*/
LPCITEMIDLIST  pidlTemp1 = pidl1,
               pidlTemp2 = pidl2;

//get the last item in each list
while((m_pPidlMgr->GetNextItem(pidlTemp1))->mkid.cb)
   pidlTemp1 = m_pPidlMgr->GetNextItem(pidlTemp1);
while((m_pPidlMgr->GetNextItem(pidlTemp2))->mkid.cb)
   pidlTemp2 = m_pPidlMgr->GetNextItem(pidlTemp2);

//at this point, both pidlTemp1 and pidlTemp2 point to the last item in the list
if(m_pPidlMgr->IsValue(pidlTemp1) != m_pPidlMgr->IsValue(pidlTemp2))
   {
   if(m_pPidlMgr->IsValue(pidlTemp1))
      return 1;

   return -1;
   }

//get the numeric value of the root key
m_pPidlMgr->GetRootKey(pidl1, &hkey1);
m_pPidlMgr->GetRootKey(pidl2, &hkey2);

//compare the root keys
if(hkey1 != hkey2)
   {
   return (int)((DWORD)hkey1 - (DWORD)hkey2);
   }

//now compare the subkey strings
m_pPidlMgr->GetSubKeyText( pidl1,
                           szString1,
                           sizeof(szString1));
m_pPidlMgr->GetSubKeyText( pidl2,
                           szString2,
                           sizeof(szString2));
nReturn = lstrcmpi(szString1, szString2);
if(nReturn)
   return nReturn;

//now compare the value strings
m_pPidlMgr->GetValueText(  pidl1,
                           szString1,
                           sizeof(szString1));
m_pPidlMgr->GetValueText(  pidl2,
                           szString2,
                           sizeof(szString2));
return lstrcmpi(szString1, szString2);
}

/**************************************************************************

   CShellFolder::CreateViewObject()

**************************************************************************/

STDMETHODIMP CShellFolder::CreateViewObject( HWND hwndOwner,
                                             REFIID riid,
                                             LPVOID *ppvOut)
{
HRESULT     hr;
CShellView  *pShellView;

*ppvOut = NULL;

pShellView = new CShellView(this, m_pidlRel);
if(!pShellView)
   return E_OUTOFMEMORY;

hr = pShellView->QueryInterface(riid, ppvOut);

pShellView->Release();

return hr;
}

/**************************************************************************

   CShellFolder::EnumObjects()

**************************************************************************/

STDMETHODIMP CShellFolder::EnumObjects(   HWND hwndOwner,
                                          DWORD dwFlags,
                                          LPENUMIDLIST *ppEnumIDList)
{
HRESULT  hr;

*ppEnumIDList = NULL;

*ppEnumIDList = new CEnumIDList(GetRootKey(), m_lpszSubKey, dwFlags, &hr);
if(!*ppEnumIDList)
   return hr;

return S_OK;
}

/**************************************************************************

   CShellFolder::GetAttributesOf()

**************************************************************************/

STDMETHODIMP CShellFolder::GetAttributesOf(  UINT uCount,
                                             LPCITEMIDLIST aPidls[],
                                             LPDWORD pdwAttribs)
{
UINT  i;

*pdwAttribs = (DWORD)-1;

for(i = 0; i < uCount; i++)
   {
   DWORD dwAttribs = 0;

   //flags common to all items
   dwAttribs |= 0;

   //is this item a key?
   if(!m_pPidlMgr->IsValue(m_pPidlMgr->GetLastItem(aPidls[i])))
      {
      dwAttribs |= SFGAO_FOLDER;

      //does this item have a sub folder?
      if(m_pPidlMgr->HasSubKeys(GetRootKey(), m_lpszSubKey, aPidls[i]))
         dwAttribs |= SFGAO_HASSUBFOLDER;
      }

   *pdwAttribs &= dwAttribs;
   }

return S_OK;
}

/**************************************************************************

   CShellFolder::GetUIObjectOf()

**************************************************************************/

STDMETHODIMP CShellFolder::GetUIObjectOf( HWND hwndOwner,
                                          UINT uCount,
                                          LPCITEMIDLIST *pPidl,
                                          REFIID riid,
                                          LPUINT puReserved,
                                          LPVOID *ppvReturn)
{
*ppvReturn = NULL;

if(IsEqualIID(riid, IID_IContextMenu))
   {
   CContextMenu   *pcm = new CContextMenu(this, pPidl, uCount);

   if(pcm)
      {
      *ppvReturn = pcm;
      return S_OK;
      }
   }

if(uCount != 1)
   return E_FAIL;

if(IsEqualIID(riid, IID_IExtractIcon))
   {
   CExtractIcon   *pei;
   LPITEMIDLIST   pidl;

   pidl = m_pPidlMgr->Concatenate(m_pidlRel, pPidl[0]);

   pei = new CExtractIcon(pidl);

   /*
   The temp PIDL can be deleted because the new CExtractIcon either failed or
   made its own copy of it.
   */
   m_pPidlMgr->Delete(pidl);

   if(pei)
      {
      *ppvReturn = pei;
      return S_OK;
      }

   return E_OUTOFMEMORY;
   }

#if (_WIN32_IE >= 0x0400)
if(IsEqualIID(riid, IID_IQueryInfo))
   {
   CQueryInfo     *pqit;
   LPITEMIDLIST   pidl;

   pidl = m_pPidlMgr->Concatenate(m_pidlRel, pPidl[0]);

   pqit = new CQueryInfo(pidl);

   /*
   The temp PIDL can be deleted because the new CQueryInfo either failed or
   made its own copy of it.
   */
   m_pPidlMgr->Delete(pidl);

   if(pqit)
      {
      *ppvReturn = pqit;
      return S_OK;
      }

   return E_OUTOFMEMORY;
   }
#endif   //#if (_WIN32_IE >= 0x0400)

return E_NOINTERFACE;
}

/**************************************************************************

   CShellFolder::GetDisplayNameOf()

**************************************************************************/

#define GET_SHGDN_FOR(dwFlags)         ((DWORD)dwFlags & (DWORD)0x0000FF00)
#define GET_SHGDN_RELATION(dwFlags)    ((DWORD)dwFlags & (DWORD)0x000000FF)

STDMETHODIMP CShellFolder::GetDisplayNameOf( LPCITEMIDLIST pidl,
                                             DWORD dwFlags,
                                             LPSTRRET lpName)
{
TCHAR szText[MAX_PATH];
int   cchOleStr;

switch(GET_SHGDN_RELATION(dwFlags))
   {
   case SHGDN_NORMAL:
      //get the full name
      m_pPidlMgr->GetPidlPath(pidl, szText, sizeof(szText));

      //If the text is NULL and this is a value, get the default item name.
      if(!*szText && m_pPidlMgr->IsValue(m_pPidlMgr->GetLastItem(pidl)))
         {
         LoadString(g_hInst, IDS_DEFAULT, szText, sizeof(szText));
         }
      break;

   case SHGDN_INFOLDER:
      {
      LPITEMIDLIST   pidlTemp = m_pPidlMgr->GetLastItem(pidl);

      //get the relative name
      m_pPidlMgr->GetItemText(pidlTemp, szText, sizeof(szText));

      //If the text is NULL and this is a value, get the default item name.
      if(!*szText && m_pPidlMgr->IsValue(pidlTemp))
         {
         LoadString(g_hInst, IDS_DEFAULT, szText, sizeof(szText));
         }
      }
      break;

   default:
      return E_INVALIDARG;
   }

//get the number of characters required
cchOleStr = lstrlen(szText) + 1;

//allocate the wide character string
lpName->pOleStr = (LPWSTR)m_pMalloc->Alloc(cchOleStr * sizeof(WCHAR));
if(!lpName->pOleStr)
   return E_OUTOFMEMORY;

lpName->uType = STRRET_WSTR;

LocalToWideChar(lpName->pOleStr, szText, cchOleStr);

return S_OK;
}

/**************************************************************************

   CShellFolder::ParseDisplayName()

**************************************************************************/

STDMETHODIMP CShellFolder::ParseDisplayName( HWND hwndOwner,
                                             LPBC pbcReserved,
                                             LPOLESTR lpDisplayName,
                                             LPDWORD pdwEaten,
                                             LPITEMIDLIST *pPidlNew,
                                             LPDWORD pdwAttributes)
{
HRESULT        hr = E_OUTOFMEMORY;
LPITEMIDLIST   pidlFull = NULL;
DWORD          dwChars = lstrlenW(lpDisplayName) + 1;
LPTSTR         pszTemp = (LPTSTR)GlobalAlloc(GPTR, dwChars * sizeof(TCHAR));

if(pszTemp)
   {
   hr = E_FAIL;

   WideCharToLocal(pszTemp, lpDisplayName, dwChars);

   if(*pszTemp)
      {
      LPTSTR         pszNext;
      TCHAR          szElement[MAX_PATH];
      int            i;
      BOOL           fValue;

      //get the root key

      pszNext = GetNextRegElement(pszTemp, szElement, ARRAYSIZE(szElement));

      for(i = 0; (-1 != (int)g_ahKey[i]) && !pidlFull; i++)
         {
         TCHAR szKeyText[MAX_PATH];

         GetRootKeyText(g_ahKey[i], szKeyText, ARRAYSIZE(szKeyText));

         if(0 == lstrcmpi(szElement, szKeyText))
            {
            pidlFull = m_pPidlMgr->CreateRootKey(g_ahKey[i]);
            }
         }

      if(pidlFull)
         {
         LPITEMIDLIST   pidlTemp = NULL;
         LPITEMIDLIST   pidlOld = NULL;
         HKEY           hKey;

         //check to see if the last item is a value or a key
         if(NOERROR == RegOpenKeyEx(   g_ahKey[i],
                                       pszNext,
                                       0,
                                       KEY_READ,
                                       &hKey))
            {
            fValue = FALSE;
            RegCloseKey(hKey);
            }
         else
            fValue = TRUE;

         //get the remaining keys
         while(pszNext = GetNextRegElement(pszNext, szElement, ARRAYSIZE(szElement)))
            {
            //if this is the last element, check to see if it is a value or a key
            if(!*pszNext && fValue)
               pidlTemp = m_pPidlMgr->CreateValue(szElement);
            else
               pidlTemp = m_pPidlMgr->CreateSubKey(szElement);

            pidlOld = pidlFull;
            pidlFull = m_pPidlMgr->Concatenate(pidlFull, pidlTemp);
            m_pPidlMgr->Delete(pidlOld);
            }

         hr = S_OK;
         }
      }

   GlobalFree((HGLOBAL)pszTemp);
   }

*pPidlNew = pidlFull;

return hr;
}

/**************************************************************************

   CShellFolder::SetNameOf()

**************************************************************************/

STDMETHODIMP CShellFolder::SetNameOf(  HWND hwndOwner,
                                       LPCITEMIDLIST pidl,
                                       LPCOLESTR lpName,
                                       DWORD dw,
                                       LPITEMIDLIST *pPidlOut)
{
return E_NOTIMPL;
}

/**************************************************************************

   CShellFolder::GetFolderPath()

**************************************************************************/

BOOL CShellFolder::GetFolderPath(LPTSTR lpszOut, DWORD dwOutSize)
{
TCHAR    szTemp[MAX_PATH] = TEXT("");
DWORD    dwSize;

if(!lpszOut)
   return FALSE;

*lpszOut = 0;

//get the text for the root key
GetRootKeyText(m_hKeyRoot, szTemp, sizeof(szTemp));

dwSize = lstrlen(szTemp);
if(m_lpszSubKey)
   dwSize += lstrlen(m_lpszSubKey) + 1;

//is the output buffer big enough?
if(dwSize > dwOutSize)
   return FALSE;

//add the text we already have
lstrcpy(lpszOut, szTemp);

//add this folder's text if present
if(m_lpszSubKey && *m_lpszSubKey)
   {
   lstrcat(lpszOut, TEXT("\\"));
   lstrcat(lpszOut, m_lpszSubKey);
   }

return TRUE;
}

/**************************************************************************

   CShellFolder::GetFolderText()

**************************************************************************/

BOOL CShellFolder::GetFolderText(LPTSTR lpszOut, DWORD dwOutSize)
{
if(!lpszOut)
   return FALSE;

*lpszOut = 0;

//add this folder's text if present
if(m_lpszSubKey && *m_lpszSubKey)
   {
   lstrcpyn(lpszOut, m_lpszSubKey, dwOutSize);
   }

return TRUE;
}

/**************************************************************************

   CShellFolder::GetRootKey()

**************************************************************************/

HKEY CShellFolder::GetRootKey(void)
{
HKEY  hkReturn = NULL;
CShellFolder   *pTemp = this;

while(pTemp)
   {
   if(pTemp->m_hKeyRoot)
      {
      hkReturn = pTemp->m_hKeyRoot;
      break;
      }

   pTemp = pTemp->m_pSFParent;
   }

return hkReturn;
}


SHLFLDR.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          ShlFldr.h

   Description:   CShellFolder definitions.

**************************************************************************/

#ifndef SHELLFOLDER_H
#define SHELLFOLDER_H

/**************************************************************************
   #include statements
**************************************************************************/

#include <windows.h>
#include <shlobj.h>

#include "EnumIDL.h"
#include "PidlMgr.h"

/**************************************************************************
   global variables
**************************************************************************/

extern HINSTANCE  g_hInst;
extern UINT       g_DllRefCount;

/**************************************************************************

   CShellFolder class definition

**************************************************************************/

class CShellFolder : public IShellFolder,
                     public IPersistFolder
{
friend class CShellView;
friend class CContextMenu;

protected:
   DWORD m_ObjRefCount;

public:
   CShellFolder(CShellFolder*, LPCITEMIDLIST);
   ~CShellFolder();

   //IUnknown methods
   STDMETHOD (QueryInterface) (REFIID riid, LPVOID * ppvObj);
   STDMETHOD_ (ULONG, AddRef) (void);
   STDMETHOD_ (ULONG, Release) (void);

   //IPersist methods
   STDMETHODIMP GetClassID(LPCLSID);

   //IPersistFolder methods
   STDMETHODIMP Initialize(LPCITEMIDLIST);

   //IShellFolder methods
   STDMETHOD (ParseDisplayName) (HWND, LPBC, LPOLESTR, LPDWORD, LPITEMIDLIST*, LPDWORD);
   STDMETHOD (EnumObjects) (HWND, DWORD, LPENUMIDLIST*);
   STDMETHOD (BindToObject) (LPCITEMIDLIST, LPBC, REFIID, LPVOID*);
   STDMETHOD (BindToStorage) (LPCITEMIDLIST, LPBC, REFIID, LPVOID*);
   STDMETHOD (CompareIDs) (LPARAM, LPCITEMIDLIST, LPCITEMIDLIST);
   STDMETHOD (CreateViewObject) (HWND, REFIID, LPVOID* );
   STDMETHOD (GetAttributesOf) (UINT, LPCITEMIDLIST*, LPDWORD);
   STDMETHOD (GetUIObjectOf) (HWND, UINT, LPCITEMIDLIST*, REFIID, LPUINT, LPVOID*);
   STDMETHOD (GetDisplayNameOf) (LPCITEMIDLIST, DWORD, LPSTRRET);
   STDMETHOD (SetNameOf) (HWND, LPCITEMIDLIST, LPCOLESTR, DWORD, LPITEMIDLIST*);

private:
LPITEMIDLIST m_pidlRel;
LPITEMIDLIST m_pidlFQ;
HKEY m_hKeyRoot;
LPTSTR m_lpszSubKey;
CShellFolder *m_pSFParent;
LPMALLOC m_pMalloc;
   LPPIDLMGR m_pPidlMgr;

   //utility functions
   BOOL GetFolderPath(LPTSTR, DWORD);
   BOOL GetFolderText(LPTSTR, DWORD);
HKEY GetRootKey(void);
};

#endif   //SHELLFOLDER_H


SHLVIEW.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          ShlView.cpp

   Description:   Implements IShellView.

**************************************************************************/

/**************************************************************************
   #include statements
**************************************************************************/

#include "ShlView.h"
#include "Guid.h"
#include "resource.h"
#include "tools.h"

#define TOOLBAR_ID   (L"RegView")

MYTOOLINFO g_Tools[] =
   {
   IDM_VIEW_KEYS, 0, IDS_TB_VIEW_KEYS, IDS_MI_VIEW_KEYS, 0, TBSTATE_ENABLED,  TBSTYLE_BUTTON,
   IDM_VIEW_IDW,  0, IDS_TB_VIEW_IDW,  IDS_MI_VIEW_IDW,  0, TBSTATE_ENABLED,  TBSTYLE_BUTTON,
   -1, 0, 0, 0, 0,
   };

/**************************************************************************

   CShellView::CShellView()

**************************************************************************/

CShellView::CShellView(CShellFolder *pFolder, LPCITEMIDLIST pidl)
{
#ifdef INITCOMMONCONTROLSEX

INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&iccex);

#else

InitCommonControls();

#endif   //INITCOMMONCONTROLSEX

m_hMenu = NULL;
m_pDockingWindow = NULL;

m_pPidlMgr = new CPidlMgr();
if(!m_pPidlMgr)
   {
   delete this;
   return;
   }

m_pSFParent = pFolder;
if(m_pSFParent)
   m_pSFParent->AddRef();

//get the shell's IMalloc pointer
//we'll keep this until we get destroyed
if(FAILED(SHGetMalloc(&m_pMalloc)))
   {
   delete this;
   return;
   }

m_pidl = m_pPidlMgr->Copy(pidl);

m_uState = SVUIA_DEACTIVATE;

m_ObjRefCount = 1;
g_DllRefCount++;
}

/**************************************************************************

   CShellView::~CShellView()

**************************************************************************/

CShellView::~CShellView()
{
if(m_pidl)
   {
   m_pPidlMgr->Delete(m_pidl);
   m_pidl = NULL;
   }

if(m_pSFParent)
   m_pSFParent->Release();

if(m_pMalloc)
   {
   m_pMalloc->Release();
   }

if(m_pPidlMgr)
   {
   delete m_pPidlMgr;
   }

g_DllRefCount--;
}

///////////////////////////////////////////////////////////////////////////
//
// IUnknown Implementation
//

/**************************************************************************

   CShellView::QueryInterface

**************************************************************************/

STDMETHODIMP CShellView::QueryInterface(REFIID riid, LPVOID *ppReturn)
{
*ppReturn = NULL;

//IUnknown
if(IsEqualIID(riid, IID_IUnknown))
   {
   *ppReturn = this;
   }

//IOleWindow
else if(IsEqualIID(riid, IID_IOleWindow))
   {
   *ppReturn = (IOleWindow*)this;
   }

//IShellView
else if(IsEqualIID(riid, IID_IShellView))
   {
   *ppReturn = (IShellView*)this;
   }

//IOleCommandTarget
else if(IsEqualIID(riid, IID_IOleCommandTarget))
   {
   *ppReturn = (IOleCommandTarget*)this;
   }

if(*ppReturn)
   {
   (*(LPUNKNOWN*)ppReturn)->AddRef();
   return S_OK;
   }

return E_NOINTERFACE;
}

/**************************************************************************

   CShellView::AddRef

**************************************************************************/

STDMETHODIMP_(DWORD) CShellView::AddRef()
{
return ++m_ObjRefCount;
}

/**************************************************************************

   CShellView::Release

**************************************************************************/

STDMETHODIMP_(DWORD) CShellView::Release()
{
if(--m_ObjRefCount == 0)
   {
   delete this;
   return 0;
   }

return m_ObjRefCount;
}

///////////////////////////////////////////////////////////////////////////
//
// IOleWindow Implementation
//

/**************************************************************************

   CShellView::GetWindow()

**************************************************************************/

STDMETHODIMP CShellView::GetWindow(HWND *phWnd)
{
*phWnd = m_hWnd;

return S_OK;
}

/**************************************************************************

   CShellView::ContextSensitiveHelp()

**************************************************************************/

STDMETHODIMP CShellView::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}

///////////////////////////////////////////////////////////////////////////
//
// IOleCommandTarget Implementation
//

/**************************************************************************

   CShellView::QueryStatus()

**************************************************************************/

STDMETHODIMP CShellView::QueryStatus(  const GUID *pguidCmdGroup,
                                       ULONG cCmds,
                                       OLECMD prgCmds[],
                                       OLECMDTEXT *pCmdText)
{
ULONG i;

//only process the commands for our command group
if(pguidCmdGroup && (*pguidCmdGroup != CLSID_CmdGrp))
   return OLECMDERR_E_UNKNOWNGROUP;

//make sure prgCmds is not NULL
if(!prgCmds)
   return E_POINTER;

//run through all of the commands and supply the correct information
for(i = 0; i < cCmds;i++)
   {
   switch(prgCmds[i].cmdID)
      {
      case IDM_VIEW_KEYS:
         prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
         break;
      }
   }

return S_OK;
}

/**************************************************************************

   CShellView::Exec()

**************************************************************************/

STDMETHODIMP CShellView::Exec(   const GUID *pguidCmdGroup,
                                 DWORD nCmdID,
                                 DWORD nCmdExecOpt,
                                 VARIANTARG *pvaIn,
                                 VARIANTARG *pvaOut)
{
//only process the commands for our command group
if(pguidCmdGroup && (*pguidCmdGroup == CLSID_CmdGrp))
   {
   OnCommand(nCmdID, 0, NULL);
   return S_OK;
   }

return OLECMDERR_E_UNKNOWNGROUP;
}

///////////////////////////////////////////////////////////////////////////
//
// IShellView Implementation
//

/**************************************************************************

   CShellView::TranslateAccelerator()

**************************************************************************/

STDMETHODIMP CShellView::TranslateAccelerator(LPMSG pMsg)
{
return E_NOTIMPL;
}

/**************************************************************************

   CShellView::EnableModeless()

**************************************************************************/

STDMETHODIMP CShellView::EnableModeless(BOOL fEnable)
{
return E_NOTIMPL;
}

/**************************************************************************

   CShellView::OnActivate()

**************************************************************************/

LRESULT CShellView::OnActivate(UINT uState)
{
//don't do anything if the state isn't really changing
if(m_uState == uState)
   return S_OK;

OnDeactivate();

//only do this if we are active
if(uState != SVUIA_DEACTIVATE)
   {
   //merge the menus
   m_hMenu = CreateMenu();

   if(m_hMenu)
      {
   OLEMENUGROUPWIDTHS   omw = {0, 0, 0, 0, 0, 0};
      MENUITEMINFO         mii;
      TCHAR                szText[MAX_PATH];

      m_pShellBrowser->InsertMenusSB(m_hMenu, &omw);

      //build the top level menu
      //get the menu item's text
      LoadString(g_hInst, IDS_MI_REGISTRY, szText, sizeof(szText));

      ZeroMemory(&mii, sizeof(mii));
      mii.cbSize = sizeof(mii);
      mii.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_STATE;
      mii.fType = MFT_STRING;
      mii.fState = MFS_ENABLED;
      mii.dwTypeData = szText;
      mii.hSubMenu = BuildRegistryMenu();

      //insert our menu into the menu bar
      if(mii.hSubMenu)
         {
         InsertMenuItem(m_hMenu, FCIDM_MENU_HELP, FALSE, &mii);
         }

      //get the view menu so we can merge with it
      ZeroMemory(&mii, sizeof(mii));
      mii.cbSize = sizeof(mii);
      mii.fMask = MIIM_SUBMENU;

      if(GetMenuItemInfo(m_hMenu, FCIDM_MENU_VIEW, FALSE, &mii))
         {
         MergeViewMenu(mii.hSubMenu);
         }

      //add the items that should only be added if we have the focus
      if(SVUIA_ACTIVATE_FOCUS == uState)
         {
         //get the file menu so we can merge with it
         ZeroMemory(&mii, sizeof(mii));
         mii.cbSize = sizeof(mii);
         mii.fMask = MIIM_SUBMENU;

         if(GetMenuItemInfo(m_hMenu, FCIDM_MENU_FILE, FALSE, &mii))
            {
            MergeFileMenu(mii.hSubMenu);
            }
         }

      m_pShellBrowser->SetMenuSB(m_hMenu, NULL, m_hWnd);
      }
   }

m_uState = uState;

return 0;
}

/**************************************************************************

   CShellView::OnDeactivate()

**************************************************************************/

void CShellView::OnDeactivate(void)
{
if(m_uState != SVUIA_DEACTIVATE)
   {
   if(m_hMenu)
      {
      m_pShellBrowser->SetMenuSB(NULL, NULL, NULL);

      m_pShellBrowser->RemoveMenusSB(m_hMenu);

      DestroyMenu(m_hMenu);

      m_hMenu = NULL;
      }

   m_uState = SVUIA_DEACTIVATE;
   }
}

/**************************************************************************

   CShellView::UIActivate()

   This function activates the view window. Note that activating it
   will not change the focus, while setting the focus will activate it.

**************************************************************************/

STDMETHODIMP CShellView::UIActivate(UINT uState)
{
//don't do anything if the state isn't really changing
if(m_uState == uState)
   return S_OK;

//OnActivate handles the menu merging and internal state
OnActivate(uState);

//remove the docking window
if(g_bShowIDW)
   AddRemoveDockingWindow(FALSE);

//only do this if we are active
if(uState != SVUIA_DEACTIVATE)
   {
   TCHAR szName[MAX_PATH];
   LRESULT  lResult;
   int      nPartArray[1] = {-1};

   //update the status bar
   LoadString(g_hInst, IDS_REGISTRY_TITLE, szName, sizeof(szName));

   m_pSFParent->GetFolderPath(szName + lstrlen(szName), sizeof(szName) - lstrlen(szName));

   //set the number of parts
   m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETPARTS, 1, (LPARAM)nPartArray, &lResult);

   //set the text for the parts
   m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 0, (LPARAM)szName, &lResult);

   //add the docking window if necessary
   if(g_bShowIDW)
      AddRemoveDockingWindow(TRUE);
   }

return S_OK;
}

/**************************************************************************

   CShellView::BuildRegistryMenu()

**************************************************************************/

HMENU CShellView::BuildRegistryMenu(void)
{
HMENU hSubMenu = CreatePopupMenu();

if(hSubMenu)
   {
   TCHAR          szText[MAX_PATH];
   MENUITEMINFO   mii;
   int            nTools,
                  i;
   //get the number of items in our global array
   for(nTools = 0; g_Tools[nTools].idCommand != -1; nTools++)
      {
      }

   //add the menu items
   for(i = 0; i < nTools; i++)
      {
      LoadString(g_hInst, g_Tools[i].idMenuString, szText, sizeof(szText));

      ZeroMemory(&mii, sizeof(mii));
      mii.cbSize = sizeof(mii);
      mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;

      if(TBSTYLE_SEP != g_Tools[i].bStyle)
         {
         mii.fType = MFT_STRING;
         mii.fState = MFS_ENABLED;
         mii.dwTypeData = szText;
         mii.wID = g_Tools[i].idCommand;
         }
      else
         {
         mii.fType = MFT_SEPARATOR;
         }

      //tack this item onto the end of the menu
      InsertMenuItem(hSubMenu, (UINT)-1, TRUE, &mii);
      }
   }

return hSubMenu;
}

/**************************************************************************

   CShellView::MergeFileMenu()

**************************************************************************/

void CShellView::MergeFileMenu(HMENU hSubMenu)
{
if(hSubMenu)
   {
   MENUITEMINFO   mii;
   TCHAR          szText[MAX_PATH];

   ZeroMemory(&mii, sizeof(mii));

   //add a separator
   mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
   mii.fType = MFT_SEPARATOR;
   mii.fState = MFS_ENABLED;

   //insert this item at the beginning of the menu
   InsertMenuItem(hSubMenu, 0, TRUE, &mii);

   //add the file menu items
   LoadString(g_hInst, IDS_MI_FILEITEM, szText, sizeof(szText));
   mii.cbSize = sizeof(mii);
   mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
   mii.fType = MFT_STRING;
   mii.fState = MFS_ENABLED;
   mii.dwTypeData = szText;
   mii.wID = IDM_MYFILEITEM;

   //insert this item at the beginning of the menu
   InsertMenuItem(hSubMenu, 0, TRUE, &mii);
   }
}

/**************************************************************************

   CShellView::MergeViewMenu()

**************************************************************************/

void CShellView::MergeViewMenu(HMENU hSubMenu)
{
if(hSubMenu)
   {
   MENUITEMINFO   mii;
   TCHAR          szText[MAX_PATH];

   ZeroMemory(&mii, sizeof(mii));

   //add a separator at the correct position in the menu
   mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
   mii.fType = MFT_SEPARATOR;
   mii.fState = MFS_ENABLED;
   InsertMenuItem(hSubMenu, FCIDM_MENU_VIEW_SEP_OPTIONS, FALSE, &mii);

   //add the view menu items at the correct position in the menu
   LoadString(g_hInst, IDS_MI_VIEW_KEYS, szText, sizeof(szText));
   mii.cbSize = sizeof(mii);
   mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
   mii.fType = MFT_STRING;
   mii.fState = MFS_ENABLED;
   mii.dwTypeData = szText;
   mii.wID = IDM_VIEW_KEYS;
   InsertMenuItem(hSubMenu, FCIDM_MENU_VIEW_SEP_OPTIONS, FALSE, &mii);
   }
}

/**************************************************************************

   CShellView::Refresh()

**************************************************************************/

STDMETHODIMP CShellView::Refresh(void)
{
//empty the list
ListView_DeleteAllItems(m_hwndList);

//refill the list
FillList();

return S_OK;
}

/**************************************************************************

   CShellView::CreateViewWindow()

**************************************************************************/

STDMETHODIMP CShellView::CreateViewWindow(   LPSHELLVIEW pPrevView,
                                             LPCFOLDERSETTINGS lpfs,
                                             LPSHELLBROWSER psb,
                                             LPRECT prcView,
                                             HWND *phWnd)
{
WNDCLASS wc;

*phWnd = NULL;

//if our window class has not been registered, then do so
if(!GetClassInfo(g_hInst, NS_CLASS_NAME, &wc))
   {
   ZeroMemory(&wc, sizeof(wc));
   wc.style          = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc    = (WNDPROC)WndProc;
   wc.cbClsExtra     = 0;
   wc.cbWndExtra     = 0;
   wc.hInstance      = g_hInst;
   wc.hIcon          = NULL;
   wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
   wc.lpszMenuName   = NULL;
   wc.lpszClassName  = NS_CLASS_NAME;

   if(!RegisterClass(&wc))
      return E_FAIL;
   }

//set up the member variables
m_pShellBrowser = psb;
m_FolderSettings = *lpfs;

//get our parent window
m_pShellBrowser->GetWindow(&m_hwndParent);

*phWnd = CreateWindowEx(   0,
                           NS_CLASS_NAME,
                           NULL,
                           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
                           prcView->left,
                           prcView->top,
                           prcView->right - prcView->left,
                           prcView->bottom - prcView->top,
                           m_hwndParent,
                           NULL,
                           g_hInst,
                           (LPVOID)this);

if(!*phWnd)
   return E_FAIL;

m_pShellBrowser->AddRef();

return S_OK;
}

/**************************************************************************

   CShellView::DestroyViewWindow()

**************************************************************************/

STDMETHODIMP CShellView::DestroyViewWindow(void)
{
//Make absolutely sure all our UI is cleaned up.
UIActivate(SVUIA_DEACTIVATE);

if(m_hMenu)
   DestroyMenu(m_hMenu);

DestroyWindow(m_hWnd);

//release the shell browser object
m_pShellBrowser->Release();

return S_OK;
}

/**************************************************************************

   CShellView::GetCurrentInfo()

**************************************************************************/

STDMETHODIMP CShellView::GetCurrentInfo(LPFOLDERSETTINGS lpfs)
{
*lpfs = m_FolderSettings;

return S_OK;
}

/**************************************************************************

   CShellView::AddPropertySheetPages()

**************************************************************************/

STDMETHODIMP CShellView::AddPropertySheetPages( DWORD dwReserved,
                                                LPFNADDPROPSHEETPAGE lpfn,
                                                LPARAM lParam)
{
return E_NOTIMPL;
}

/**************************************************************************

   CShellView::SaveViewState()

**************************************************************************/

STDMETHODIMP CShellView::SaveViewState(void)
{
return S_OK;
}

/**************************************************************************

   CShellView::SelectItem()

**************************************************************************/

STDMETHODIMP CShellView::SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags)
{
return E_NOTIMPL;
}

/**************************************************************************

   CShellView::GetItemObject()

**************************************************************************/

STDMETHODIMP CShellView::GetItemObject(UINT uItem, REFIID riid, LPVOID *ppvOut)
{
*ppvOut = NULL;

return E_NOTIMPL;
}

/**************************************************************************

   CShellView::WndProc()

**************************************************************************/

LRESULT CALLBACK CShellView::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
CShellView  *pThis = (CShellView*)GetWindowLong(hWnd, GWL_USERDATA);

switch (uMessage)
   {
   case WM_NCCREATE:
      {
      LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
      pThis = (CShellView*)(lpcs->lpCreateParams);
      SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);

      //set the window handle
      pThis->m_hWnd = hWnd;
      }
      break;

   case WM_SIZE:
      return pThis->OnSize(LOWORD(lParam), HIWORD(lParam));

   case WM_CREATE:
      return pThis->OnCreate();

   case WM_SETFOCUS:
      return pThis->OnSetFocus();

   case WM_KILLFOCUS:
      return pThis->OnKillFocus();

   case WM_ACTIVATE:
      return pThis->OnActivate(SVUIA_ACTIVATE_FOCUS);

   case WM_COMMAND:
      return pThis->OnCommand(   GET_WM_COMMAND_ID(wParam, lParam),
                                 GET_WM_COMMAND_CMD(wParam, lParam),
                                 GET_WM_COMMAND_HWND(wParam, lParam));

   case WM_INITMENUPOPUP:
      return pThis->UpdateMenu((HMENU)wParam);

   case WM_NOTIFY:
      return pThis->OnNotify((UINT)wParam, (LPNMHDR)lParam);

   case WM_SETTINGCHANGE:
      return pThis->OnSettingChange((LPCTSTR)lParam);
   }

return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

/**************************************************************************

   CShellView::OnSetFocus()

**************************************************************************/

LRESULT CShellView::OnSetFocus(void)
{
/*
Tell the browser one of our windows has received the focus. This should always
be done before merging menus (OnActivate merges the menus) if one of our
windows has the focus.
*/
m_pShellBrowser->OnViewWindowActive(this);

OnActivate(SVUIA_ACTIVATE_FOCUS);

return 0;
}

/**************************************************************************

   CShellView::OnKillFocus()

**************************************************************************/

LRESULT CShellView::OnKillFocus(void)
{
OnActivate(SVUIA_ACTIVATE_NOFOCUS);

return 0;
}

/**************************************************************************

   CShellView::OnCommand()

**************************************************************************/

LRESULT CShellView::OnCommand(DWORD dwCmdID, DWORD dwCmd, HWND hwndCmd)
{
switch(dwCmdID)
   {
   case IDM_VIEW_KEYS:
      g_bViewKeys = ! g_bViewKeys;
      Refresh();
      break;

   case IDM_VIEW_IDW:
      g_bShowIDW = ! g_bShowIDW;
      AddRemoveDockingWindow(g_bShowIDW);
      break;

   case IDM_MYFILEITEM:
      MessageBeep(MB_OK);
      break;
   }

return 0;
}

/**************************************************************************

   CShellView::UpdateMenu()

**************************************************************************/

LRESULT CShellView::UpdateMenu(HMENU hMenu)
{
CheckMenuItem(hMenu, IDM_VIEW_KEYS, MF_BYCOMMAND | (g_bViewKeys ? MF_CHECKED: MF_UNCHECKED));

if(CanDoIDockingWindow())
   {
   EnableMenuItem(hMenu, IDM_VIEW_IDW, MF_BYCOMMAND | MF_ENABLED);
   CheckMenuItem(hMenu, IDM_VIEW_IDW, MF_BYCOMMAND | (g_bShowIDW ? MF_CHECKED: MF_UNCHECKED));
   }
else
   {
   EnableMenuItem(hMenu, IDM_VIEW_IDW, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
   CheckMenuItem(hMenu, IDM_VIEW_IDW, MF_BYCOMMAND | MF_UNCHECKED);
   }

return 0;
}

/**************************************************************************

   CShellView::OnNotify()

**************************************************************************/

#define MENU_OFFSET  1
#define MENU_MAX     100

LRESULT CShellView::OnNotify(UINT CtlID, LPNMHDR lpnmh)
{
switch(lpnmh->code)
   {
   case NM_SETFOCUS:
      OnSetFocus();
      break;

   case NM_KILLFOCUS:
      OnDeactivate();
      break;

   case HDN_ENDTRACK:
      {
      g_nColumn1 = ListView_GetColumnWidth(m_hwndList, 0);
      g_nColumn2 = ListView_GetColumnWidth(m_hwndList, 1);
      }
      return 0;

   case LVN_DELETEITEM:
      {
      NM_LISTVIEW *lpnmlv = (NM_LISTVIEW*)lpnmh;

      //delete the pidl because we made a copy of it
      m_pPidlMgr->Delete((LPITEMIDLIST)lpnmlv->lParam);
      }
      break;

#ifdef LVN_ITEMACTIVATE

   case LVN_ITEMACTIVATE:

#else    //LVN_ITEMACTIVATE

   case NM_DBLCLK:
   case NM_RETURN:

#endif   //LVN_ITEMACTIVATE

      DoContextMenu(0, 0, TRUE);
      return 0;

   case LVN_GETDISPINFO:
      {
      LV_DISPINFO    *lpdi = (LV_DISPINFO *)lpnmh;
      LPITEMIDLIST   pidl = (LPITEMIDLIST)lpdi->item.lParam;

      //is the sub-item information being requested?
      if(lpdi->item.iSubItem)
         {
         //is the text being requested?
         if(lpdi->item.mask & LVIF_TEXT)
            {
            //is this a value or a key?
            if(m_pPidlMgr->IsValue(pidl))
               {
               //get the item's value text
               m_pPidlMgr->GetDataText(m_pidl, pidl, lpdi->item.pszText, lpdi->item.cchTextMax);

               //if the data text is NULL, get the no value string
               if(!*lpdi->item.pszText)
                  LoadString(g_hInst, IDS_NODATA, lpdi->item.pszText, lpdi->item.cchTextMax);
               }
            //its a key
            else
               {
               LoadString(g_hInst, IDS_KEY, lpdi->item.pszText, lpdi->item.cchTextMax);
               }
            }
         }
      //the item text is being requested
      else
         {
         //is the text being requested?
         if(lpdi->item.mask & LVIF_TEXT)
            {
            STRRET   str;

            if(SUCCEEDED(m_pSFParent->GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &str)))
               {
               if(STRRET_WSTR == str.uType)
                  {
                  WideCharToLocal(lpdi->item.pszText, str.pOleStr, lpdi->item.cchTextMax);

                  //delete the string buffer
                  m_pMalloc->Free(str.pOleStr);
                  }
               }
            }

         //is the image being requested?
         if(lpdi->item.mask & LVIF_IMAGE)
            {
            IExtractIcon   *pei;

            if(SUCCEEDED(m_pSFParent->GetUIObjectOf(m_hWnd, 1, (LPCITEMIDLIST*)&pidl, IID_IExtractIcon, NULL, (LPVOID*)&pei)))
               {
               UINT  uFlags;

               //GetIconLoaction will give us the index into our image list
               pei->GetIconLocation(GIL_FORSHELL, NULL, 0, &lpdi->item.iImage, &uFlags);

               pei->Release();
               }
            }
         }
      }
      return 0;

   case NM_RCLICK:
      {
      DWORD dwCursor = GetMessagePos();
      DoContextMenu(LOWORD(dwCursor), HIWORD(dwCursor), FALSE);
      }
      return 0;
   }

return 0;
}

/**************************************************************************

   CShellView::OnSize()

**************************************************************************/

LRESULT CShellView::OnSize(WORD wWidth, WORD wHeight)
{
//resize the ListView to fit our window
if(m_hwndList)
   {
   MoveWindow(m_hwndList, 0, 0, wWidth, wHeight, TRUE);
   }

return 0;
}

/**************************************************************************

   CShellView::OnCreate()

**************************************************************************/

LRESULT CShellView::OnCreate(void)
{
//create the ListView
if(CreateList())
   {
   if(InitList())
      {
      FillList();
      }
   }

return 0;
}

/**************************************************************************

   CShellView::CreateList()

**************************************************************************/

BOOL CShellView::CreateList(void)
{
DWORD dwStyle;

dwStyle =   WS_TABSTOP |
            WS_VISIBLE |
            WS_CHILD |
            WS_BORDER |
            LVS_REPORT |
            LVS_NOSORTHEADER |
            LVS_SHAREIMAGELISTS;

m_hwndList = CreateWindowEx(     WS_EX_CLIENTEDGE,
                                 WC_LISTVIEW,
                                 NULL,
                                 dwStyle,
                                 0,
                                 0,
                                 0,
                                 0,
                                 m_hWnd,
                                 (HMENU)ID_LISTVIEW,
                                 g_hInst,
                                 NULL);

if(!m_hwndList)
   return FALSE;

UpdateShellSettings();

return TRUE;
}

/**************************************************************************

   CShellView::InitList()

**************************************************************************/

BOOL CShellView::InitList(void)
{
LV_COLUMN   lvColumn;
RECT        rc;
TCHAR       szString[MAX_PATH];

//empty the list
ListView_DeleteAllItems(m_hwndList);

//initialize the columns

lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.pszText = szString;

lvColumn.cx = g_nColumn1;
LoadString(g_hInst, IDS_COLUMN1, szString, sizeof(szString));
ListView_InsertColumn(m_hwndList, 0, &lvColumn);

GetClientRect(m_hWnd, &rc);

lvColumn.cx = g_nColumn2;
LoadString(g_hInst, IDS_COLUMN2, szString, sizeof(szString));
ListView_InsertColumn(m_hwndList, 1, &lvColumn);

ListView_SetImageList(m_hwndList, g_himlSmall, LVSIL_SMALL);

return TRUE;
}

/**************************************************************************

   CShellView::FillList()

**************************************************************************/

void CShellView::FillList(void)
{
LPENUMIDLIST   pEnumIDList;

if(SUCCEEDED(m_pSFParent->EnumObjects(m_hWnd, SHCONTF_NONFOLDERS | (g_bViewKeys ? SHCONTF_FOLDERS : 0), &pEnumIDList)))
   {
   LPITEMIDLIST   pidl;
   DWORD          dwFetched;

   //turn the listview's redrawing off
   SendMessage(m_hwndList, WM_SETREDRAW, FALSE, 0);

   while((S_OK == pEnumIDList->Next(1, &pidl, &dwFetched)) && dwFetched)
      {
      LV_ITEM  lvItem;

      ZeroMemory(&lvItem, sizeof(lvItem));

      //set the mask
      lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;

      //add the item to the end of the list
      lvItem.iItem = ListView_GetItemCount(m_hwndList);

      //set the item's data
      lvItem.lParam = (LPARAM)m_pPidlMgr->Copy(pidl);

      //get text on a callback basis
      lvItem.pszText = LPSTR_TEXTCALLBACK;

      //get the image on a callback basis
      lvItem.iImage = I_IMAGECALLBACK;

      //add the item
      ListView_InsertItem(m_hwndList, &lvItem);
      }

   //sort the items
   ListView_SortItems(m_hwndList, CompareItems, (LPARAM)m_pSFParent);

   //turn the listview's redrawing back on and force it to draw
   SendMessage(m_hwndList, WM_SETREDRAW, TRUE, 0);
   InvalidateRect(m_hwndList, NULL, TRUE);
   UpdateWindow(m_hwndList);

   pEnumIDList->Release();
   }
}

/**************************************************************************

   CShellView::CanDoIDockingWindow()

**************************************************************************/

BOOL CShellView::CanDoIDockingWindow(void)
{
#if (_WIN32_IE < 0x0400)

return FALSE;

#else //(_WIN32_IE >= 0x0400)

BOOL              bReturn = FALSE;
HRESULT           hr;
IServiceProvider  *pSP;

//get the browser's IServiceProvider
hr = m_pShellBrowser->QueryInterface((REFIID)IID_IServiceProvider, (LPVOID*)&pSP);
if(SUCCEEDED(hr))
   {
   IDockingWindowFrame *pFrame;

   //get the IDockingWindowFrame
   hr = pSP->QueryService(SID_SShellBrowser, IID_IDockingWindowFrame, (LPVOID*)&pFrame);
   if(SUCCEEDED(hr))
      {
      bReturn = TRUE;

      pFrame->Release();
      }

   pSP->Release();
   }

return bReturn;
#endif   //(_WIN32_IE >= 0x0400)
}

/**************************************************************************

   CShellView::AddRemoveDockingWindow()

**************************************************************************/

BOOL CShellView::AddRemoveDockingWindow(BOOL bAdd)
{
#if (_WIN32_IE < 0x0400)

return FALSE;

#else //(_WIN32_IE < 0x0400)

BOOL              bReturn = FALSE;
HRESULT           hr;
IServiceProvider  *pSP;

//get the browser's IServiceProvider
hr = m_pShellBrowser->QueryInterface((REFIID)IID_IServiceProvider, (LPVOID*)&pSP);
if(SUCCEEDED(hr))
   {
   IDockingWindowFrame *pFrame;

   //get the IDockingWindowFrame pointer
   hr = pSP->QueryService(SID_SShellBrowser, IID_IDockingWindowFrame, (LPVOID*)&pFrame);
   if(SUCCEEDED(hr))
      {
      if(bAdd)
         {
         hr = S_OK;
         if(!m_pDockingWindow)
            {
            //create the toolbar object
            m_pDockingWindow = new CDockingWindow(this, m_hWnd);
            }

         if(m_pDockingWindow)
            {
            //add the toolbar object
            hr = pFrame->AddToolbar((IDockingWindow*)m_pDockingWindow, TOOLBAR_ID, 0);

            if(SUCCEEDED(hr))
               {
               bReturn = TRUE;
               }
            }
         }
      else
         {
         if(m_pDockingWindow)
            {
            hr = pFrame->RemoveToolbar((IDockingWindow*)m_pDockingWindow, DWFRF_NORMAL);

            if(SUCCEEDED(hr))
               {
               /*
               RemoveToolbar should release the toolbar object which will cause
               it to destroy itself. Our toolbar object is no longer valid at
               this point.
               */
               m_pDockingWindow = NULL;
               bReturn = TRUE;
               }
            }
         }
      pFrame->Release();
      }
   pSP->Release();
   }

return bReturn;
#endif   //(_WIN32_IE < 0x0400)
}

/**************************************************************************

   CShellView::UpdateShellSettings()

**************************************************************************/

typedef void (WINAPI *PFNSHGETSETTINGSPROC)(LPSHELLFLAGSTATE lpsfs, DWORD dwMask);

void CShellView::UpdateShellSettings(void)
{
#if (_WIN32_IE >= 0x0400)
SHELLFLAGSTATE       sfs;
HINSTANCE            hinstShell32;

/*
Since SHGetSettings is not implemented in all versions of the shell, get the
function address manually at run time. This allows the extension to run on all
platforms.
*/

ZeroMemory(&sfs, sizeof(sfs));

/*
The default, in case any of the following steps fails, is classic Windows 95
style.
*/
sfs.fWin95Classic = TRUE;

hinstShell32 = LoadLibrary(TEXT("shell32.dll"));
if(hinstShell32)
   {
   PFNSHGETSETTINGSPROC pfnSHGetSettings;

   pfnSHGetSettings = (PFNSHGETSETTINGSPROC)GetProcAddress(hinstShell32, TEXT("SHGetSettings"));
   if(pfnSHGetSettings)
      {
      (*pfnSHGetSettings)(&sfs, SSF_DOUBLECLICKINWEBVIEW | SSF_WIN95CLASSIC);
      }
   FreeLibrary(hinstShell32);
   }

DWORD dwExStyles = 0;

if(!sfs.fWin95Classic && !sfs.fDoubleClickInWebView)
   dwExStyles |= LVS_EX_ONECLICKACTIVATE |
                  LVS_EX_TRACKSELECT |
                  LVS_EX_UNDERLINEHOT;

ListView_SetExtendedListViewStyle(m_hwndList, dwExStyles);

#endif   //(_WIN32_IE >= 0x0400)
}

/**************************************************************************

   CShellView::OnSettingChange()

**************************************************************************/

LRESULT CShellView::OnSettingChange(LPCTSTR lpszSection)
{
//if(0 == lstrcmpi(lpszSection, TEXT("ShellState")))
   {
   UpdateShellSettings();
   return 0;
   }

return 0;
}

/**************************************************************************

   CShellView::DoContextMenu()

**************************************************************************/

void CShellView::DoContextMenu(WORD x, WORD y, BOOL fDefault)
{
UINT           uSelected = ListView_GetSelectedCount(m_hwndList);
LPITEMIDLIST   *aSelectedItems;

aSelectedItems = (LPITEMIDLIST*)m_pMalloc->Alloc(uSelected * sizeof(LPITEMIDLIST));

if(aSelectedItems)
   {
   UINT           i;
   LPCONTEXTMENU  pContextMenu = NULL;
   LVITEM         lvItem;

   ZeroMemory(&lvItem, sizeof(lvItem));
   lvItem.mask = LVIF_STATE | LVIF_PARAM;
   lvItem.stateMask = LVIS_SELECTED;
   lvItem.iItem = 0;

   i = 0;

   while(ListView_GetItem(m_hwndList, &lvItem) && (i < uSelected))
      {
      if(lvItem.state & LVIS_SELECTED)
         {
         aSelectedItems[i] = (LPITEMIDLIST)lvItem.lParam;
         i++;
         }
      lvItem.iItem++;
      }

   m_pSFParent->GetUIObjectOf(   m_hwndParent,
                                 uSelected,
                                 (LPCITEMIDLIST*)aSelectedItems,
                                 IID_IContextMenu,
                                 NULL,
                                 (LPVOID*)&pContextMenu);

   if(pContextMenu)
      {
      HMENU hMenu = CreatePopupMenu();

      if(hMenu && SUCCEEDED(pContextMenu->QueryContextMenu( hMenu,
                                                            0,
                                                            MENU_OFFSET,
                                                            MENU_MAX,
                                                            CMF_NORMAL)))
         {
         UINT  uCommand;

         if(fDefault)
            {
            MENUITEMINFO   mii;
            int            nMenuIndex;

            uCommand = 0;

            ZeroMemory(&mii, sizeof(mii));
            mii.cbSize = sizeof(mii);
            mii.fMask = MIIM_STATE | MIIM_ID;

            nMenuIndex = 0;

            //find the default item in the menu
            while(GetMenuItemInfo(hMenu, nMenuIndex, TRUE, &mii))
               {
               if(mii.fState & MFS_DEFAULT)
                  {
                  uCommand = mii.wID;
                  }

               nMenuIndex++;
               }
            }
         else
            {
            uCommand = TrackPopupMenu( hMenu,
                                       TPM_LEFTALIGN | TPM_RETURNCMD,
                                       x,
                                       y,
                                       0,
                                       m_hWnd,
                                       NULL);
            }

         if(uCommand > 0)
            {
            CMINVOKECOMMANDINFO  cmi;

            ZeroMemory(&cmi, sizeof(cmi));
            cmi.cbSize = sizeof(cmi);
            cmi.hwnd = m_hwndParent;
            cmi.lpVerb = MAKEINTRESOURCE(uCommand - MENU_OFFSET);

            pContextMenu->InvokeCommand(&cmi);
            }

         DestroyMenu(hMenu);
         }
      }

   m_pMalloc->Free(aSelectedItems);
   }
}


SHLVIEW.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          ShlView.h

   Description:   CShellView definitions.

**************************************************************************/

#ifndef SHELLVIEW_H
#define SHELLVIEW_H

#include <windows.h>
#include <windowsx.h>
#include <objbase.h>
#include <shlobj.h>
#include <ServProv.h>
#include <DocObj.h>
#include "ShlFldr.h"
#include "PidlMgr.h"
#include "DockWndw.h"

/**************************************************************************

   global variables

**************************************************************************/

extern HINSTANCE  g_hInst;
extern UINT       g_DllRefCount;
extern HIMAGELIST g_himlLarge;
extern HIMAGELIST g_himlSmall;

/**************************************************************************

   global IDs

**************************************************************************/

#define NS_CLASS_NAME   (TEXT("RegViewNSClass"))

//menu items
#define IDM_VIEW_KEYS   (FCIDM_SHVIEWFIRST + 0x500)
#define IDM_VIEW_IDW    (FCIDM_SHVIEWFIRST + 0x501)
#define IDM_MYFILEITEM  (FCIDM_SHVIEWFIRST + 0x502)

//control IDs
#define ID_LISTVIEW     2000

/**************************************************************************

   CShellView class definition

**************************************************************************/

class CShellView : public IShellView, public IOleCommandTarget
{
friend CDockingWindow;

protected:
   DWORD m_ObjRefCount;

public:
   CShellView(CShellFolder*, LPCITEMIDLIST);
   ~CShellView();

   //IUnknown methods
   STDMETHOD (QueryInterface)(REFIID, LPVOID*);
   STDMETHOD_ (DWORD, AddRef)();
   STDMETHOD_ (DWORD, Release)();

   //IOleWindow methods
   STDMETHOD (GetWindow) (HWND*);
   STDMETHOD (ContextSensitiveHelp) (BOOL);

   //IShellView methods
   STDMETHOD (TranslateAccelerator) (LPMSG);
   STDMETHOD (EnableModeless) (BOOL);
   STDMETHOD (UIActivate) (UINT);
   STDMETHOD (Refresh) (void);
   STDMETHOD (CreateViewWindow) (LPSHELLVIEW, LPCFOLDERSETTINGS, LPSHELLBROWSER, LPRECT, HWND*);
   STDMETHOD (DestroyViewWindow) (void);
   STDMETHOD (GetCurrentInfo) (LPFOLDERSETTINGS);
   STDMETHOD (AddPropertySheetPages) (DWORD, LPFNADDPROPSHEETPAGE, LPARAM);
   STDMETHOD (SaveViewState) (void);
   STDMETHOD (SelectItem) (LPCITEMIDLIST, UINT);
   STDMETHOD (GetItemObject) (UINT, REFIID, LPVOID*);

   //IOleCommandTarget methods
   STDMETHOD (QueryStatus) (const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText);
   STDMETHOD (Exec) (const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut);

private:
//private member variables
   UINT m_uState;
HKEY m_hKeyRoot;
LPITEMIDLIST m_pidl;
FOLDERSETTINGS m_FolderSettings;
LPSHELLBROWSER m_pShellBrowser;
HWND m_hwndParent;
HWND m_hWnd;
HWND m_hwndList;
HMENU m_hMenu;
   CShellFolder *m_pSFParent;
LPMALLOC m_pMalloc;
   LPPIDLMGR m_pPidlMgr;
CDockingWindow* m_pDockingWindow;

//private member functions
   static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
LRESULT UpdateMenu(HMENU hMenu);
HMENU BuildRegistryMenu(void);
void MergeFileMenu(HMENU);
void MergeViewMenu(HMENU);
LRESULT OnCommand(DWORD, DWORD, HWND);
LRESULT OnActivate(UINT);
void OnDeactivate(void);
LRESULT OnSetFocus(void);
LRESULT OnKillFocus(void);
LRESULT OnNotify(UINT, LPNMHDR);
LRESULT OnSize(WORD, WORD);
LRESULT OnCreate(void);
BOOL CreateList(void);
BOOL InitList(void);
void FillList(void);
BOOL CanDoIDockingWindow(void);
BOOL AddRemoveDockingWindow(BOOL);
LRESULT OnSettingChange(LPCTSTR);
   void UpdateShellSettings(void);
   void DoContextMenu(WORD, WORD, BOOL);

};

#endif   //SHELLVIEW_H


TOOLS.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          Tools.h

   Description:

**************************************************************************/

/**************************************************************************
   #include statements
**************************************************************************/

#include "resource.h"

/**************************************************************************
   structure definitions
**************************************************************************/

typedef struct
   {
   int   idCommand;
   int   iImage;
   int   idButtonString;
   int   idMenuString;
   int   nStringOffset;
   BYTE  bState;
   BYTE  bStyle;
   }MYTOOLINFO, *LPMYTOOLINFO;

extern MYTOOLINFO g_Tools[];


UTILITY.CPP

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          Utility.cpp

   Description:

**************************************************************************/

/**************************************************************************
   #include statements
**************************************************************************/

#include "Utility.h"
#include "ShlFldr.h"
#include "resource.h"

/**************************************************************************
   private function prototypes
**************************************************************************/

/**************************************************************************
   global variables
**************************************************************************/

extern HIMAGELIST g_himlLarge;
extern HIMAGELIST g_himlSmall;

#define MAIN_KEY_STRING       (TEXT("Software\\RegView"))
#define VALUE_STRING          (TEXT("Display Settings"))
#define INITIAL_COLUMN_SIZE   100

/**************************************************************************

   GetKeyName()

**************************************************************************/

DWORD GetKeyName( HKEY hKeyRoot,
                  LPCTSTR pszSubKey,
                  DWORD dwIndex,
                  LPTSTR pszOut,
                  DWORD dwSize)
{
HKEY     hKey;
LONG     lResult;
FILETIME ft;

if(!pszOut)
   return 0;

if(!pszSubKey)
   pszSubKey = TEXT("");

//open the specified key
lResult = RegOpenKeyEx( hKeyRoot,
                        pszSubKey,
                        0,
                        KEY_ENUMERATE_SUB_KEYS,
                        &hKey);

if(ERROR_SUCCESS != lResult)
   return 0;

//try to get the specified subkey
lResult = RegEnumKeyEx( hKey,
                        dwIndex,
                        pszOut,
                        &dwSize,
                        NULL,
                        NULL,
                        NULL,
                        &ft);

RegCloseKey(hKey);

if(ERROR_SUCCESS == lResult)
   return dwSize;

return 0;
}

/**************************************************************************

   GetValueName()

**************************************************************************/

BOOL GetValueName(   HKEY hKeyRoot,
                     LPCTSTR pszSubKey,
                     DWORD dwIndex,
                     LPTSTR pszOut,
                     DWORD dwSize)
{
HKEY     hKey;
LONG     lResult;
DWORD    dwType;

if(!pszOut)
   return FALSE;

if(!pszSubKey)
   pszSubKey = TEXT("");

//open the specified key
lResult = RegOpenKeyEx( hKeyRoot,
                        pszSubKey,
                        0,
                        KEY_QUERY_VALUE,
                        &hKey);

if(ERROR_SUCCESS != lResult)
   return FALSE;

//try to get the specified subkey
lResult = RegEnumValue( hKey,
                        dwIndex,
                        pszOut,
                        &dwSize,
                        NULL,
                        &dwType,
                        NULL,
                        NULL);

RegCloseKey(hKey);

return BOOL(ERROR_SUCCESS == lResult);
}

/**************************************************************************

   GetRootKeyText()

**************************************************************************/

DWORD GetRootKeyText(HKEY hKeyRoot, LPTSTR lpszOut, DWORD dwOutSize)
{
*lpszOut = 0;

if(hKeyRoot == HKEY_CLASSES_ROOT)
   {
   lstrcpyn(lpszOut, TEXT("HKEY_CLASSES_ROOT"), dwOutSize);
   }

else if(hKeyRoot == HKEY_CURRENT_USER)
   {
   lstrcpyn(lpszOut, TEXT("HKEY_CURRENT_USER"), dwOutSize);
   }

else if(hKeyRoot == HKEY_LOCAL_MACHINE)
   {
   lstrcpyn(lpszOut, TEXT("HKEY_LOCAL_MACHINE"), dwOutSize);
   }

else if(hKeyRoot == HKEY_USERS)
   {
   lstrcpyn(lpszOut, TEXT("HKEY_USERS"), dwOutSize);
   }

else if(hKeyRoot == HKEY_PERFORMANCE_DATA)
   {
   lstrcpyn(lpszOut, TEXT("HKEY_PERFORMANCE_DATA"), dwOutSize);
   }

else if(hKeyRoot == HKEY_CURRENT_CONFIG)
   {
   lstrcpyn(lpszOut, TEXT("HKEY_CURRENT_CONFIG"), dwOutSize);
   }

else if(hKeyRoot == HKEY_DYN_DATA)
   {
   lstrcpyn(lpszOut, TEXT("HKEY_DYN_DATA"), dwOutSize);
   }

return lstrlen(lpszOut) + 1;
}

/**************************************************************************

   RootKeyExists()

**************************************************************************/

BOOL RootKeyExists(HKEY hKeyRoot)
{
LONG  lResult;
HKEY  hKey;

//open the specified key
lResult = RegOpenKeyEx( hKeyRoot,
                        NULL,
                        0,
                        KEY_ENUMERATE_SUB_KEYS,
                        &hKey);

if(ERROR_SUCCESS != lResult)
   return FALSE;

RegCloseKey(hKey);

return TRUE;
}

/**************************************************************************

   SaveGlobalSettings()

**************************************************************************/

BOOL SaveGlobalSettings(void)
{
HKEY  hKey;
LONG  lResult;
DWORD dwDisp;

lResult = RegCreateKeyEx(  HKEY_CURRENT_USER,
                           MAIN_KEY_STRING,
                           0,
                           NULL,
                           REG_OPTION_NON_VOLATILE,
                           KEY_ALL_ACCESS,
                           NULL,
                           &hKey,
                           &dwDisp);

if(lResult != ERROR_SUCCESS)
   return FALSE;

//create an array to put our data in
DWORD dwArray[4];
dwArray[0] = g_nColumn1;
dwArray[1] = g_nColumn2;
dwArray[2] = g_bViewKeys;
dwArray[3] = g_bShowIDW;

//save the last printer selected
lResult = RegSetValueEx(   hKey,
                           VALUE_STRING,
                           0,
                           REG_BINARY,
                           (LPBYTE)dwArray,
                           sizeof(dwArray));

RegCloseKey(hKey);

if(lResult != ERROR_SUCCESS)
   return FALSE;

return TRUE;
}

/**************************************************************************

   GetGlobalSettings()

**************************************************************************/

BOOL GetGlobalSettings(void)
{
HKEY  hKey;
LONG  lResult;

//set up the default data
g_nColumn1 = INITIAL_COLUMN_SIZE;
g_nColumn2 = INITIAL_COLUMN_SIZE;
g_bViewKeys = TRUE;
g_bShowIDW = FALSE;

lResult = RegOpenKeyEx( HKEY_CURRENT_USER,
                        MAIN_KEY_STRING,
                        0,
                        KEY_ALL_ACCESS,
                        &hKey);

if(lResult != ERROR_SUCCESS)
   return FALSE;

//create an array to put our data in
DWORD dwArray[4];
DWORD dwType;
DWORD dwSize = sizeof(dwArray);

//save the last printer selected
lResult = RegQueryValueEx( hKey,
                           VALUE_STRING,
                           NULL,
                           &dwType,
                           (LPBYTE)dwArray,
                           &dwSize);

RegCloseKey(hKey);

if(lResult != ERROR_SUCCESS)
   return FALSE;

g_nColumn1 = dwArray[0];
g_nColumn2 = dwArray[1];
g_bViewKeys = dwArray[2];
g_bShowIDW = dwArray[3];

return TRUE;
}

/**************************************************************************

   CompareItems()

**************************************************************************/

int CALLBACK CompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData)
{
CShellFolder  *pFolder = (CShellFolder*)lpData;

if(!pFolder)
   return 0;

return (int)pFolder->CompareIDs(0, (LPITEMIDLIST)lParam1, (LPITEMIDLIST)lParam2);
}

/**************************************************************************

   CreateImageLists()

**************************************************************************/

VOID CreateImageLists(VOID)
{
int   cx;
int   cy;

cx = GetSystemMetrics(SM_CXSMICON);
cy = GetSystemMetrics(SM_CYSMICON);

cx = 16;
cy = 16;

if(g_himlSmall)
   ImageList_Destroy(g_himlSmall);

//set the small image list
g_himlSmall = ImageList_Create(cx, cy, ILC_COLORDDB | ILC_MASK, 4, 0);

if(g_himlSmall)
   {
   HICON hIcon;

   hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_BINARY), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
   ImageList_AddIcon(g_himlSmall, hIcon);

   hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_STRING), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
   ImageList_AddIcon(g_himlSmall, hIcon);

   hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_FOLDER), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
   ImageList_AddIcon(g_himlSmall, hIcon);

   hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_FOLDEROPEN), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
   ImageList_AddIcon(g_himlSmall, hIcon);
   }

if(g_himlLarge)
   ImageList_Destroy(g_himlLarge);

cx = GetSystemMetrics(SM_CXICON);
cy = GetSystemMetrics(SM_CYICON);

cx = 32;
cy = 32;

//set the large image list
g_himlLarge = ImageList_Create(cx, cy, ILC_COLORDDB | ILC_MASK, 4, 0);

if(g_himlSmall)
   {
   HICON hIcon;

   hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_BINARY), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
   ImageList_AddIcon(g_himlLarge, hIcon);

   hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_STRING), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
   ImageList_AddIcon(g_himlLarge, hIcon);

   hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_FOLDER), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
   ImageList_AddIcon(g_himlLarge, hIcon);

   hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_FOLDEROPEN), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
   ImageList_AddIcon(g_himlLarge, hIcon);
   }

}

/**************************************************************************

   DestroyImageLists()

**************************************************************************/

VOID DestroyImageLists(VOID)
{
if(g_himlSmall)
   ImageList_Destroy(g_himlSmall);

if(g_himlLarge)
   ImageList_Destroy(g_himlLarge);
}

/**************************************************************************

   WideCharToLocal()

**************************************************************************/

int WideCharToLocal(LPTSTR pLocal, LPWSTR pWide, DWORD dwChars)
{
*pLocal = 0;

#ifdef UNICODE
lstrcpyn(pLocal, pWide, dwChars);
#else
WideCharToMultiByte( CP_ACP,
                     0,
                     pWide,
                     -1,
                     pLocal,
                     dwChars,
                     NULL,
                     NULL);
#endif

return lstrlen(pLocal);
}

/**************************************************************************

   LocalToWideChar()

**************************************************************************/

int LocalToWideChar(LPWSTR pWide, LPTSTR pLocal, DWORD dwChars)
{
*pWide = 0;

#ifdef UNICODE
lstrcpyn(pWide, pLocal, dwChars);
#else
MultiByteToWideChar( CP_ACP,
                     0,
                     pLocal,
                     -1,
                     pWide,
                     dwChars);
#endif

return lstrlenW(pWide);
}

/**************************************************************************

   GetNextRegElement()

**************************************************************************/

LPTSTR GetNextRegElement(LPTSTR pszNext, LPTSTR pszOut, DWORD dwOut)
{
if(!pszNext || !*pszNext)
   return NULL;

LPTSTR   pszTail = pszNext;

while(*pszTail && (*pszTail != '\\'))
   {
   pszTail++;
   }

DWORD dwCopy = ((LPBYTE)pszTail - (LPBYTE)pszNext)/sizeof(TCHAR) + 1;

lstrcpyn(pszOut, pszNext, min(dwOut, dwCopy));

if(*pszTail)
   pszTail++;

return pszTail;
}


UTILITY.H

/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          Utility.h

   Description:   Utility definitions.

**************************************************************************/

#ifndef UTILITY_H
#define UTILITY_H

/**************************************************************************
   #include statements
**************************************************************************/

#include <windows.h>
#include <shlobj.h>
#include <Regstr.h>

#ifdef _cplusplus
extern "C" {
#endif   //_cplusplus

/**************************************************************************
   global variables
**************************************************************************/

extern int  g_nColumn1;
extern int  g_nColumn2;
extern BOOL g_bViewKeys;
extern BOOL g_bShowIDW;

/**************************************************************************
   function prototypes
**************************************************************************/

//registry utility functions
DWORD GetKeyName(HKEY, LPCTSTR, DWORD, LPTSTR, DWORD);
BOOL GetValueName(HKEY, LPCTSTR, DWORD, LPTSTR, DWORD);
DWORD GetRootKeyText(HKEY, LPTSTR, DWORD);
BOOL RootKeyExists(HKEY);

//utility functions
BOOL SaveGlobalSettings(void);
BOOL GetGlobalSettings(void);
int CALLBACK CompareItems(LPARAM, LPARAM, LPARAM);
VOID CreateImageLists(VOID);
VOID DestroyImageLists(VOID);
int WideCharToLocal(LPTSTR, LPWSTR, DWORD);
int LocalToWideChar(LPWSTR, LPTSTR, DWORD);
LPTSTR GetNextRegElement(LPTSTR, LPTSTR, DWORD);

#ifdef _cplusplus
}
#endif   //_cplusplus

#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))

#endif   //UTILITY_H


README.TXT

Basic Shell Extension

The SHELLEXT sample demonstrates how to implement the basic shell
extensions. These include context menu, property sheet, icon handler, and
copy hook extensions. This sample does not implement a drag-drop handler,
but the implementation would be very similar to context menu handlers.


CTXMENU.CPP

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   ctxmenu.cpp
//
//  PURPOSE:   Implements the IContextMenu member functions necessary to support
//             the context menu portioins of this shell extension.  Context menu
//             shell extensions are called when the user right clicks on a file
//             (of the type registered for the shell extension--see SHELLEXT.REG
//             for details on the registry entries.  In this sample, the relevant
//             files are of type .GAK) in the Explorer, or selects the File menu
//             item.
//

#include "priv.h"
#include "shellext.h"

//
//  FUNCTION: CShellExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
//
//  PURPOSE: Called by the shell just before the context menu is displayed.
//           This is where you add your specific menu items.
//
//  PARAMETERS:
//    hMenu      - Handle to the context menu
//    indexMenu  - Index of where to begin inserting menu items
//    idCmdFirst - Lowest value for new menu ID's
//    idCmtLast  - Highest value for new menu ID's
//    uFlags     - Specifies the context of the menu event
//
//  RETURN VALUE:
//
//
//  COMMENTS:
//

STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu,
                                         UINT indexMenu,
                                         UINT idCmdFirst,
                                         UINT idCmdLast,
                                         UINT uFlags)
{
    ODS("CShellExt::QueryContextMenu()\r\n");

    UINT idCmd = idCmdFirst;
    char szMenuText[64];
    char szMenuText2[64];
    char szMenuText3[64];
    char szMenuText4[64];
    BOOL bAppendItems=TRUE;

    if ((uFlags & 0x000F) == CMF_NORMAL)  //Check == here, since CMF_NORMAL=0
    {
        ODS("CMF_NORMAL...\r\n");
        lstrcpy(szMenuText, "&New .GAK menu 1, Normal File");
        lstrcpy(szMenuText2, "&New .GAK menu 2, Normal File");
        lstrcpy(szMenuText3, "&New .GAK menu 3, Normal File");
        lstrcpy(szMenuText4, "&New .GAK menu 4, Normal File");
    }
    else
        if (uFlags & CMF_VERBSONLY)
        {
            ODS("CMF_VERBSONLY...\r\n");
            lstrcpy(szMenuText, "&New .GAK menu 1, Shortcut File");
            lstrcpy(szMenuText2, "N&ew .GAK menu 2, Shortcut File");
            lstrcpy(szMenuText3, "&New .GAK menu 3, Shortcut File");
            lstrcpy(szMenuText4, "&New .GAK menu 4, Shortcut File");
        }
    else
        if (uFlags & CMF_EXPLORE)
        {
            ODS("CMF_EXPLORE...\r\n");
            lstrcpy(szMenuText, "&New .GAK menu 1, Normal File right click in Explorer");
            lstrcpy(szMenuText2, "N&ew .GAK menu 2, Normal File right click in Explorer");
            lstrcpy(szMenuText3, "&New .GAK menu 3, Normal File right click in Explorer");
            lstrcpy(szMenuText4, "&New .GAK menu 4, Normal File right click in Explorer");
        }
    else
        if (uFlags & CMF_DEFAULTONLY)
        {
            ODS("CMF_DEFAULTONLY...\r\n");
            bAppendItems = FALSE;
        }
    else
        {
            char szTemp[32];

            wsprintf(szTemp, "uFlags==>%d\r\n", uFlags);
            ODS("CMF_default...\r\n");
            ODS(szTemp);
            bAppendItems = FALSE;
        }

    if (bAppendItems)
    {
        InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);

        InsertMenu(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuText);

        InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);

        InsertMenu(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuText2);

        InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);

        InsertMenu(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuText3);

        InsertMenu(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuText4);

        return ResultFromShort(idCmd-idCmdFirst); //Must return number of menu
  //items we added.
   }

   return NOERROR;
}

//
//  FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO)
//
//  PURPOSE: Called by the shell after the user has selected on of the
//           menu items that was added in QueryContextMenu().
//
//  PARAMETERS:
//    lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
//
//  RETURN VALUE:
//
//
//  COMMENTS:
//

STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
    ODS("CShellExt::InvokeCommand()\r\n");

HRESULT hr = E_INVALIDARG;

    //If HIWORD(lpcmi->lpVerb) then we have been called programmatically
    //and lpVerb is a command that should be invoked.  Otherwise, the shell
    //has called us, and LOWORD(lpcmi->lpVerb) is the menu ID the user has
    //selected.  Actually, it's (menu ID - idCmdFirst) from QueryContextMenu().
if (!HIWORD(lpcmi->lpVerb))
    {
        UINT idCmd = LOWORD(lpcmi->lpVerb);

        switch (idCmd)
        {
            case 0:
                hr = DoGAKMenu1(lpcmi->hwnd,
                                lpcmi->lpDirectory,
                                lpcmi->lpVerb,
                                lpcmi->lpParameters,
                                lpcmi->nShow);
                break;

            case 1:
                hr = DoGAKMenu2(lpcmi->hwnd,
                                lpcmi->lpDirectory,
                                lpcmi->lpVerb,
                                lpcmi->lpParameters,
                                lpcmi->nShow);
                break;

            case 2:
                hr = DoGAKMenu3(lpcmi->hwnd,
                                lpcmi->lpDirectory,
                                lpcmi->lpVerb,
                                lpcmi->lpParameters,
                                lpcmi->nShow);
                break;

            case 3:
                hr = DoGAKMenu4(lpcmi->hwnd,
                                lpcmi->lpDirectory,
                                lpcmi->lpVerb,
                                lpcmi->lpParameters,
                                lpcmi->nShow);
                break;
        }
    }
    return hr;
}

//
//  FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO)
//
//  PURPOSE: Called by the shell after the user has selected on of the
//           menu items that was added in QueryContextMenu().
//
//  PARAMETERS:
//    lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
//
//  RETURN VALUE:
//
//
//  COMMENTS:
//

STDMETHODIMP CShellExt::GetCommandString(UINT idCmd,
                                         UINT uFlags,
                                         UINT FAR *reserved,
                                         LPSTR pszName,
                                         UINT cchMax)
{
    ODS("CShellExt::GetCommandString()\r\n");

    switch (idCmd)
    {
        case 0:
            lstrcpy(pszName, "New menu item number 1");
            break;

        case 1:
            lstrcpy(pszName, "New menu item number 2");
            break;

        case 2:
            lstrcpy(pszName, "New menu item number 3");
            break;

        case 3:
            lstrcpy(pszName, "New menu item number 4");
            break;
    }

    return NOERROR;
}

STDMETHODIMP CShellExt::DoGAKMenu1(HWND hParent,
                                   LPCSTR pszWorkingDir,
                                   LPCSTR pszCmd,
                                   LPCSTR pszParam,
                                   int iShowCmd)
{
    ODS("CShellExt::DoGAKMenu1()\r\n");

    MessageBox(hParent, "Menu item 1!", "Shell Extension Sample", MB_OK);

    return NOERROR;
}

STDMETHODIMP CShellExt::DoGAKMenu2(HWND hParent,
                                   LPCSTR pszWorkingDir,
                                   LPCSTR pszCmd,
                                   LPCSTR pszParam,
                                   int iShowCmd)
{
    ODS("CShellExt::DoGAKMenu2()\r\n");

    MessageBox(hParent, "Menu item 2!", "Shell Extension Sample", MB_OK);

    return NOERROR;
}

STDMETHODIMP CShellExt::DoGAKMenu3(HWND hParent,
                                   LPCSTR pszWorkingDir,
                                   LPCSTR pszCmd,
                                   LPCSTR pszParam,
                                   int iShowCmd)
{
    ODS("CShellExt::DoGAKMenu3()\r\n");

    MessageBox(hParent, "Menu item 3!", "Shell Extension Sample", MB_OK);

    return NOERROR;
}

STDMETHODIMP CShellExt::DoGAKMenu4(HWND hParent,
                                   LPCSTR pszWorkingDir,
                                   LPCSTR pszCmd,
                                   LPCSTR pszParam,
                                   int iShowCmd)
{
    ODS("CShellExt::DoGAKMenu4()\r\n");

    MessageBox(hParent, "Menu item 4!", "Shell Extension Sample", MB_OK);

    return NOERROR;
}


ICONHDLR.CPP

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
// Modudule: ICONHDLR.CPP
//
// Purpose:  Implements the IExtractIcon and IPersistFile member
//           functions necessary to support the icon handler portion
//           of this shell extension.
//

#include "priv.h"
#include "shellext.h"

extern HINSTANCE g_hmodThisDll; // Handle to this DLL itself.

// *********************** IExtractIcon Implementation *************************

STDMETHODIMP CShellExt::GetIconLocation(UINT   uFlags,
                                        LPSTR  szIconFile,
                                        UINT   cchMax,
                                        int   *piIndex,
                                        UINT  *pwFlags)
{
    ODS("CShellExt::GetIconLocation()\r\n");

    GetModuleFileName(g_hmodThisDll, szIconFile, cchMax);

    *piIndex = (int)GetPrivateProfileInt("IconImage",
                                         "Index",
                                         0,
                                         m_szFileUserClickedOn);

    return S_OK;
}

STDMETHODIMP CShellExt::Extract(LPCSTR pszFile,
                                UINT   nIconIndex,
                                HICON  *phiconLarge,
                                HICON  *phiconSmall,
                                UINT   nIconSize)
{
    ODS("CShellExt::Extract()\r\n");

    return S_FALSE;
}

// *********************** IPersistFile Implementation ******************

STDMETHODIMP CShellExt::GetClassID(LPCLSID lpClassID)
{
    ODS("CShellExt::GetClassID()\r\n");

    return E_FAIL;
}

STDMETHODIMP CShellExt::IsDirty()
{
    ODS("CShellExt::IsDirty()\r\n");

    return S_FALSE;
}

STDMETHODIMP CShellExt::Load(LPCOLESTR lpszFileName, DWORD grfMode)
{
    ODS("CShellExt::Load()\r\n");

    WideCharToMultiByte(CP_ACP,                // CodePage
                        0,              // dwFlags
                        lpszFileName,          // lpWideCharStr
                        -1,                    // cchWideChar
                        m_szFileUserClickedOn, // lpMultiByteStr
                        sizeof(m_szFileUserClickedOn),  // cchMultiByte,
                        NULL,                  // lpDefaultChar,
                        NULL);                 // lpUsedDefaultChar

    return NOERROR;
}

STDMETHODIMP CShellExt::Save(LPCOLESTR lpszFileName, BOOL fRemember)
{
    ODS("CShellExt::Save()\r\n");

    return E_FAIL;
}

STDMETHODIMP CShellExt::SaveCompleted(LPCOLESTR lpszFileName)
{
    ODS("CShellExt::SaveCompleted()\r\n");

    return E_FAIL;
}

STDMETHODIMP CShellExt::GetCurFile(LPOLESTR FAR* lplpszFileName)
{
    ODS("CShellExt::GetCurFile()\r\n");

    return E_FAIL;
}


PRIV.H

//--------------------------------------------------------------
// common user interface routines
//
//
//--------------------------------------------------------------

#ifndef STRICT
#define STRICT
#endif

#define INC_OLE2        // WIN32, get ole2 from windows.h

#include <windows.h>
#include <windowsx.h>
#include <shlobj.h>

#define ResultFromShort(i)  ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(i)))


PROPSHET.CPP

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   propshet.cpp
//
//  PURPOSE:   Implements the IShellPropSheetExt member functions necessary to
//             support the property sheet page of this shell extension.  This
//             page is displayed when the user selects "Properties..." after
//             right clicking on a .GAK file
//

#include "priv.h"
#include "shellext.h"
#include "resource.h"

extern UINT g_cRefThisDll;         // Reference count of this DLL.
extern HINSTANCE g_hmodThisDll; // Handle to this DLL itself.

//
//  FUNCTION: GAKPageCallback(HWND, UINT, LPPROPSHEETPAGE)
//
//  PURPOSE: Callback  procedure for the property page
//
//  PARAMETERS:
//    hWnd      - Reserved (will always be NULL)
//    uMessage  - Action flag: Are we being created or released
//    ppsp      - The page that is being created or destroyed
//
//  RETURN VALUE:
//
//    Depends on message.
//
//    For PSPCB_CREATE it's TRUE to let the page be created
//    or false to prevent it from being created.
//    For PSPCB_RELEASE the return value is ignored.
//
//  COMMENTS:
//
UINT CALLBACK
GAKPageCallback(HWND hWnd,
                UINT uMessage,
                LPPROPSHEETPAGE  ppsp)
{
    switch(uMessage)
    {
        case PSPCB_CREATE:
            return TRUE;

        case PSPCB_RELEASE:
            if (ppsp->lParam)
            {
               ((LPCSHELLEXT)(ppsp->lParam))->Release();
            }
            return TRUE;
    }
    return TRUE;
}

//
//  FUNCTION: GAKPageDlgProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Callback dialog procedure for the property page
//
//  PARAMETERS:
//    hDlg      - Dialog box window handle
//    uMessage  - current message
//    wParam    - depends on message
//    lParam    - depends on message
//
//  RETURN VALUE:
//
//    Depends on message.  In general, return TRUE if we process it.
//
//  COMMENTS:
//

BOOL CALLBACK GAKPageDlgProc(HWND hDlg,
                             UINT uMessage,
                             WPARAM wParam,
                             LPARAM lParam)
{
    LPPROPSHEETPAGE psp=(LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER);
    UINT iIndex=0;
    int  iID;
    LPCSHELLEXT lpcs;
 char szTemp[4];

    switch (uMessage)
    {
        //
        // When the shell creates a dialog box for a property sheet page,
        // it passes the pointer to the PROPSHEETPAGE data structure as
        // lParam. The dialog procedures of extensions typically store it
        // in the DWL_USER of the dialog box window.
        //
        case WM_INITDIALOG:
            SetWindowLong(hDlg, DWL_USER, lParam);

            psp = (LPPROPSHEETPAGE)lParam;

            lpcs = (LPCSHELLEXT)psp->lParam;

if (*(lpcs->m_szPropSheetFileUserClickedOn))
            iIndex = (UINT)GetPrivateProfileInt("IconImage",
                                            "Index",
                                            0,
                                            lpcs->m_szPropSheetFileUserClickedOn);

            //Note that the following line assumes that IDC_RED, IDC_GREEN,
            //and IDC_BLUE are in sequential order, with IDC_RED having the
            //smallest value.
            SendDlgItemMessage(hDlg, iIndex + IDC_RED, BM_SETCHECK, TRUE, 0L);

            break;

        case WM_DESTROY:
            RemoveProp(hDlg, "ID");
            break;

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDC_RED:
                case IDC_GREEN:
                case IDC_BLUE:
                    SetProp(hDlg, "ID", (HANDLE)lParam);
                    break;

                default:
                    break;
            }
            break;

        case WM_NOTIFY:
            switch (((NMHDR FAR *)lParam)->code)
            {
                case PSN_SETACTIVE:
                    break;

                case PSN_APPLY:
                    //User has clicked the OK or Apply button so we'll
                    //update the icon information in the .GAK file
                    lpcs = (LPCSHELLEXT)psp->lParam;
                    iID  = GetDlgCtrlID((HWND)GetProp(hDlg, "ID"));
                    iID -= IDC_RED;
                    wsprintf(szTemp, "%i", iID);
    WritePrivateProfileString("IconImage",
                                              "Index",
                                               szTemp,
                                               lpcs->m_szPropSheetFileUserClickedOn);
                    //Ask the shell to refresh the icon list...
                    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, 0, 0);
                    break;

                default:
                    break;
            }
            break;

        default:
            return FALSE;
    }

    return TRUE;
}

//
//  FUNCTION: CShellExt::AddPages(LPFNADDPROPSHEETPAGE, LPARAM)
//
//  PURPOSE: Called by the shell just before the property sheet is displayed.
//
//  PARAMETERS:
//    lpfnAddPage -  Pointer to the Shell's AddPage function
//    lParam      -  Passed as second parameter to lpfnAddPage
//
//  RETURN VALUE:
//
//    NOERROR in all cases.  If for some reason our pages don't get added,
//    the Shell still needs to bring up the Properties... sheet.
//
//  COMMENTS:
//

STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
{
    PROPSHEETPAGE psp;
    HPROPSHEETPAGE hpage;

    ODS("CShellExt::AddPages()\r\n");

    FORMATETC fmte = {CF_HDROP,
                  (DVTARGETDEVICE FAR *)NULL,
                  DVASPECT_CONTENT,
                  -1,
                  TYMED_HGLOBAL
                 };
    STGMEDIUM medium;
HRESULT hres = 0;

    if (m_pDataObj)  //Paranoid check, m_pDataObj should have something by now...
       hres = m_pDataObj->GetData(&fmte, &medium);

    if (SUCCEEDED(hres))
    {
        //Find out how many files the user has selected...
        UINT cbFiles = 0;
        LPCSHELLEXT lpcsext = this;

        if (medium.hGlobal)
            cbFiles = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, 0, 0);

        if (cbFiles < 2)
        {
            //OK, the user has only selected a single file, so lets go ahead
            //and add the property sheet.  Note that it doesn't HAVE to be this
            //way, but for simplicity and educational reasons, I'll only add
            //the property sheet if a single .GAK file is selected.

            //Get the name of the file the user has clicked on
if (cbFiles)
               DragQueryFile((HDROP)medium.hGlobal,
                             0,
                             m_szPropSheetFileUserClickedOn,
                             sizeof(m_szPropSheetFileUserClickedOn));

//
// Create a property sheet page object from a dialog box.
//
// We store a pointer to our class in the psp.lParam, so we
            // can access our class members from within the GAKPageDlgProc
//
// If the page needs more instance data, you can append
// arbitrary size of data at the end of this structure,
// and pass it to the CreatePropSheetPage. In such a case,
// the size of entire data structure (including page specific
// data) must be stored in the dwSize field.   Note that in
            // general you should NOT need to do this, as you can simply
            // store a pointer to date in the lParam member.
//

            psp.dwSize      = sizeof(psp);// no extra data.
            psp.dwFlags     = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK;
            psp.hInstance   = g_hmodThisDll;
            psp.pszTemplate = MAKEINTRESOURCE(IDD_GAKPAGE);
            psp.hIcon       = 0;
            psp.pszTitle    = "GAK Color";
            psp.pfnDlgProc  = GAKPageDlgProc;
            psp.pcRefParent = &g_cRefThisDll;
            psp.pfnCallback = GAKPageCallback;
            psp.lParam      = (LPARAM)lpcsext;

            AddRef();
            hpage = CreatePropertySheetPage(&psp);

            if (hpage)
            {
                if (!lpfnAddPage(hpage, lParam))
                {
                    DestroyPropertySheetPage(hpage);
                    Release();
                }
            }
        }
    }

    return NOERROR;
}

//
//  FUNCTION: CShellExt::ReplacePage(UINT, LPFNADDPROPSHEETPAGE, LPARAM)
//
//  PURPOSE: Called by the shell only for Control Panel property sheet
//           extensions
//
//  PARAMETERS:
//    uPageID         -  ID of page to be replaced
//    lpfnReplaceWith -  Pointer to the Shell's Replace function
//    lParam          -  Passed as second parameter to lpfnReplaceWith
//
//  RETURN VALUE:
//
//    E_FAIL, since we don't support this function.  It should never be
//    called.

//  COMMENTS:
//

STDMETHODIMP CShellExt::ReplacePage(UINT uPageID,
                                    LPFNADDPROPSHEETPAGE lpfnReplaceWith,
                                    LPARAM lParam)
{
    ODS("CShellExt::ReplacePage()\r\n");

    return E_FAIL;
}


COPYHOOK.CPP

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   copyhook.cpp
//
//  PURPOSE:   Implements the ICopyHook member functions necessary to support
//             the copy hook portioins of this shell extension.
//             Copy hook handlers are called each time a folder is copied, moved,
//             renamed, etc. in the system.  Note that the CopyCallback is NOT
//             called for each file, but only for entire folders.
//

#include "priv.h"
#include "shellext.h"

//
//  FUNCTION: CShellExt::CopyCallback(HWND,
//                                    UINT,
//                                    UINT,
//                                    LPCSTR,
//                                    DWORD,
//                                    LPCSTR,
//                                    DWORD)
//
//  PURPOSE: Called by the shell when a folder is being manipulated.
//
//  PARAMETERS:
//    hwnd          - Window handle to use for any UI stuff
//    wFunc         - what operation is being done
//    wFlags        - and flags (FOF_*) set in the initial call
//                    to the file operation
//    pszSrcFile    - name of the source file
//    dwSrcAttribs  - file attributes of the source file
//    pszDestFile   - name of the destiation file (for move and renames)
//    dwDestAttribs - file attributes of the destination file
//
//  RETURN VALUE:
//
//    IDYES    - allow the operation
//    IDNO     - disallow the operation on this file, but continue with
//               any other operations (eg. batch copy)
//    IDCANCEL - disallow the current operation and cancel any pending
//               operations
//
//  COMMENTS:
//

STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND hwnd,
                                            UINT wFunc,
                                            UINT wFlags,
                                            LPCSTR pszSrcFile,
                                            DWORD dwSrcAttribs,
                                            LPCSTR pszDestFile,
                                            DWORD dwDestAttribs)
{
    ODS("CShellExt::CopyCallback\r\n");

    MessageBox(NULL, "CopyCallback!", "SHELLEXT", MB_OK);

    return IDYES;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by shellext.rc
//
#define IDI_ICON1                       129
#define IDI_ICON2                       130
#define IDI_ICON3                       131
#define IDD_GAKPAGE                     132
#define IDI_ICON4                       133
#define IDI_ICON5                       134
#define IDI_ICON6                       135
#define IDC_RED                         1000
#define IDC_GREEN                       1001
#define IDC_BLUE                        1002

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        136
#define _APS_NEXT_COMMAND_VALUE         32771
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SHELLEXT.CPP

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   shellext.cpp
//
// Purpose:  Implements the class factory code as well as CShellExt::QI,
//           CShellExt::AddRef and CShellExt::Release code.

#include "priv.h"

//
// Initialize GUIDs (should be done only and at-least once per DLL/EXE)
//
#pragma data_seg(".text")
#define INITGUID
#include <initguid.h>
#include <shlguid.h>
#include "shellext.h"
#pragma data_seg()

//
// Global variables
//
UINT      g_cRefThisDll = 0;    // Reference count of this DLL.
HINSTANCE g_hmodThisDll = NULL;// Handle to this DLL itself.

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        ODS("In DLLMain, DLL_PROCESS_ATTACH\r\n");

        // Extension DLL one-time initialization

        g_hmodThisDll = hInstance;
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        ODS("In DLLMain, DLL_PROCESS_DETACH\r\n");
    }

    return 1;   // ok
}

//---------------------------------------------------------------------------
// DllCanUnloadNow
//---------------------------------------------------------------------------

STDAPI DllCanUnloadNow(void)
{
    ODS("In DLLCanUnloadNow\r\n");

    return (g_cRefThisDll == 0 ? S_OK : S_FALSE);
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
{
    ODS("In DllGetClassObject\r\n");

    *ppvOut = NULL;

    if (IsEqualIID(rclsid, CLSID_ShellExtension))
    {
        CShellExtClassFactory *pcf = new CShellExtClassFactory;

        return pcf->QueryInterface(riid, ppvOut);
    }

    return CLASS_E_CLASSNOTAVAILABLE;
}

CShellExtClassFactory::CShellExtClassFactory()
{
    ODS("CShellExtClassFactory::CShellExtClassFactory()\r\n");

    m_cRef = 0L;

    g_cRefThisDll++;
}

CShellExtClassFactory::~CShellExtClassFactory()
{
    g_cRefThisDll--;
}

STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID riid,
                                                   LPVOID FAR *ppv)
{
    ODS("CShellExtClassFactory::QueryInterface()\r\n");

    *ppv = NULL;

    // Any interface on this object is the object pointer

    if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
    {
        *ppv = (LPCLASSFACTORY)this;

        AddRef();

        return NOERROR;
    }

    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CShellExtClassFactory::Release()
{
    if (--m_cRef)
        return m_cRef;

    delete this;

    return 0L;
}

STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
                                                      REFIID riid,
                                                      LPVOID *ppvObj)
{
    ODS("CShellExtClassFactory::CreateInstance()\r\n");

    *ppvObj = NULL;

    // Shell extensions typically don't support aggregation (inheritance)

    if (pUnkOuter)
    return CLASS_E_NOAGGREGATION;

    // Create the main shell extension object.  The shell will then call
    // QueryInterface with IID_IShellExtInit--this is how shell extensions are
    // initialized.

    LPCSHELLEXT pShellExt = new CShellExt();  //Create the CShellExt object

    if (NULL == pShellExt)
    return E_OUTOFMEMORY;

    return pShellExt->QueryInterface(riid, ppvObj);
}

STDMETHODIMP CShellExtClassFactory::LockServer(BOOL fLock)
{
    return NOERROR;
}

// *********************** CShellExt *************************
CShellExt::CShellExt()
{
    ODS("CShellExt::CShellExt()\r\n");

    m_cRef = 0L;
    m_pDataObj = NULL;

    g_cRefThisDll++;
}

CShellExt::~CShellExt()
{
    if (m_pDataObj)
        m_pDataObj->Release();

    g_cRefThisDll--;
}

STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv)
{
    *ppv = NULL;

    if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
    {
        ODS("CShellExt::QueryInterface()==>IID_IShellExtInit\r\n");

    *ppv = (LPSHELLEXTINIT)this;
    }
    else if (IsEqualIID(riid, IID_IContextMenu))
    {
        ODS("CShellExt::QueryInterface()==>IID_IContextMenu\r\n");

        *ppv = (LPCONTEXTMENU)this;
    }
    else if (IsEqualIID(riid, IID_IExtractIcon))
    {
        ODS("CShellExt::QueryInterface()==>IID_IExtractIcon\r\n");

        *ppv = (LPEXTRACTICON)this;
    }
    else if (IsEqualIID(riid, IID_IPersistFile))
    {
        ODS("CShellExt::QueryInterface()==>IPersistFile\r\n");

        *ppv = (LPPERSISTFILE)this;
    }
    else if (IsEqualIID(riid, IID_IShellPropSheetExt))
    {
        ODS("CShellExt::QueryInterface()==>IShellPropSheetExt\r\n");

        *ppv = (LPSHELLPROPSHEETEXT)this;
    }
    else if (IsEqualIID(riid, IID_IShellCopyHook))
    {
        ODS("CShellExt::QueryInterface()==>ICopyHook\r\n");

        *ppv = (LPCOPYHOOK)this;
    }

    if (*ppv)
    {
        AddRef();

        return NOERROR;
    }

    ODS("CShellExt::QueryInterface()==>Unknown Interface!\r\n");

return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CShellExt::AddRef()
{
    ODS("CShellExt::AddRef()\r\n");

    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CShellExt::Release()
{
    ODS("CShellExt::Release()\r\n");

    if (--m_cRef)
        return m_cRef;

    delete this;

    return 0L;
}


SHELLEXT.H

//
// The class ID of this Shell extension class.
//
// class id:  87b9bd00-c65c-11cd-a259-00dd010e8c28
//
//
// NOTE!!!  If you use this shell extension as a starting point,
//          you MUST change the GUID below.  Simply run UUIDGEN.EXE
//          to generate a new GUID.
//

#define ODS(sz) OutputDebugString(sz)

#ifndef _SHELLEXT_H
#define _SHELLEXT_H

DEFINE_GUID(CLSID_ShellExtension, 0x87b9bd00L, 0xc65c, 0x11cd, 0xa2, 0x59, 0x00, 0xdd, 0x01, 0x0e, 0x8c, 0x28 );

// this class factory object creates context menu handlers for Windows 95 shell
class CShellExtClassFactory : public IClassFactory
{
protected:
ULONGm_cRef;

public:
CShellExtClassFactory();
~CShellExtClassFactory();

//IUnknown members
STDMETHODIMPQueryInterface(REFIID, LPVOID FAR *);
STDMETHODIMP_(ULONG)AddRef();
STDMETHODIMP_(ULONG)Release();

//IClassFactory members
STDMETHODIMPCreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *);
STDMETHODIMPLockServer(BOOL);

};
typedef CShellExtClassFactory *LPCSHELLEXTCLASSFACTORY;

// this is the actual OLE Shell context menu handler
class CShellExt : public IContextMenu,
                         IShellExtInit,
                         IExtractIcon,
                         IPersistFile,
                         IShellPropSheetExt,
                         ICopyHook
{
public:
    char         m_szPropSheetFileUserClickedOn[MAX_PATH];  //This will be the same as
                                                            //m_szFileUserClickedOn but I include
                                                            //here for demonstration.  That is,
                                                            //m_szFileUserClickedOn gets filled in
                                                            //as a result of this sample supporting
                                                            //the IExtractIcon and IPersistFile
                                                            //interface.  If this sample *only* showed
                                                            //a Property Sheet extesion, you would
                                                            //need to use the method I do here to find
                                                            //the filename the user clicked on.

protected:
ULONG        m_cRef;
LPDATAOBJECT m_pDataObj;
    char         m_szFileUserClickedOn[MAX_PATH];

STDMETHODIMP DoGAKMenu1(HWND hParent,
                        LPCSTR pszWorkingDir,
                        LPCSTR pszCmd,
                            LPCSTR pszParam,
                            int iShowCmd);

STDMETHODIMP DoGAKMenu2(HWND hParent,
                        LPCSTR pszWorkingDir,
                        LPCSTR pszCmd,
                            LPCSTR pszParam,
                            int iShowCmd);

STDMETHODIMP DoGAKMenu3(HWND hParent,
                        LPCSTR pszWorkingDir,
                        LPCSTR pszCmd,
                            LPCSTR pszParam,
                            int iShowCmd);

STDMETHODIMP DoGAKMenu4(HWND hParent,
                        LPCSTR pszWorkingDir,
                        LPCSTR pszCmd,
                            LPCSTR pszParam,
                            int iShowCmd);
public:
CShellExt();
~CShellExt();

//IUnknown members
STDMETHODIMPQueryInterface(REFIID, LPVOID FAR *);
STDMETHODIMP_(ULONG)AddRef();
STDMETHODIMP_(ULONG)Release();

//IShell members
STDMETHODIMPQueryContextMenu(HMENU hMenu,
                                         UINT indexMenu,
                                         UINT idCmdFirst,
                                             UINT idCmdLast,
                                             UINT uFlags);

STDMETHODIMPInvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);

STDMETHODIMPGetCommandString(UINT idCmd,
                                         UINT uFlags,
                                         UINT FAR *reserved,
                                             LPSTR pszName,
                                             UINT cchMax);

//IShellExtInit methods
STDMETHODIMP    Initialize(LPCITEMIDLIST pIDFolder,
                                   LPDATAOBJECT pDataObj,
                                   HKEY hKeyID);

    //IExtractIcon methods
    STDMETHODIMP GetIconLocation(UINT   uFlags,
                                 LPSTR  szIconFile,
                                 UINT   cchMax,
                                 int   *piIndex,
                                 UINT  *pwFlags);

    STDMETHODIMP Extract(LPCSTR pszFile,
                         UINT   nIconIndex,
                         HICON  *phiconLarge,
                         HICON  *phiconSmall,
                         UINT   nIconSize);

    //IPersistFile methods
    STDMETHODIMP GetClassID(LPCLSID lpClassID);

    STDMETHODIMP IsDirty();

    STDMETHODIMP Load(LPCOLESTR lpszFileName, DWORD grfMode);

    STDMETHODIMP Save(LPCOLESTR lpszFileName, BOOL fRemember);

    STDMETHODIMP SaveCompleted(LPCOLESTR lpszFileName);

    STDMETHODIMP GetCurFile(LPOLESTR FAR* lplpszFileName);

    //IShellPropSheetExt methods
    STDMETHODIMP AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam);

    STDMETHODIMP ReplacePage(UINT uPageID,
                             LPFNADDPROPSHEETPAGE lpfnReplaceWith,
                             LPARAM lParam);

    //ICopyHook method
    STDMETHODIMP_(UINT) CopyCallback(HWND hwnd,
                                     UINT wFunc,
                                     UINT wFlags,
                                     LPCSTR pszSrcFile,
                                     DWORD dwSrcAttribs,
                                     LPCSTR pszDestFile,
                                     DWORD dwDestAttribs);

};
typedef CShellExt *LPCSHELLEXT;

#endif // _SHELLEXT_H


SHEXINIT.CPP

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   shexinit.cpp
//
//  PURPOSE:   Implements the IShellExtInit member function necessary to support
//             the context menu and property sheet portioins of this shell extension.
//

#include "priv.h"
#include "shellext.h"

//
//  FUNCTION: CShellExt::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY)
//
//  PURPOSE: Called by the shell when initializing a context menu or property
//           sheet extension.
//
//  PARAMETERS:
//    pIDFolder - Specifies the parent folder
//    pDataObj  - Spefifies the set of items selected in that folder.
//    hRegKey   - Specifies the type of the focused item in the selection.
//
//  RETURN VALUE:
//
//    NOERROR in all cases.
//
//  COMMENTS:   Note that at the time this function is called, we don't know
//              (or care) what type of shell extension is being initialized.
//              It could be a context menu or a property sheet.
//

STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST pIDFolder,
                                   LPDATAOBJECT pDataObj,
                                   HKEY hRegKey)
{
    ODS("CShellExt::Initialize()\r\n");

    // Initialize can be called more than once

    if (m_pDataObj)
    m_pDataObj->Release();

    // duplicate the object pointer and registry handle

    if (pDataObj)
    {
    m_pDataObj = pDataObj;
    pDataObj->AddRef();
    }

    return NOERROR;
}


APP32.C

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "app32.h"
#include <windowsx.h>

#include "resource.h"

#define MYWM_NOTIFYICON(WM_APP+100)

//---------------------------------------------------------------------------
// Global to everybody...
HINSTANCE g_hinst;

#ifndef ARRAYSIZE
#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))
#endif

struct _DLGITEMS
{
DWORD dwStart;
UINT uNotify;
UINT uDelayID;
UINT uState1;
UINT uTip1;
UINT uState2;
UINT uTip2;
} g_sDlgItems [] =
{
{
0, IDC_NOTIFY1, IDC_DELAY1, IDC_STATE11, IDC_TIP11, IDC_STATE12, IDC_TIP12,
},
{
0, IDC_NOTIFY2, IDC_DELAY2, IDC_STATE21, IDC_TIP21, IDC_STATE22, IDC_TIP22,
},
{
0, IDC_NOTIFY3, IDC_DELAY3, IDC_STATE31, IDC_TIP31, IDC_STATE32, IDC_TIP32,
},
} ;

BOOL TrayMessage(HWND hDlg, DWORD dwMessage, UINT uID, HICON hIcon, PSTR pszTip)
{
        BOOL res;

NOTIFYICONDATA tnd;

tnd.cbSize= sizeof(NOTIFYICONDATA);
tnd.hWnd= hDlg;
tnd.uID= uID;

tnd.uFlags= NIF_MESSAGE|NIF_ICON|NIF_TIP;
tnd.uCallbackMessage= MYWM_NOTIFYICON;
tnd.hIcon= hIcon;
if (pszTip)
{
lstrcpyn(tnd.szTip, pszTip, sizeof(tnd.szTip));
}
else
{
tnd.szTip[0] = '\0';
}

res = Shell_NotifyIcon(dwMessage, &tnd);

if (hIcon)
    DestroyIcon(hIcon);

return res;
}

LRESULT IconDrawItem(LPDRAWITEMSTRUCT lpdi)
{
HICON hIcon;

hIcon = (HICON)LoadImage(g_hinst, MAKEINTRESOURCE(lpdi->CtlID), IMAGE_ICON,
16, 16, 0);
if (!hIcon)
{
return(FALSE);
}

DrawIconEx(lpdi->hDC, lpdi->rcItem.left, lpdi->rcItem.top, hIcon,
16, 16, 0, NULL, DI_NORMAL);

return(TRUE);
}

void StateChange(HWND hDlg, UINT uIndex, UINT uSelect)
{
UINT uState1, uState2;
HWND hwndIcon;
LPCSTR pszIDIcon;
UINT uTip;
char szTip[64];

uState1 = g_sDlgItems[uIndex].uState1;
uState2 = g_sDlgItems[uIndex].uState2;

// if !uSelect, find out which button is selected
if (!uSelect)
{
uSelect = IsDlgButtonChecked(hDlg, uState2) ? uState2 : uState1;
}
// if uSelect<0, find out shich button is NOT selected
else if ((int)uSelect < 0)
{
uSelect = IsDlgButtonChecked(hDlg, uState2) ? uState1 : uState2;
}

CheckRadioButton(hDlg, uState1, uState2, uSelect);

// If there is a tip specified, use it, otherwise use the default
uTip = uSelect==uState1
? g_sDlgItems[uIndex].uTip1 : g_sDlgItems[uIndex].uTip2;
if (!GetDlgItemText(hDlg, uTip, szTip, sizeof(szTip))
&& !LoadString(g_hinst, uSelect, szTip, sizeof(szTip)))
{
*szTip = '\0';
}

// HACK: The ID of window after the radio button is the ID of the icon
hwndIcon = GetWindow(GetDlgItem(hDlg, uSelect), GW_HWNDNEXT);
pszIDIcon = MAKEINTRESOURCE(GetDlgCtrlID(hwndIcon));

TrayMessage(hDlg, NIM_MODIFY, g_sDlgItems[uIndex].uNotify,
LoadImage(g_hinst, pszIDIcon, IMAGE_ICON, 16, 16, 0), szTip);
}

void NotifyDelete(HWND hDlg, UINT uIndex)
{
TrayMessage(hDlg, NIM_DELETE, g_sDlgItems[uIndex].uNotify, NULL, NULL);
}

void NotifyAdd(HWND hDlg, UINT uIndex)
{
TrayMessage(hDlg, NIM_ADD, g_sDlgItems[uIndex].uNotify, NULL, NULL);

StateChange(hDlg, uIndex, 0);
}

void NotifyChange(HWND hDlg, UINT uIndex)
{
UINT uDelay;
BOOL bTranslated;
BOOL bEnable;

if (IsDlgButtonChecked(hDlg, g_sDlgItems[uIndex].uNotify))
{
uDelay = GetDlgItemInt(hDlg, g_sDlgItems[uIndex].uDelayID,
&bTranslated, FALSE);
if (uDelay)
{
g_sDlgItems[uIndex].dwStart = GetTickCount() + uDelay*60000;
SetTimer(hDlg, uIndex, 60000, NULL);
}
else
{
NotifyAdd(hDlg, uIndex);
}
}
else
{
NotifyDelete(hDlg, uIndex);
}

for (uIndex=0, bEnable=FALSE; uIndex<ARRAYSIZE(g_sDlgItems); ++uIndex)
{
if (IsDlgButtonChecked(hDlg, g_sDlgItems[uIndex].uNotify))
{
bEnable = TRUE;
}
}
EnableWindow(GetDlgItem(hDlg, IDABORT), bEnable);
}

BOOL CALLBACK TestAppDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
break;

case WM_DRAWITEM:
return(IconDrawItem((LPDRAWITEMSTRUCT)lParam));
break;

case WM_DESTROY:
NotifyDelete(hDlg, 0);
NotifyDelete(hDlg, 1);
NotifyDelete(hDlg, 2);
break;

case WM_TIMER:
if (wParam >= ARRAYSIZE(g_sDlgItems))
{
break;
}

if ((int)(GetTickCount() - g_sDlgItems[wParam].dwStart) < 0)
{
break;
}

KillTimer(hDlg, wParam);
NotifyAdd(hDlg, wParam);
break;

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
EndDialog(hDlg, TRUE);
break;

case IDABORT:
ShowWindow(hDlg, SW_HIDE);
break;

case IDC_NOTIFY1:
NotifyChange(hDlg, 0);
break;

case IDC_NOTIFY2:
NotifyChange(hDlg, 1);
break;

case IDC_NOTIFY3:
NotifyChange(hDlg, 2);
break;

case IDC_STATE11:
case IDC_STATE12:
StateChange(hDlg, 0, GET_WM_COMMAND_ID(wParam, lParam));
break;

case IDC_STATE21:
case IDC_STATE22:
StateChange(hDlg, 1, GET_WM_COMMAND_ID(wParam, lParam));
break;

case IDC_STATE31:
case IDC_STATE32:
StateChange(hDlg, 2, GET_WM_COMMAND_ID(wParam, lParam));
break;
}
break;

case MYWM_NOTIFYICON:
switch (lParam)
{
case WM_LBUTTONDOWN:
switch (wParam)
{
case IDC_NOTIFY1:
StateChange(hDlg, 0, (UINT)-1);
break;

case IDC_NOTIFY2:
StateChange(hDlg, 1, (UINT)-1);
break;

case IDC_NOTIFY3:
StateChange(hDlg, 2, (UINT)-1);
break;

default:
break;
}
break;

case WM_RBUTTONDOWN:
ShowWindow(hDlg, SW_SHOW);
SetForegroundWindow(hDlg);// make us come to the front
break;

default:
break;
}
break;

default:
return(FALSE);
}

return(TRUE);
}

//---------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
g_hinst = hInstance;
DialogBox(hInstance, MAKEINTRESOURCE(IDD_APP32), NULL, TestAppDlgProc);
return(FALSE);
}


APP32.H

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
#define STRICT

#define _INC_OLE
#include <windows.h>
#undef  _INC_OLE

#ifndef WIN32
#include <shell.h>
#endif

extern HINSTANCE g_hinst;


RESOURCE.H

//{{NO_DEPENDENCIES}}
// App Studio generated include file.
// Used by APP32.RC
//
#define IDI_APP32                       102
#define IDD_APP32                       103
#define IDI_STATE11                     105
#define IDI_STATE12                     106
#define IDI_STATE21                     107
#define IDI_STATE22                     108
#define IDI_STATE31                     109
#define IDI_STATE32                     110
#define IDC_NOTIFY1                     1006
#define IDC_NOTIFY2                     1007
#define IDC_NOTIFY3                     1008
#define IDC_STATE11                     1009
#define IDC_STATE12                     1010
#define IDC_STATE21                     1011
#define IDC_STATE22                     1012
#define IDC_STATE31                     1013
#define IDC_STATE32                     1014
#define IDC_DELAY1                      1017
#define IDC_TIP11                       1018
#define IDC_TIP12                       1019
#define IDC_DELAY2                      1020
#define IDC_TIP21                       1021
#define IDC_TIP22                       1022
#define IDC_DELAY3                      1023
#define IDC_TIP31                       1024
#define IDC_TIP32                       1025
#define IDC_STATIC                      -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        111
#define _APS_NEXT_COMMAND_VALUE         106
#define _APS_NEXT_CONTROL_VALUE         1019
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif