From: tomh@wes.on.ca (Tom Haapanen)
Newsgroups: comp.os.ms-windows.announce
Subject: Windows Programmer FAQ: ASCII format, 02/04
Date: 27 Jan 93 16:59:07 GMT
Followup-To: comp.os.ms-windows.misc
Organization: Waterloo Engineering Software

The following is part 02 of 04 of the ASCII text version of the
Microsoft Windows Programmer FAQ.  While some of you may find this the best
format, the more popular format is the WinHelp file.  These are posted
simultaneously with the ASCII version to comp.binaries.ms-windows.

You can obtain a copy of the WinHelp version as "faqprg01.zip" by ftp
from sonygate.sony.com [192.63.138.2], in /pub/comp.binaries.ms-windows.
It will also be placed on ftp.cica.indiana.edu once disk space permits.



BEGIN--------------cut here--------cut here------------cut here----------
  from the system menu, if there is one.
  
  Also, you can  alternately disable resizing by creating the windows with
  ~WS_THICKFRAME, and disabling the Size... item on the system menu.
  
...........................................................................

5.1.10.  Getting the handle of the active window
------------------------------------------------
  To get the active window's handle, just call
       hActiveWindow = GetFocus();
  
...........................................................................

5.1.11.  Keeeping a window on top
---------------------------------
  To keep a window on top of all other windows without requiring it to be the
  active window (akin to the behaviour exhibited by the Windows 3.1 clock),
  you can make the following function call:
       SetWindowPos(hWnd, (HWND)-1, 0, 0, 0, 0,
                    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  Note that it is not considered polite behavior by an application to always
  keep its window topmost, unless you at least give the user the option of
  disabling this "feature".
  
...........................................................................

5.1.12.  Limiting window size
-----------------------------
  To limit window size, you'll need to process the WM_GETMINMAXINFO message,
  and fill in the structure describing the minimum and maximum window sizes.
  
...........................................................................

5.1.13.  Right-justifying menu items
------------------------------------
  To right-justify an entire menu item or just a part of it, place a \a in
  the string just before the right-justified part.
  
  Incidentally, the Windows 3.0 CUA guidelines no longer call for right-
  justifying the Help menu on the menu bar.
  
...........................................................................

5.1.14.  Right-justifying menu items at runtime
-----------------------------------------------
  It's undocumented, but what you need is a 0x08 in the menu string.  The
  easiest way to do this is to place a \b in the string before the right-
  justified part (either the text of the accelerator key).
  
  Incidentally, the Windows 3.0 CUA guidelines no longer call for right
  justifying the Help menu on the menu bar.
  
...........................................................................

5.1.15.  Trapping mouse clicks on desktop
-----------------------------------------
  To trap mouseclicks on the desktop, you will need to subclass the desktop
  window.  The following code fragment illustrates the technique (sample code
  courtesy of Blake Coverett, blakeco@microsoft.com ):
  
  To subclass the desktop:
      hWndDesktop=GetDesktopWindow();
      hSaveTask=GetCurrentTask();
      lpfnDesktop=(FARPROC)GetWindowLong(hWndDesktop, GWL_WNDPROC);
      lpfnSubClassProc=MakeProcInstance((FARPROC)WndProc, hInst);
      SetWindowLong(hWndDesktop, GWL_WNDPROC,
                    (LPARAM)(LONG)lpfnSubClassProc);
  
  and then to undo it when ready to unload:
      SetWindowLong(hWnd,GWL_WNDPROC, (LPARAM)(LONG)lpfnDesktop);
      PostAppMessage(hSaveTask,WM_QUIT,0,0);
  
...........................................................................

5.1.16.  Using status bars with MDI
-----------------------------------
  Add the following code fragments to the indicated places in the WinProc()
  of an application, or the FrameWinProc() of a MDI application.
  
  case WM_CREATE:
      hWndClient = CreateWindow( "MDIClient",...,
                      WS_CHILD|WS_VISIBLE|WS_CLIBSIBLINGS|
                      WS_HSCROLL|WS_VSCROLL,... );
      hWndStatus = CreateWindow( "Static",...,
                      WS_CHILD|WS_VISIBLE|SS_LEFT|SS_NOPREFIX,... );
      ...
  case WM_SIZE:
      GetClientRect( hWnd,&rect );
      // Calculate DIVIDING_LINE such that.
      // rect.top < DIVIDING_LINE < rect.bottom
      // One choice:
      // DIVIDING_LINE = rect.bottom - GetSystemMetrics(SM_CYMENU);
      MoveWindow( hWndClient,rect.left,rect.top,
                  rect.right,DIVIDING_LINE,TRUE );
      MoveWindow( hWndStatus,rect.left,DIVIDING_LINE,
                  rect.right,rect.bottom,TRUE );
      break;  // Do *not* pass to DefFrameProc() of MDI app!!!
      ...
      // To change the status text:
      SendMessage( hWndStatus,WM_SETTEXT,0,(LONG)(LPSTR)szStatusText );
  
  Notes:
  * For non-MDI applications, all references to hWndClient above will simply
     be removed.
  * Menu selection can be tracked by setting the status text at a response to
     the WM_MENUSELECT message.
  * To draw a line between status bar and the rest of the client area, the
     DIVIDING_LINE should be adjusted in either MoveWindow() call to leave a
     gap between, which is called InvalidateRect() for, and actually being
     painted in response to the WM_PAINT.
  * The parent window should have WS_CLIPCHILDREN style bit set.
  
---------------------------------------------------------------------------

5.2.  Dialogs
=============
  
...........................................................................

5.2.1.  A dialog as an MDI child window [Borland OWL]
-----------------------------------------------------
  If you want to use a dialog as an MDI child window, you can use the
  following code as the basis of your own implementation.  Thanks to Martin
  Calsyn (martin@iastate.edu) for the sample code.
  
  What follows are three files:
       TMDIDLOG.H          The interface for modeless MDI-child dialogs.
       TMDIDLOG.CPP   The implementation of same
       EXAMPLE.CPP         An (partial) example of using the TMDIDLOG
  classes.
  
  You need to sub-class TMDIDialogWindow in order to give your MDI child a
  unique icon, etc (not shown in the example) and to implement the
  CreateChildDialog abstract method.  And, of course, you need to sub-class
  TMDIDialog in order to establish a transfer structure, interface objects
  and all the things you usually do with sub-classes of TDialog.
  
  The classes included below work by presenting the dialog as a child of a
  TBWindow which is itself a child of the MDI.  These classes are based on
  tips and suggestions offered by the Borland advisor line.
  
  Martin claims that the transfer of this technique to MS MFC is trivial.
  
  ---------------------------- TMDIDLOG.H -------------------------
  
  #ifndef __tmdidlog_h__
  #define __tmdidlog_h__
  
  #include <owl.h>
  #include <bwindow.h>
  
  _CLASSDEF(TMDIDialog)
  _CLASSDEF(TMDIDialogWindow)
  
  class TMDIDialog : public TDialog
  {
  public:
      TMDIDialog(PTWindowsObject AParent,LPSTR AName,PTModule AModule=NULL) :
      TDialog(AParent, AName, AModule) {};
      TMDIDialog(PTWindowsObject AParent,int ResourceId,PTModule
  AModule=NULL):
      TDialog( AParent, ResourceId, AModule ) {};
      virtual void WMSetFocus( RTMessage Msg ) = [WM_FIRST + WM_SETFOCUS];
      virtual void CloseWindow( int ret );
      virtual void CloseWindow();
      virtual void Cancel( RTMessage msg ) = [ID_FIRST + IDCANCEL];
  
      friend class TMDIDialogWindow;
  
  protected:
      TMDIDialogWindow     *mdiw;
  };
  
  class TMDIDialogWindow : public TBWindow
  {
  public:
      TMDIDialogWindow(PTWindowsObject aParent,LPSTR aName,
                       PTModule aModule = NULL);
      virtual void WMMDIActivate( RTMessage Msg ) = [WM_FIRST +
  WM_MDIACTIVATE];
      virtual void WMSetFocus( RTMessage Msg ) = [WM_FIRST + WM_SETFOCUS];
      virtual void WMSize( RTMessage Msg ) = [WM_FIRST + WM_SIZE];
      virtual void SetupWindow();
      virtual LPSTR GetClassName() { return "_TMDIDialogWindow"; };
      virtual void GetWindowClass( WNDCLASS& aWndClass );
      virtual void WMGetMinMaxInfo( RTMessage msg ) = [WM_FIRST +
  WM_GETMINMAXINFO];
      virtual TMDIDialog *CreateChildDialog() = 0;
  
  private:
      PTMDIDialog dlog;
  };
  
  #endif
  
  ------------------------- TMDIDLOG.CPP --------------------------
  
  #include "tmdidlog.h"
  
  TMDIDialogWindow::TMDIDialogWindow(PTWindowsObject aParent,
                  LPSTR aName,
                  PTModule aModule) :
            TBWindow(aParent,aName,aModule)
  {
      dlog = NULL;
      GetApplication()->SetKBHandler( this );
      SetFlags( WB_KBHANDLER, FALSE );
  }
  
  void TMDIDialogWindow::WMMDIActivate( RTMessage Msg )
  {
      TWindow::WMMDIActivate( Msg );
  
      if ( Msg.WParam == TRUE ) {
          GetApplication()->SetKBHandler(this);
          if (dlog!=NULL)
              SetFocus(((PTDialog)dlog)->HWindow );
      }
  }
  
  void TMDIDialogWindow::GetWindowClass( WNDCLASS& aWndClass )
  {
      TWindow::GetWindowClass(aWndClass);
  }
  
  void TMDIDialogWindow::WMSetFocus(RTMessage Msg)
  {
      PostMessage(HWindow, WM_MDIACTIVATE, TRUE, 0L );
      DefWndProc(Msg);
  }
  
  void TMDIDialogWindow::SetupWindow()
  {
      TWindow::SetupWindow();
  
      dlog = CreateChildDialog();
      if (dlog) {
          dlog->mdiw = this;
          RECT r;
          GetApplication()->MakeWindow(dlog);
          SetFocus(dlog->HWindow);
          GetWindowRect(HWindow,&r);
          Attr.X = r.left;
          Attr.Y = r.top;
          GetWindowRect(dlog->HWindow,&r);
          AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
          Attr.W = r.right - r.left;
          Attr.H = r.bottom - r.top;
          MoveWindow(HWindow,Attr.X, Attr.Y, Attr.W, Attr.H, 1);
      }
  }
  
  void TMDIDialogWindow::WMGetMinMaxInfo( RTMessage msg )
  {
      if (dlog) {
          MINMAXINFO *lpmmi = (MINMAXINFO *)msg.LParam;
          RECT r;
          POINT pt1,pt2;
          GetWindowRect(HWindow,&r);
          pt1.x = r.left;
          pt1.y = r.top;
          GetWindowRect(dlog->HWindow,&r);
          AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
          pt2.x = r.right - r.left;
          pt2.y = r.bottom - r.top;
          lpmmi->ptMaxSize = pt2;
          lpmmi->ptMaxPosition = pt1;
          lpmmi->ptMinTrackSize = pt2;
          lpmmi->ptMaxTrackSize = pt2;
      }
  }
  
  void TMDIDialogWindow::WMSize( RTMessage Msg )
  {
      RECT r;
      TWindow::WMSize( Msg );
      if (!IsIconic(HWindow)) {
          GetWindowRect(dlog->HWindow,&r);
          AdjustWindowRect(&r,WS_CAPTION|WS_BORDER,0);
          Attr.W = r.right - r.left;
          Attr.H = r.bottom - r.top;
          MoveWindow(HWindow, Attr.X, Attr.Y, Attr.W, Attr.H, 1);
      }
  }
  
  //
  //  TMDIDialog methods...
  //
  
  void TMDIDialog::WMSetFocus( RTMessage Msg )
  {
      GetApplication()->SetKBHandler( this );
      DefWndProc( Msg );
  }
  
  void TMDIDialog::CloseWindow(int ret)
  {
      TDialog::CloseWindow(ret);
      mdiw->CloseWindow();
  }
  
  void TMDIDialog::CloseWindow()
  {
      TDialog::CloseWindow();
      mdiw->CloseWindow();
  }
  
  void TMDIDialog::Cancel(RTMessage msg)
  {
      CloseWindow(IDCANCEL);
  }
  
  -------------------------- EXAMPLE.CPP --------------------------
  
  //
  //  #include owl, tmdidlog.h and others as needed
  //
  
  //
  //  ChildDialog is a dialog box defined in the RC file under
  //    the name 'MY_DLOG'.  The definition of MY_DLOG is pretty plain
  //    except that it should have the following attributes:
  //              STYLE WS_CHILD | WS_VISIBLE
  //    and optionally CLASS "BORDLG"
  //  That is, it should have no caption, sys menu, or border of any
  //  type.  It should also have an origin of 0,0 otherwise it will
  //  be offset within the MDI child window.
  //
  //  An instance of ClientDialog can be created as a modeless MDI
  //  client by doing : `new ChildDialogWindow(this, "Title", NULL)'
  //  from your MDI frame object or other appropriate place.
  //
  
  class ChildDialog : public TMDIDialog
  {
  public:
      ChildDialog(PTWindowsObject aParent,
                   LPSTR aName, PTModule aModule = NULL);
  
  };
  
  ChildDialogWindow::ChildDialogWindow(PTWindowsObject aParent,LPSTR aName,
                                         PTModule aModule)
            : TMDIDialogWindow(aParent,aName,aModule)
  {
      obj_id = object_id;
  }
  
  TMDIDialog* ChildDialogWindow::CreateChildDialog()
  {
      return (TMDIDialog*)new ChildDialog(this,(LPSTR)"MY_DLOG",NULL);
  }
  
  ChildDialog::ChildDialog(PTWindowsObject aParent,
                             LPSTR aName, PTModule aModule)
            : TMDIDialog(aParent,aName,aModule)
  {
      // The usual stuff:  Initialize the transfer structure, create
      //  interface objects, etc.
  
  }
  
  
...........................................................................

5.2.2.  Adding controls to a non-dialog window
----------------------------------------------
  You can do this by simply calling CreateWindow() with one of the predefined
  child window control class names (see the control class definition table in
  the SDK reference manual).
  
...........................................................................

5.2.3.  Doing a timeout in a dialog
-----------------------------------
  Start a timer in your WM_INITDIALOG processing. If your dialog box receives
  the WM_TIMER message, kill the timer and post yourself a WM_COMMAND messgae
  with wParam == IDOK. If the user presses any button, restart the timer.
  
...........................................................................

5.2.4.  Minimize button on modal dialog moves when clicked
----------------------------------------------------------
  It's a bug in Windows 3.1.  To duplicate this, create a modal dialog with
  the styles CAPTION, MODAL FRAME, MINIMIZE-BOX, activate the dialog, press
  the Minimize button -- and watch it move to the top right corner, on top of
  the modal frame!
  
  The workaround: don't use a Minimize box on a modal dialog.
  
...........................................................................

5.2.5.  Modifying common dialogs
--------------------------------
  If you want to modify a common dialog, do not delete controls from the
  templates, as the common dialog procedures expect these controls to exist.
  Instead, make them invisible or move them outside the boundaries of the
  dialog gox to prevent the user from seeing or accessing them.
  
...........................................................................

5.2.6.  Null dialog handles from Borland custom dialogs
-------------------------------------------------------
  If you keep getting null dialog handles with Borland C++ unless I have
  Turbo C++ running, your dialog is probably of the BorDlg class, which
  requires code in BWCC.DLL.  However, you have probably not done anything to
  force BWCC.DLL to be loaded with your application, so the dialog manager
  cannot find the necessary routines to draw the dialog.  The easiest way to
  force BWCC.DLL to be loaded is to call BWCCGetVersion() at the very
  beginning of your application, and to link in BWCC.LIB.
  
  Alternatly, you can do a
       WinExec("loadbwcc.exe");
  when you start up your application, as long as loadbwcc.exe is available.
  
...........................................................................

5.2.7.  Preventing switching away from a modal dialog
-----------------------------------------------------
  The design of the Windows API means that if there are two dialogs active
  simultaneously, the user can switch between the two, even one of them is
  modal.  To prevent this, you should use EnableWindow() to explicitly diable
  any modeless dialogs when your modal dialog starts up.
  
...........................................................................

5.2.8.  Using a dialog as your main window
------------------------------------------
  First, you have to create a dialog box first.  Include a class name(you
  name
  it).  Then, in your WinMain function, register a class using that class
  name
  and add the constant DLGWINDOWEXTRA to the window extra byte component.
  This
  constant is defined in Windows.h.  Then, use CreateDialog() to display the
  dialog, make sure the last 2 parameters are set to NULL.  In your WndProc
  function, instead of calling DefWindowProc, call DefDlgProc.  Then, you are
  basically all set.  Make sure in your dialog definition you create your
  dialog
  box initially visible.  For further information, see your docs.
  
  
...........................................................................

5.2.9.  Using Borland custom dialogs with other compilers
---------------------------------------------------------
  You can't integrate bwcc.dll with the Dialog Editor but you can manually
  modify the dialog file and use appropriate BWCC control classes and styles.
  
  Include bwcc.h in your header file and then add bwcc.lib to your link
  options (before libw.lib). Also make sure bwcc.dll can be found in either
  the Windows directory or the current directory when the app starts or in
  the path.
  
  Examples (thanks to Sam Espartero, sqe@hpcc01.corp.hp.com):
  
  Link Options:
      /align:16 /NOD PLAYCD USERCODE MCICDA SUPERCLS,PLAYCD.EXE,,
      LIBW MLIBCEW bwcc mmsystem, PLAYCD.DEF
  
  Dialog File:
  
       ABOUT DIALOG  4, 5, 199, 137
       STYLE WS_POPUPWINDOW | DS_MODALFRAME | WS_VISIBLE |
             WS_CLIPSIBLINGS | WS_DLGFRAME
       CAPTION "Sample BWCC Dialog"
       CLASS "BorDlg"
       BEGIN
           CONTROL "", 100, "Static", SS_ICON | WS_CHILD | WS_VISIBLE,
                   5, 16, 16, 16
           CONTROL "", -1, "BorShade", WS_CHILD | WS_BORDER,
                   33,6,161,126
           ...
       
           CONTROL "", IDOK, "BorBtn", BS_DEFPUSHBUTTON | WS_TABSTOP |
                   WS_CHILD, 154, 9, 32, 20
       END
  
  Message box:
  
       #ifdef BWCC
           BWCCMessageBox(GetActiveWindow(), "Unknown MCI Error!",
                          "MCI Error", MB_OK | MB_ICONHAND);
       #else
           MessageBox(GetActiveWindow(), "Unknown MCI Error!",
                      "MCI Error", MB_OK | MB_ICONHAND);
       #endif
  
---------------------------------------------------------------------------

5.3.  Controls
==============
  
...........................................................................

5.3.1.  Allowing ENTER in a multiline edit control
--------------------------------------------------
  To allow the use of the Enter key, there is no need to subclass the edit
  control.  An easier way in Windows 3.1 and later (which also works better!)
  is to specify ES_WANTRETURN as part of the style for the edit control (see
  the Windows 3.1 SDK documentation for details).
  
...........................................................................

5.3.2.  Aligning multi-column listboxes
---------------------------------------
  In the resource file make sure the list box has the LBS_USETABSTOPS style.
  When you add the items to the listbox, separate the fields with tabs.  You
  can either use the default tab stops, or set your own by sending the
  LBS_SETTABSTOPS message to the listbox. For more information, see the SDK
  Reference, volumes 1 and 2.
  
   It is also possible to use a fixed font, but the tabstop solution usually
  ends up looking much better.
  
...........................................................................

5.3.3.  Changing button colors
------------------------------
  In Windows 3.0, the button face is defined by two colors. The grey (white
  with EGA) face and a dark grey (grey if ega) shadow.  The colors also
  change when the button goes from a normal to pushed in state.  The
  WM_CTLCOLOR message only allows you to change one color at a time so to
  which of the button face colors should this apply?  (Windows 2.x button
  faces had only one color so it made sense.)
  
  Maybe something tricky could have been done by using the background color
  for the shadow and foreground color for the face and perhaps doing
  something strange to get the text color in another way... And how do you
  return 2 brushes (you now need a foreground and a background brush)?  Or
  maybe even better, make colors a property of the window and some windows
  could have multiple color properties...
  
  Anyway, Windows doesn't look at the WM_CTLCOLOR message for buttons and
  thus doesn't allow you to change the button colors.  Try it with a listbox
  instead...  The only way to change button colors is to specify
  ButtonColor=, ButtonShadow= and ButtonText= in the [Colors] section of your
  win.ini file.
  
  In Windows 3.1, the button text, shadow and face colors can also be defined
  using the Control Panel.
  
...........................................................................

5.3.4.  Creating controls at runtime in Visual Basic
----------------------------------------------------
  To create controls dynamically, you need to create a control array at
  design time.  You can then extend it at runtime, and specify new properties
  for the new controls you have created.
  
...........................................................................

5.3.5.  Combo boxes with tab stops
----------------------------------
  Windows does not support combo boxes with tab stops.  If you need such
  items, you have three choices:
     * Use an owner-draw combo box.  This will allow you to draw the items at
       the precise locations you need.  Text-only owner-draw controls are not
       difficult to implement.
     * Use GetTextExtent() to determine the length of each part, padding with
       blanks until it approximately lines up.  Fairly easy, but not fast and
       not precise.
     * Change the font of the control to either Courier or the fixed System
       font.  While this is simple to do, your combo box font will not be
       consistent with the rest of your dialog box controls.
  
...........................................................................

5.3.6.  Custom button bitmaps in Borland dialogs
------------------------------------------------
  To create owner-drawn buttons for new additional buttons beyond those
  supplied in bwcc.dll, you first need to create the button bitmaps.  Create
  them 63 pixels wide by 39 deep, and number them "logically".
  
  The bitmap numbers are related to the resource id of the button that you
  want to use them.  Use the following numbers:
       button id + 1000    Unpressed bitmap
       button id + 3000    Pressed bitmap
       button id + 2000    Unpressed bitmap, EGA
       button id + 4000    Pressed bitmap, EGA
  If you use these bitmap ids, bwcc.dll will use the bitmaps on the buttons
  automatically.
  
...........................................................................

5.3.7.  Drawing on a dialog background
--------------------------------------
  To draw on a dialog background, you can either subclass the dialog, or,
  more simply, respond to the WM_PAINT message in your dialog procedure,
  calling ValidateRect() after you have redrawn the background.
  
...........................................................................

5.3.8.  Hiding dialog controls
------------------------------
  EnableWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE), FALSE);
  ShowWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE), SW_HIDE);
  UpdateWindow(GetDlgItem(hDlg, IDD_CONTROLTOHIDE));
  
...........................................................................

5.3.9.  Listboxes with large amounts of data
--------------------------------------------
  A standard listbox is limited to 32K of data.  If you need a listbox with a
  larger number of items, you can use one of the following options:
     * Use an owner-draw listbox with LBS_HASSTRINGS.  This will allow 4096
       items.
     * Use an owner-draw listbox LBS_OWNERDRAWFIXED but without
       LBS_HASSTRINGS.  This is somewhat more work, but will allow 8192
       items.
  Some third-party libraries also implement listbox classes which can handle
  huge amounts of data.
  Finally, Microsoft has a virtual listbox implementation available by ftp at
  ftp.uu.net, in the directory /vendor/microsoft/developer-network.
  
...........................................................................

5.3.10.  Subclassing standard controls
--------------------------------------
  You can subclass standard controls by having your own window procedure
  handle the messages for the windows you create (using SetWindowLong()).
  The only caveat here is for useability: make sure that your subclassed
  controls don't behave in an unexpected manner.
  
  What is definitely a bad idea is modify the class procedure of a standard
  control (using SetClassLong()) and changing the window procedure for all
  such windows, as this will affect all edit controls in all applications
  currently running in the Windows session.
  
...........................................................................

5.3.11.  Using a window as a modal dialog
-----------------------------------------
  To use a window as a modal dialog, you will need to implement your own
  message loop for that window, complete with PeekMessage(),
  TranslateMessage() and DispatchMessage() calls.  This will allow you to
  accept events only for the current window, discarding all other events.
  
---------------------------------------------------------------------------

5.4.  Memory
============
  
...........................................................................

5.4.1.  Using new() in C++
--------------------------
  In Borland C++ 2.0, and in 3.x's medium model, new() ends up calling
  LocalAlloc(), allocating memory from your near 64K segment.  In BCC 3.x's
  large and compact models (and in Microsoft C/C++ 7.0), however, it will
  make one GlobalAlloc() and do subsegment allocations to allow you access to
  the full memory without making excessive demands on the system limit of
  4096 (8192 in 386 enhanced mode) global memory handles.
  
...........................................................................

5.4.2.  Global memory owned by DLL
----------------------------------
  If you use GlobalAlloc in a DLL, the application that called the DLL will
  own the object.  There is a way around this, though: allocate the memory
  using the GMEM_DDESHARE flag; this will make the allocating code segment
  (rather than the current task) own the memory.
  
...........................................................................

5.4.3.  Determining size of physical memory
-------------------------------------------
  You need to make a DPMI call to obtain that piece of information.  DPMI
  call 0500h with ES:DI pointing to a 30h byte buffer returns the "Free
  Memory Information":
  
      Offset  Description
      00h     Largest available free block in bytes
      04h     Maximum unlocked page allocation
      08h     Maximum locked page allocation
      0Ch     Linear address space size in pages
      10h     Total number of unlocked pages
      14h     Number of free pages
      18h     Total number of physical pages
      1Ch     Free linear address space in pages
      20h     Size of paging file/partition in pages
      24h-2Fh Reserved
  
  The size of one page in bytes can be determined by function 0604h, which
  returns the page size in bytes in BX:CX.  To call a DPMI function, invoke
  the interrupt 31h. Carry bit will be clear if call was successful.
  
  The complete DPMI 0.9 specification is available free from Intel Literature
  JP26, Santa Clara.  It's also available on ftp.cica.indiana.edu.
  
  
---------------------------------------------------------------------------

5.5.  GDI
=========
  
...........................................................................

5.5.1.  Animation
-----------------
  If you want to do good-quality animation under Windows 3.1 without
  requiring that each user have a 486/50 with an accelerated video card, you
  should consider using a differential animation technique.  There is a good
  example available on ftp.uunet.uu.net (and also on CompuServe) under
  /vendor/microsoft/multimedia/sample  called rleapp, which uses this
  technique.  Another sample program in the same directory, transblt,
  demonstrates a technique for doing fast BitBlits.
  
  Both techniques are also documented in the technotes in /vendor/microsoft/
  multimedia/technote.
  
...........................................................................

5.5.2.  Background color
------------------------
  If you insist on a white background, use
       WinClass.hbrBackground = GetStockObject(GCW_WHITEBRUSH);
  for your window background.  If it doesn't matter to you, however, you
  should use the Control Panel-defined window background color instead:
       WinClass.hbrBackground = CreateSolidBrush(COLOR_WINDOW + 1);
  
  
...........................................................................

5.5.3.  Changing palette entries in 16-color mode
-------------------------------------------------
  If you are using a standard driver, you will need to bypass Windows to do
  it (if you happen to have a 16-color driver which support palettes, you can
  use standard Windows palette management functions).
  
  Microsoft will tell you to buy the DDK, but there is another way.  Now, the
  Windows system palette maps onto the VGA 16-color palette as follows:
  
  VGAPAL SYSPAL   VGAPAL  SYSPAL
    00     00       08      07
    01     01       09      13
    02     02       10      14
    03     03       11      15
    04     04       12      16
    05     05       13      17
    06     06       14      18
    07     12       15      19
  
  So you can define some macros to take care of the mapping:
  
  #define syspal(n) (n<7 ? n : (n>8 ? n+4 : (n=7 ? 12 : 7)))
  #define vgapal(n) (n<7 ? n : (n>12 ? n-4 : (n=7 ? 8 : 7)))
  
  When you get a WM_SETFOCUS event, save the current state of the hardware
  colormap and installs the one you want.  When you get a WM_KILLFOCUS event,
  restore the original palette. Don't use the palette registers directly,
  though, just modify the color registersthat they point to.  (For details on
  redefining a VGA palette, see a book such as A Programmer's Guide to PC and
  PS/2 Video Systems by Richard Wilton.)
  
...........................................................................

5.5.4.  DIB bitmaps
-------------------
  Microsoft has higher-level DIB library and DLL available for downloading
  from CompuServe.  Unfortunately, it is not currently available by ftp.
  
...........................................................................

5.5.5.  Speeding up WM_PAINT redraws
------------------------------------
  To speed up your WM_PAINT processing, you may want to use a technique
  similar to the following one, as presented by John Grant
  (jagrant@emr1.emr.ca):
  
  There are two reasons for redrawing your window:
     *   because Windows tells you to do it
     *   because something application specific requires it
  
  You shouldn't have to redraw everything from scratch every time you get a
  WM_PAINT message - just save it as a bitmap and repaint from the bitmap.
  However, there are cases when the bitmap becomes invalid and you have to re-
  paint the hard way.
  
  In the main window WndProc, I respond to Windows messages as follows (note
  there may be other code too, I just put in the stuff relevant to the
  redraw):
  
  case WM_CREATE:     KillBitmap();
                      break;
  case WM_SIZE:       KillBitmap();
                      break;
  case WM_PAINT:      DrawMyPicture(hwnd);
                      break;
  case WM_DESTROY:    KillBitmap();
                      PostQuitMessage(0);
                      break;
  
  Notice that you should not do KillBitmap() when I handle WM_PAINT.  If, in
  response to an application-specific condition, you want to force a redraw,
  do the following:
  
       KillBitmap();
       InvalidateRect(hwnd,NULL,TRUE);    // generates WM_PAINT
  
  Now, for an application specific sample code fragment:
  
  // global variable
       static HBITMAP hbitmap_main = NULL;
  
  void DrawMyPicture(HWND hwnd)
  {
      PAINTSTRUCT ps;
      HDC hdc;
  
      hdc = BeginPaint(hwnd, &ps);
      if (hbitmap_main == NULL){
            DrawMyPictureTheHardWay(hwnd, hdc);
          hbitmap_main = SaveClientAreaAsBitmap(hwnd);
      } else {
           DrawBitMap(hdc, hwnd, 0, 0, hbitmap_main);
      }
      EndPaint(hwnd,&ps);
  }
  
  void KillBitmap(void)
  {
      if (hbitmap_main != NULL){
         DeleteObject(hbitmap_main);
         hbitmap_main = NULL;
      }
  }
  
  Don't use PAINTSTRUCT.rcPaint which describes the area that Windows says
  needs repainting; just BitBlt the whole thing and Windows will clip it.
  No, it's not overkill - it's fast!
  
  Finally, two routines that you can put in your library for use with all
  your apps.
  
  
  /*-----------------------------------------------------------------*
   | save entire client area of window into a bitmap                 |
   *-----------------------------------------------------------------*/
  
  HBITMAP SaveClientAreaAsBitmap(HWND hwnd)
  {
  RECT rect;
  HDC hdc,hmemdc;
  HBITMAP hbitmap,old_hbitmap;
  
       hbitmap=NULL;
       hmemdc=NULL;
       hdc=NULL;
  
       //get source device context for the client area
       hdc=GetDC(hwnd);
       if(hdc==NULL) goto done;
  
       //get destination memory hdc compatible with client area
       hmemdc=CreateCompatibleDC(hdc);
       if(hmemdc==NULL) goto done;
  
       //create compatible bitmap for client area
       GetClientRect(hwnd,&rect);      //.top & .left are both 0
       hbitmap=CreateCompatibleBitmap(hdc,rect.right,rect.bottom);
       if(hbitmap==NULL) goto done;
  
       //select client area bitmap into device context so we can write on it
       old_hbitmap=SelectObject(hmemdc,hbitmap);
  
       //and copy it to the new hmemdc
       BitBlt( hmemdc,0,0,             //destination (x,y)
            rect.right,rect.bottom, //width, height
            hdc,0,0,                //source (x,y)
            SRCCOPY);
  
       //all done
       SelectObject(hmemdc,old_hbitmap);
  
  done:   if(hdc   !=NULL) ReleaseDC(hwnd,hdc);
       if(hmemdc!=NULL) DeleteDC(hmemdc);
  
       return(hbitmap);
  }
  
  
  /*----------------------------------------------------------------*
   | Draw a bitmap into the current device context.                 |
   | This is essentially the same as Petzold's code.                |
   *----------------------------------------------------------------*/
  void DrawBitMap(HDC hdc,int xleft,int ytop,HBITMAP hbitmap)
  {
  RECT    rect;
  HDC     hmemdc;
  BITMAP  bm;
  POINT   point;
  HBITMAP old_hbitmap;
  
       hmemdc=NULL;
       old_hbitmap=NULL;
  
       //create memory device context & select bitmap
       hmemdc=CreateCompatibleDC(hdc);
       if(hmemdc==NULL) goto done;
       old_hbitmap=SelectObject(hmemdc,hbitmap);
  
       SetMapMode(hmemdc,GetMapMode(hdc));     //same as for hdc
  
       //get bitmap dimensions & convert to logical
       GetObject(hbitmap,sizeof(bm),&bm);
       point.x=bm.bmWidth;
       point.y=bm.bmHeight;
       DPtoLP(hdc,&point,1);
  
       BitBlt(hdc,xleft,ytop,                  //destination
            point.x,point.y,                //width, height
            hmemdc,0,0,                     //source
            SRCCOPY);
  
  done:   if(old_hbitmap!=NULL) SelectObject(hmemdc,old_hbitmap);
       if(hmemdc!=NULL) DeleteDC(hmemdc);
       return;
  }
  
  
...........................................................................

5.5.6.  Using CMY colors instead of RGB
---------------------------------------
  To use CMY colors with the Windows GDI instead of RGB, you can define the
  following macro to supplement the standard RGB one:
     #define  CMY(c, m, y)     RGB(255 - c, 255 - m, 255 - y)
  
...........................................................................

5.5.7.  Using only solid colors
-------------------------------
  If you want to use only solid colors (that is, get the nearest solid color
  to the one you specified), use the GetNearestColor()  function call to map
  an RGB value to a solid cor available in the current color palette.  Also,
  if you are using a palette, you can call the PALETTERGB(r,g,b) macro
  instead of the usual RGB(r,g,b) to map to the nearest color in the palette.
  
---------------------------------------------------------------------------

5.6.  Text and fonts
====================
  
...........................................................................

5.6.1.  Creating new fonts
--------------------------
  To create new TrueType fonts, you will need a commercial package such as
  Fontographer or FontMonger.  While FontMonger is a more limited tool, it is
  quite inexpensive and often sufficient for smaller projects.  Fontographer
  is a semi-professional tool with a higher price tag.  Neither tool allows
  you to construct your own hints for small point sizes.
  
...........................................................................

5.6.2.  Rotating fonts
----------------------
  First, you cannot rotate screen fonts.  Effectively this means that you can
  only rotate TrueType and Type 1 (ATM) fonts.  To do the rotation, you will
  have to create a new logical font with the correct escapement value.
  Select the font into the display context, do your displaying, select the
  original font back in, and delete the font object.
  
  Note that Type 1 fonts, which are considered device fonts, use a reversed
  direction for the rotation from TrueType fonts; you'll have to check the
  font type before doing the rotation, or otherwise you will have some fonts
  angled up and others down.
  
...........................................................................

5.6.3.  TrueType width calculation
----------------------------------
  The most accurate widths for TrueType fonts can be obtained using the
  following method (courtesy of Glenn Adams (glenn@wheat-chex.ai.mit.edu)):
  1.  Using EnumFonts() (or EnumFontFamilies()), obtain lpntm->ntmSizeEM.
      This value "specifies the size of the em square for the font, in the
      units for which the font was designed (notional units)."  Most fonts
      will have the value of 2048.
  2.  Create a font using the above size as the lfHeight.  This will create a
      font with metrics that coincide with the coordinate space used in the
      font's design, i.e., no scaling will occur in the logical coordinate
      space.
  3.  Use GetCharABCWidths() to get the ABC width structures for all the
      font's glyphs.  You can now compute the widths quite accurately, along
      with overhang and overhang.  Keep in mind that the horizontal escapement
      (i.e., the distance current point is advanced after rendering a glyph)
      is equal to A + B + C of the returned widths.  If A is negative, the
      glyph extends outside the EM square to the left; if C is negative, then
      the glyph extends outside the EM square to the right.
  One last point: the above widths will not take into account grid fitting,
  which will occur in actual display.  But, if you set the mapping mode to
  MM_ISOTROPIC and use a logical coordinate space which coresponds to the
  font design size, you can do all your layout computations using design
  sizes without regard to final viewport mapping.
  
---------------------------------------------------------------------------

5.7.  Miscellaneous
END--cut here--cut here