Chapter 8  Icons, Cursors, Bitmaps, and Strings
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Most Windows programs include a customized icon that Windows displays when
the program is minimized. Some programs (such as the Windows PAINTBRUSH
program) also use customized cursors to represent different operations of
the program. Most Windows programs also use menus and dialog boxes.

Icons, cursors, menus, and dialog boxes are all examples of "resources."
Resources are data and are included in a program's .EXE file, but they do
not reside in a program's normal data segment. When Windows loads a program
into memory for execution, it usually leaves the resources on disk. Only
when Windows needs a particular resource does it load the resource into
memory. (You've probably noticed dynamic loading of resources when working
with Windows programs. When you invoke a program's dialog box for the first
time, Windows usually accesses the disk to copy the dialog box resource from
the program's .EXE file into memory.)

Most resources are read-only data and are marked as discardable. When
Windows needs more memory, segments occupied by discardable resources can be
freed up. If the

resource is required again later, Windows reloads it from the .EXE file.
Just as multiple instances of the same program share the same code, multiple
instances also usually share resources. I'll be discussing these resources:

  þ   Icons

  þ   Cursors

  þ   Bitmaps

  þ   Character strings

  þ   User-defined resources

  þ   Menus

  þ   Keyboard accelerators

  þ   Dialog boxes

  þ   Fonts

The first five resources in the list are discussed in this chapter. Menus
and keyboard accelerators are covered in Chapter 9, dialog boxes in Chapter
10, and fonts in Chapter 14.

COMPILING RESOURCES

During program development, resources are defined in a "resource script,"
which is an ASCII text file with the extension .RC. The resource script can
contain ASCII representations of resources and can also reference other
files (either ASCII or binary files) that contain resources. The resource
compiler (RC.EXE) compiles the resource script into a binary form, adds the
resources to the end of the .EXE file that LINK generates, and creates a
"resource table" in the .EXE header.

You can use the resource compiler included in the Windows Software
Development Kit in one of three ways:

  þ   You can compile resources and add them to the linked .EXE file in one
      step by executing the command:

      RC filename

      where filename.RC is the name of the resource script (the .RC
      extension is assumed) and filename.EXE is the name of the linked .EXE
      file. You can also use:

      RC resource-name exe-name

      if the name of your .RC resource script and the name of your .EXE
      executable are different. (This is usually not the case.)

  þ   You can compile a .RC resource script into a binary compiled form with
      the extension .RES by executing:

      RC -r filename

      This uses the ASCII filename.RC file to create a binary file called
      filename.RES. You can then add the resources to the linked file by
      executing:

      RC filename.RES

      The .RES extension is required here to differentiate this command from
      the command shown earlier that both compiles the resource script and
      adds the resources to the .EXE file.

  þ   If your program has no resources, you should run rc.exe on the linked
      file:

      RC filename.EXE

      This flags the program as being "Windows 3 aware."

      This second method is the one most commonly used when the resource
      script contains resources. Although it requires that the RC.EXE
      resource compiler be run twice_once to compile the resource script and
      again to add the resources to the .EXE file--it actually results in a
      faster edit-make-run cycle when developing Windows programs. The
      reason is that compiling the resources generally takes much longer
      than adding them to the .EXE file. During program development, you
      will probably modify your C source code much more frequently than the
      resource script, so you have no need to recompile the resources each
      time.

      The procedure of compiling resources is reflected in a different make
      file. Up until now we have been using a make file that looks something
      like this:

      progname.exe : progname.obj progname.def
           link progname, /align:16, NUL, /nod slibcew libw, progname
           rc progname.exe

      progname.obj : progname.c
           cl -c -Gsw -Ow -W2 -Zp progname.c

      When we start using resources, we'll use an expanded make file that
      looks like this:

      progname.exe : progname.obj progname.def progname.res
           link progname, /align:16, NUL, /nod slibcew libw, progname
           rc progname.res

      progname.obj : progname.c [progname.h]
           cl -c -Gsw -Ow -W2 -Zp progname.c

      progname.res : progname.rc [progname.h] [and other files]
           rc -r progname.rc

      In the second and third sections I've indicated that a .H header file
      can be used in both the C source code and the resource script. This
      header file usually defines identifiers used by the program to
      reference resources. I've also indicated in the third section that the
      depen- dent file list possibly includes "other files." These are files
      referenced from within the resource script. Generally they are binary
      files that contain icons, cursors, or bitmaps.

      The RC.EXE resource compiler uses a preprocessor called RCPP.EXE. This
      preprocessor folds added or subtracted constants, recognizes /* and */
      as comment delimiters, and recognizes the C preprocessor directives
      #define, #undef, #ifdef, #ifndef, #include, #if, #elif, #else, and
      #endif. The #include directive works a little differently than in
      normal C programs. We'll examine this in greater detail in Chapter 10.

      In the first section of the make file, the .OBJ and .RES files are
      dependent files for the .EXE target. NMAKE checks the rest of the make
      file to determine if these dependent files must be updated. The second
      section compiles the C source code as usual. The third section
      compiles the .RC resource script into a binary .RES file.

      The first section is then executed if either the .OBJ, .DEF, or .RES
      file has changed since the last .EXE file was created. This section
      links the program as usual and runs .RC again to add the resources to
      the .EXE file. If you change only the .RC resource script file, you
      still need to relink to produce a new .EXE file without the previous
      resources. The resource compiler cannot remove old resources from a
      .EXE file when adding new ones.


ICONS AND CURSORS

Let's begin by looking at a sample program that uses two resources--an icon
and a cursor. RESOURC1, shown in Figure 8-1, displays a customized icon when
the program is minimized and uses a customized cursor when the mouse is in
RESOURC1's client area. RESOURC1 also draws its icon in several rows and
columns within the client area.

 RESOURC1.MAK

#------------------------
# RESOURC1.MAK make file
#------------------------

resourc1.exe : resourc1.obj resourc1.def resourc1.res
     link resourc1, /align:16, NUL, /nod slibcew libw, resourc1
     rc resourc1.res

resourc1.obj : resourc1.c
     cl -c -Gsw -Ow -W2 -Zp resourc1.c

resourc1.res : resourc1.rc resourc1.ico resourc1.cur
     rc -r resourc1.rc

 RESOURC1.C

/*-----------------------------------------------------------
   RESOURC1.C -- Icon and Cursor Demonstration Program No. 1
                 (c) Charles Petzold, 1990
  -----------------------------------------------------------*/

#include 

long FAR PASCAL WndProc  (HWND, WORD, WORD, LONG) ;

char   szAppName [] = "Resourc1" ;
HANDLE hInst ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor       = LoadCursor (hInstance, szAppName) ;
          wndclass.hbrBackground = COLOR_WINDOW + 1 ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hInst = hInstance ;

     hwnd = CreateWindow (szAppName, "Icon and Cursor Demo",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;
     while (GetMessage (&msg, NULL, 0, 0))
          {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
          }
     return msg.wParam ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HICON hIcon ;
     static short cxIcon, cyIcon, cxClient, cyClient ;
     HDC          hdc ;
     RECT         rect ;
     PAINTSTRUCT  ps ;
     short        x, y ;

     switch (message)
          {
          case WM_CREATE :
               hIcon = LoadIcon (hInst, szAppName) ;
               cxIcon = GetSystemMetrics (SM_CXICON) ;
               cyIcon = GetSystemMetrics (SM_CYICON) ;
               return 0 ;

          case WM_SIZE :
               cxClient = LOWORD (lParam) ;
               cyClient = HIWORD (lParam) ;
               return 0 ;

          case WM_PAINT :
               hdc = BeginPaint (hwnd, &ps) ;

               for (y = cyIcon ; y < cyClient ; y += 2 * cyIcon)
                    for (x = cxIcon ; x < cxClient ; x += 2 * cxIcon)
                         DrawIcon (hdc, x, y, hIcon) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY :
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

 RESOURC1.RC

/*-----------------------------
   RESOURC1.RC resource script
  -----------------------------*/

resourc1  ICON    resourc1.ico
resourc1  CURSOR  resourc1.cur

 RESOURC1.ICO  -- Please refer to the book.

 RESOURC1.CUR  -- Please refer to the book.

 RESOURC1.DEF

;-------------------------------------
; RESOURC1.DEF module definition file
;-------------------------------------

NAME           RESOURC1
DESCRIPTION    'Icon and Cursor Demo Program No. 1 (c) Charles Petzold,
1990'
EXETYPE        WINDOWS
STUB           'WINSTUB.EXE'
CODE           PRELOAD MOVEABLE DISCARDABLE
DATA           PRELOAD MOVEABLE MULTIPLE
HEAPSIZE       1024
STACKSIZE      8192
EXPORTS        WndProc

Both the icon and the cursor were created using the SDKPAINT program
supplied with the Windows Software Development Kit. They are shown in Figure
8-1 against a light gray background. SDKPAINT is a Windows application, and
it requires a mouse. Icons are saved from SDKPAINT with a .ICO extension;
cursors have a .CUR extension. These files are referred to in the
RESOURC1.RC resource script.

The SDKPAINT Tool

SDKPAINT is one of the most important development tools in the Windows
Software Development Kit. The program allows you to create bitmaps, icons,
and cursors for use in your Windows programs. Icons and cursors are both
variations of bitmaps, so it will be helpful to examine bitmaps first.

A bitmap is an array of bits where one or more bits corresponds to each
display pixel. In a monochrome bitmap, one bit corresponds to one pixel. (In
the simplest case, a 1 bit represents white and a 0 bit represents black.
However, bitmaps are often used in logical operations rather than merely to
create simple drawings.) In a color bitmap, multiple bits correspond to each
pixel to represent color. SDKPAINT supports the creation of monochrome
bitmaps and 16-color bitmaps. In a 16-color bitmap, 4 bits are required for
each pixel.

A bitmap may have any number of rows and columns. (However, the bitmaps you
create in SDKPAINT are limited to 72 rows and 72 columns. You can create
larger bitmaps in PAINTBRUSH.) Bitmaps are stored in files with a .BMP
extension. (I'll discuss the format of the bitmap file in Chapter 13.)

You can also create icons and cursors in SDKPAINT. Icons and cursors are
very similar, and they are both variations of bitmaps.

Windows displays icons and cursors on the screen in a pixel size that
depends on the resolution of the video display. This ensures that the icons
and cursors are neither too large nor too small. A program can obtain these
pixel dimensions using the GetSystemMetrics function with parameters of
SM_CXICON, SM_CYICON, SM_CXCURSOR, and SM_CYCURSOR. On most video displays,
the dimensions of icons and cursors are identical. To keep it simple in the
following discussion, I'll refer only to icons, but keep in mind that
everything I say applies to cursors also.

On an IBM Color Graphics Adapter (CGA), the width of an icon is 32 pixels
and the height is 16 pixels. On an Enhanced Graphics Adapter (EGA), Video
Graphics Array (VGA), and the IBM 8514/A video adapter, the icons are 32
pixels wide and 32 pixels high. For higher-resolution adapters, icons could
be displayed as 64 pixels by 64 pixels.

Each .ICO file can contain multiple icon images, each one designed for
particular resolutions and color capabilities of the various video adapters
on which your Windows program can run. SDKPAINT supports four different
image formats. When you create a new icon file (by selecting New from
SDKPAINT's File menu), you select one of these four formats. After creating
an icon in this format, you can then select another of the four formats from
the New option on the Image menu. These four formats are:

  þ   32 pixels by 16 pixels with 2 colors (monochrome)

  þ   32 pixels by 32 pixels with 2 colors (monochrome)

  þ   32 pixels by 32 pixels with 8 colors

  þ   32 pixels by 32 pixels with 16 colors

The first format is for the CGA, and the second is for other video adapters
(EGA, VGA, and 8514/A) running in a monochrome mode. The third and fourth
are for non-CGA adapters running in color modes. The 8-color format is of
limited use: SDKPAINT actually uses a 16-color format internally and when
saving the image to the file, but allows you to color it with only 8 colors.
The EGA, VGA, and 8514/A are all capable of 16 colors.

You don't need to create icon images in all four formats. When a program
contains an icon resource, Windows will choose the format that most closely
approximates the size and color capabilities appropriate to the video
adapter. For example, if you create only 32-by-32-pixel icons and your
program is run on a CGA, Windows will display the icon using every other row
of pixels, effectively compressing the height of the icon.

If you create only a 32-by-32 icon with 16 colors, use color sparingly
because the colors can be approximated only with gray shades (or converted
to black or white) when running with a monochrome display. All the icons and
cursors in the programs in this chapter were created in the 32-by-32
monochrome format.

When you create an icon image in one of the four formats, SDKPAINT actually
stores it as two bitmaps--a monochrome bitmap "mask" and a monochrome or
color bitmap image. Icons are always rectangular, but this mask allows the
icon to appear to be nonrectangular. That is, part of the icon allows the
background against which the icon is displayed to be visible. The icon can
also contain areas that invert the background color.

These two options are indicated in SDKPAINT by radio buttons labeled
"Screen" and "Inverse." After selecting "Screen," anything you draw in the
icon will be transparent, and after selecting "Inverse," anything you draw
in the icon will invert the background. You can select different background
colors to see how this looks. The icons and cursor in Figure 8-1 are shown
against a light gray background. The light gray areas were colored using the
"Screen" option, and the dark gray areas were colored using the "Inverse"
option.

For a monochrome icon, the following table shows how SDKPAINT constructs the
two bitmaps that describe the icon:

Color:         Black  White  Screen  Inverse Screen
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Mask Bitmap:   0      0      1       1
Image Bitmap:  0      1      0       1

When displaying the icon, Windows first uses a bitwise AND operation of the
display and the first bitmap. The display pixels corresponding to 0 bits
from the first bitmap all become 0's, which are black. The display pixels
corresponding to 1 bit remain the same. This is shown in the following logic
table.

          Display Pixel
Mask Bit  0              1
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
0         0              0
1         0              1

Next, Windows performs a bitwise exclusive OR operation of the image bitmap
and the display. A 0 in the second bitmap leaves the display pixel the same;
a 1 in the second bitmap inverts the display pixel. Here's the logic table:

           Display Pixel
Image Bit  0              1
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
0          0              1
1          1              0

Using C notation for the operations, the display is altered by the following
formula:

  Display = (Display & Mask) ^ Image

For a 16-color icon, the mask bitmap is still monochrome and constructed as
shown above. The image bitmap contains 4 bits per pixel to represent 16
colors. All four bits are set to 1 for areas of the icon that invert the
background.

Earlier I said that when talking about bitmaps, 0 does not necessarily mean
black, and 1 does not necessarily mean white. As you can see here, it
depends on how Windows uses the bitmaps. (I'll discuss this more in Chapter
13.)

In RESOURC1, I've defined the window class to make the background of the
client area be COLOR_WINDOW. You may want to bring up the Windows Control
Panel program and change the window color to see how the icon and cursor
invert colors.


Getting a Handle on Icons

A resource script references the icon file with a statement that looks like
this:

myicon ICON iconfile.ico

where ICONFILE.ICO is the name of the icon file. This statement assigns the
name  "myicon" to the icon. In your C program, you use the LoadIcon function
to obtain a handle to the icon. LoadIcon requires two parameters. The first
is the instance handle of your program, generally called hInstance in
WinMain. Windows needs this handle to determine which .EXE file contains the
icon resource. The second parameter is the icon name from the resource
script, in the form of a pointer to a null-terminated string. LoadIcon
returns a value of type HICON, which is defined in WINDOWS.H.

This diagram shows the relationship between the icon name in the resource
script and the LoadIcon statement in your C program:

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Resource script:      myicon ICON iconfile.ico
Program source:       hIcon = LoadIcon (hInstance, "myicon") ;

Don't worry about uppercase and lowercase here. The resource compiler
converts the name in the resource script file to uppercase and inserts the
name in the resource table of the program's .EXE file header. The first time
you call LoadIcon, Windows converts the string from the second parameter to
uppercase and searches the resource table of the .EXE file for a matching
name.

You can speed up this search by using a number (an unsigned integer) instead
of a name. This number is called an ID number for the icon. Here's how it's
done:

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Resource script:  125 ICON iconfile.ico
Program source:   hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (125)) ;

MAKEINTRESOURCE ("make an integer into a resource string") is a macro
defined in WINDOWS.H that converts a number into a far pointer. The offset
address is set to the number, and the segment address is set to 0. Here's
how MAKINTRESOURCE is defined in WINDOWS.H:

#define MAKEINTRESOURCE(i) (LPSTR)((DWORD)((WORD)(i)))

Windows knows that the second parameter is a number rather than a pointer to
a character string because the segment address is 0.

Sample programs presented earlier in this book use predefined icons:

LoadIcon (NULL, IDI_APPLICATION) ;

Windows knows that this is a predefined icon because the hInstance parameter
is set to NULL. IDI_APPLICATION happens also to be defined in WINDOWS.H in
terms of MAKEINTRESOURCE:

#define IDI_APPLICATION MAKEINTRESOURCE(32512)

The predefined icons and cursors are part of the display driver file.

You can also reference the icon name using a third method that combines the
string method and the number method:

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Resource script:       125 ICON iconfile.ico
Program source:        hIcon = LoadIcon (hInstance, "#125") ;

Windows recognizes the initial # character as prefacing a number in ASCII
form.

How about a fourth method? This one uses a macro definition in a header file
that is included (using the #include directive) in both the resource script
and your program:

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Header file:      #define myicon 125
Resource script:  myicon ICON iconfile.ico
Program source:   hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (myicon)) ;

Be careful when you use this method! Although case does not matter when the
icon name is a character string, case does make a difference for identifiers
that are generated from #define statements.

Using ID numbers rather than names for icons reduces the .EXE file size and
speeds up the LoadIcon call. Moreover, if your program uses many icons,
you'll find it easier to store the ID numbers in an array.


Using Icons in Your Program

Icons have only a couple of purposes. Most Windows programs use an icon only
for displaying the program in the icon area. This is accomplished when
defining the window class:

wndclass.hIcon = LoadIcon (hInstance, "myicon") ;

If you later want to change the program's icon, you can do so using
SetClassWord. Let's assume you had a second icon in your resource script:

anothericon ICON iconfil2.ico

You can substitute this icon for "myicon" with the statement:

SetClassWord (hwnd, GCW_HICON, LoadIcon (hInstance,"anothericon")) ;

If you save the icon handle from a LoadIcon call, you can also draw the icon
on the client area of your window:

DrawIcon (hdc, x, y, hIcon) ;

Windows itself uses the DrawIcon function when displaying your program's
icon in the icon area. Windows obtains the handle to the icon from the
window class structure. You can obtain the handle in the same way:

DrawIcon (hdc, x, y, GetClassWord (hwnd, GGW_HICON)) ;

The RESOURC1 sample program uses the same icon for the window class and for
displaying in its client area. In the resource script the icon is given the
same name as the program:

resourc1  ICON  resourc1.ico

Because the character string "Resourc1" is stored in the array szAppName and
is already used in the program for the window class name, the LoadIcon call
is simply:

LoadIcon (hInstance, szAppName) ;

You'll notice that LoadIcon is called twice in RESOURC1 for the same icon,
once when defining the window class in WinMain and again when obtaining a
handle to the icon while processing the WM_CREATE message in WndProc.
Calling LoadIcon twice presents no problem: Both calls return the same
handle. Windows actually loads the icon only once from the .EXE file and
then uses it for all instances of the RESOURC1 program.


Using Alternate Cursors

The statements that you use to specify a cursor in your resource script and
to obtain a handle to a cursor in your program are very similar to the icon
statements shown above:

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Resource script:    mycursor CURSOR cursfile.cur
Program source:     hCursor = LoadCursor (hInstance, "mycursor") ;

The other methods shown for icons (using ID numbers and MAKEINTRESOURCE)
work with cursors also. WINDOWS.H includes a typedef definition for HCURSOR
that you can use for storing the cursor handle. (Both HICON and HCURSOR are
defined as HANDLE.)

You can use the cursor handle obtained from LoadCursor when setting the
hCursor member of the window class structure:

wndclass.hCursor = LoadCursor (hInstance, "mycursor") ;

This causes the mouse cursor to be displayed as your customized cursor when
the mouse is within the client area of your window.

If you use child windows, you may want the cursor to appear differently,
depending on the child window below the cursor. If your program defines the
window class for these child windows, you can use different cursors for each
class by appropriately setting the hCursor field in each window class. And
if you use predefined child window controls, you can alter the hCursor field
of the window class using:

SetClassWord (hwndChild, GCW_HCURSOR,
               LoadCursor (hInstance, "childcursor") ;

If you separate your client area into smaller logical areas without using
child windows, you can use SetCursor to change the mouse cursor:

SetCursor (hCursor) ;

You should call SetCursor during processing of the WM_MOUSEMOVE message.
Otherwise, Windows uses the cursor specified in the window class to redraw
the cursor when it is moved.

RESOURC1 uses the name of the program for the name of the cursor:

resourc1  CURSOR  resourc1.cur

When RESOURC1.C defines the window class, this szAppName variable is used
for LoadCursor:

wndclass.hCursor = LoadCursor (hInstance, szAppName) ;



RESOURCES AND MEMORY

The LoadIcon and LoadCursor functions certainly sound as if they load the
icon or cursor from the .EXE file into memory. They do not. Windows doesn't
load the icon or cursor until it needs the object for drawing. During
loading, Windows may alter the object to fit the dimensions and color
capabilities of the display.

Icons and cursors (as well as all other resources except bitmaps) are
"owned" by the program. Multiple instances of the same program share the
same cursors and icons loaded into memory. When the last instance
terminates, Windows frees up the memory occupied by the resource. And for
most resources, Windows can discard the resource from memory to generate
free space and then load it back into memory when needed.

You can override some of these characteristics, however. For all resources
except the keyboard accelerators (covered in Chapter 9), you can specify
"load" and "memory" options in the resource script file. These options are
similar to the module definition file options for code and data segments
discussed in Chapter 7. In the resource script, the load and memory options
follow the resource type. This is the generalized form of the ICON statement
in a resource script file:

iconID ICON [load-option] [memory-option] iconfile.ico

The load option can be either PRELOAD or LOADONCALL. A resource defined as
PRELOAD will be loaded into memory when the program is loaded. LOADONCALL
means that the resource will not be loaded until Windows needs it.
LOADONCALL is the default for all resources. You will probably want to use
PRELOAD only when you know that your program will need the resource
immediately after beginning to execute.

The memory options are FIXED, MOVEABLE, and DISCARDABLE. DISCARDABLE
resources must also be MOVEABLE. For the resources discussed in this
chapter, the icon, cursor, and character string resources have default
memory options of MOVEABLE and DISCARDABLE. The bitmap and user-defined
resources are MOVEABLE only. Why the difference? Icon, cursor, and character
string resources are read-only, so Windows can safely discard them from
memory. Windows allows bitmaps and user-defined resources to be modified
from within a program--and modified resources cannot be discarded.

Bitmaps: Pictures in Pixels

We've already talked about the use of bitmaps in icons and cursors. Windows
also includes a resource type called BITMAP.

Bitmaps are used for two major purposes. The first is to draw pictures on
the display. For instance, the Windows display driver files contain lots of
tiny bitmaps used for drawing the arrows in scroll bars, the check mark in
pull-down menus, the system menu box, the size box, check boxes, and radio
buttons. Programs such as PAINTBRUSH use bitmaps to display a graphics menu.

The second major use of bitmaps is to create brushes. Brushes, you'll
recall, are patterns of pixels that Windows uses to fill an area of the
display. (Chapter 9 discusses a third and less common use of bitmaps, as
selection items in menus.)


Using Bitmaps and Brushes

The RESOURC2 program, shown in Figure 8-2, is an upgraded version of
RESOURC1 that includes a monochrome bitmap resource used to create a brush
for the background of the client area. The bitmap was created in SDKPAINT
with dimensions of 8 by 8, which is the minimum size for a brush.

 RESOURC2.MAK

#------------------------
# RESOURC2.MAK make file
#------------------------

resourc2.exe : resourc2.obj resourc2.def resourc2.res
     link resourc2, /align:16, NUL, /nod slibcew libw, resourc2
     rc resourc2.res

resourc2.obj : resourc2.c
     cl -c -Gsw -Ow -W2 -Zp resourc2.c

resourc2.res : resourc2.rc resourc2.ico resourc2.cur resourc2.bmp
     rc -r resourc2.rc

 RESOURC2.C

/*-----------------------------------------------------------
   RESOURC2.C -- Icon and Cursor Demonstration Program No. 2
                 (c) Charles Petzold, 1990
  -----------------------------------------------------------*/


#include 

long FAR PASCAL WndProc  (HWND, WORD, WORD, LONG) ;

char   szAppName[] = "Resourc2" ;
HANDLE hInst ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HBITMAP  hBitmap ;
     HBRUSH   hBrush ;
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     hBitmap = LoadBitmap (hInstance, szAppName) ;
     hBrush = CreatePatternBrush (hBitmap) ;

     if (!hPrevInstance)
          {
          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor       = LoadCursor (hInstance, szAppName) ;
          wndclass.hbrBackground = hBrush ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }

     hInst = hInstance ;

     hwnd = CreateWindow (szAppName, "Icon and Cursor Demo",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, nCmdShow) ;
     UpdateWindow (hwnd) ;
     while (GetMessage (&msg, NULL, 0, 0))
          {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
          }

     DeleteObject (hBrush) ;       // clean up
     DeleteObject (hBitmap) ;

     return msg.wParam ;
     }

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HICON hIcon ;
     static short cxIcon, cyIcon, cxClient, cyClient ;
     HDC          hdc ;
     PAINTSTRUCT  ps ;
     RECT         rect ;
     short        x, y ;

     switch (message)
          {
          case WM_CREATE :
               hIcon = LoadIcon (hInst, szAppName) ;
               cxIcon = GetSystemMetrics (SM_CXICON) ;
               cyIcon = GetSystemMetrics (SM_CYICON) ;
               return 0 ;

          case WM_SIZE :
               cxClient = LOWORD (lParam) ;
               cyClient = HIWORD (lParam) ;
               return 0 ;

          case WM_PAINT :
               hdc = BeginPaint (hwnd, &ps) ;

               for (y = cyIcon ; y < cyClient ; y += 2 * cyIcon)
                    for (x = cxIcon ; x < cxClient ; x += 2 * cxIcon)
                         DrawIcon (hdc, x, y, hIcon) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;

          case WM_DESTROY :
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

 RESOURC2.RC

/*-----------------------------
   RESOURC2.RC resource script
  -----------------------------*/

resourc2  ICON    resourc2.ico
resourc2  CURSOR  resourc2.cur
resourc2  BITMAP  resourc2.bmp

 RESOURC2.ICO  -- Please refer to the book.

 RESOURC2.CUR  -- Please refer to the book.

 RESOURC2.BMP  -- Please refer to the book.

 RESOURC2.DEF

;-------------------------------------
; RESOURC2.DEF module definition file
;-------------------------------------

NAME           RESOURC2

DESCRIPTION    'Icon and Cursor Demo Program No. 2 (c) Charles Petzold,
1990'
EXETYPE        WINDOWS
STUB           'WINSTUB.EXE'
CODE           PRELOAD MOVEABLE DISCARDABLE
DATA           PRELOAD MOVEABLE MULTIPLE
HEAPSIZE       1024
STACKSIZE      8192
EXPORTS        WndProc

The bitmap resource is included in the resource script in the same format as
the icon and cursor:

resourc2 BITMAP resourc2.bmp

The LoadBitmap function used in WinMain is similar to the LoadIcon and
LoadCursor calls. It returns a handle to a bitmap:

hBitmap = LoadBitmap (hInstance, szAppName) ;

This handle is then used to create a pattern brush. The brush is based on
the bitmap:

hBrush = CreatePatternBrush (hBitmap) ;

When Windows fills an area of the display with this brush, the bitmap is
repeated horizontally and vertically every eight pixels. We want to use this
brush to color the background of the client area, which we accomplish when
defining the window class:

wndclass.hbrBackground = hBrush ;

The major difference between bitmaps and other resources is of practical
significance and can be simply stated: Bitmaps are GDI objects. They are not
shared among instances of your program, and they are not automatically
deleted from memory when your program terminates. Because bitmaps and
brushes are GDI objects, they must be deleted before the program terminates.
In RESOURC2 this is done at the end of WinMain:

DeleteObject (hBrush) ;
DeleteObject (hBitmap) ;



CHARACTER STRINGS

Having a resource for character strings may seem odd at first. Certainly we
haven't had any problem using regular old character strings defined as
variables right in our source code.

Character string resources are primarily for easing the translation of your
program to other languages. As you'll discover in the next two chapters,
menus and dialog boxes are also part of the resource script. If you use
character string resources rather than put strings directly into your source
code, then all text that your program uses will be in one file--the resource
script. If the text in this resource script is translated, all you need do
to create a foreign-language version of your program is relink the program
and add the translated resources to the .EXE file. This method is much safer
than messing around with your source code. (Of course, you could also choose
to define all your character strings as macros and store them in a header
file. This method also avoids altering source code during language
translations.)

A second reason for using character string resources is to reduce memory
space. This reason is less obvious--in fact, if you use character string
resources inefficiently, you might not reduce memory space at all. We'll
examine this problem after we get through the basics.

Using Character String Resources

The character string resources are defined in your resource script using the
keyword STRINGTABLE:

STRINGTABLE [load option] [memory option]
     {
          nID1, "character string 1"
          nID2, "character string 2"
[other string definitions]
     }

 The resource script can contain only one string table. LOADONCALL is the
default load option; MOVEABLE and DISCARDABLE are the default memory
options. Each string can be only one line long with a maximum of 255
characters. The strings cannot contain any C-style control characters except
for \t (tab). However, the strings can contain octal constants:

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Tab                               \011
Linefeed                          \012
Carriage return                   \015

These control characters are recognized by the DrawText and MessageBox
functions.

Your program can use the LoadString call to copy a string resource into a
buffer in the program's data segment:

LoadString (hInstance, nID, lpszBuffer, nMaxLength) ;

The nID parameter refers to the ID number that precedes each string in the
resource script; lpszBuffer is a far (or long) pointer to a character array
that receives the character string; and nMaxLength is the maximum number of
characters to transfer into the lpszBuffer. The string ID numbers that
precede each string are generally macro identifiers defined in a header
file. Many Windows programmers use the prefix IDS_ to denote an ID number
for a string. Sometimes a filename or other information must be embedded in
the string when the string is displayed. In this case you put C formatting
characters in the string and use it as a formatting string in sprintf or
wsprintf.


Using Strings with MessageBox

Let's look at an example of a program that uses three character strings to
display three error messages in a message box. A header file that we'll call
PROGRAM.H defines three identifiers for these messages:

#define IDS_FILENOTFOUND 1
#define IDS_FILETOOBIG   2
#define IDS_FILEREADONLY 3

The resource script looks like this:

#include "program.h"
[other resource script]
STRINGTABLE
     {
          IDS_FILENOTFOUND,   "File %s not found."
          IDS_FILETOOBIG,     "File %s too large to edit."
          IDS_FILEREADONLY,   "File %s is read-only."
     }

The C source code file also includes this header file and defines a function
to display a message box. (I'm assuming that szAppName is a global variable
that contains the program name.)

#include "program.h"
[other program lines]
OkMessage (HWND hwnd, WORD wErrorNumber, char *szFileName)
     {
     char szFormat [40] ;
     char szBuffer [60] ;

     LoadString (hInst, wErrorNumber, szFormat, 40) ;

     sprintf (szBuffer, szFormat, szFilename) ;

     return MessageBox (hwnd, szBuffer, szAppName,
                            MB_OK | MB_ICONEXCLAMATION) ;
     }

To display a message box containing the "file not found" message, the
program calls:

OkMessage (hwnd, IDS_FILENOTFOUND, szFileName) ;


Character Strings and Memory Space

Character string resources usually save memory space, but the amount of
space saved depends on how efficiently they're used. When the RC.EXE
resource compiler adds strings to the .EXE file, the strings are grouped
into different segments depending on the ID numbers of the strings. Each
segment contains a maximum of 16 strings. Strings with ID numbers from 0 to
15 are stored in one segment, from 16 to 31 are in another, and so forth.
Because of this grouping into segments, your .EXE file will be shorter if
you use consecutive numbers for your string IDs.

When you use LoadString to copy a string resource into memory, however,
Windows loads the entire segment (containing up to 16 strings) into memory
as a resource. Windows then also copies the content of the string specified
in the LoadString call to a buffer in your program's data segment. So when
string resources are loaded into memory, they initially occupy less memory
if you do not use consecutive numbers for the string IDs. Probably the worst
way to use string resources is to load all the strings into separate global
static arrays during program initialization in WinMain. If you do that, you
don't use any less space than if you had included the strings in your C
source code. In fact, you'll use more space, because you'll set the size of
these arrays somewhat larger than the actual string lengths. Here are some
general rules for using string resources:

  þ   Assign string ID numbers according to logical groupings. For instance,
      if five strings are involved in one section of your program and six
      strings are involved in another section, you might use ID numbers 0 to
      4 for the first group of strings and 16 to 21 for the second group.

  þ   Whenever possible, load the strings into automatic local variables
      within functions, thus freeing up the space when the function is
      exited. (The OkMessage function shown above uses this approach.)

  þ   Alternatively, reuse a single static array for loading strings.

Here's an example of the last approach. Let's assume that your program never
requires more than one string at a time. You can define a function that
loads the string and returns a pointer to a static variable containing the
string:

char *String (WORD wID)
     {
     static szBuffer [256] ;

     LoadString (hInst, wID, szBuffer, 255) ;
     return buffer ;
     }

If you want to use DrawText to display the string with ID number 45, you use
this statement:

DrawText (hdc, String (45), -1, &rect, DT_LEFT) ;

Each call to String destroys the contents of the static buffer. If you
require access to (for example) three strings at one time, you can modify
String as follows:

char *String (WORD wID, short n)
     {
     static szBuffer [3][256] ;

     LoadString (hInst, wID, szBuffer [n], 255) ;
     return szBuffer [n] ;
     }

When you call String now, you pass an ID number and a second parameter that
is either 0, 1, or 2.



USER-DEFINED RESOURCES

The "user-defined resource" is convenient for attaching miscellaneous data
to your .EXE file and obtaining access to that data within the program. The
data can be in any format you want. The Windows functions used to access
user-defined resources return a far pointer to the data when Windows loads
the data into memory. You can do whatever you want with that data. For
instance, suppose you have a file called PROGHELP.TXT that contains "help"
text for your program. This file needn't be a pure ASCII file: It can also
contain binary data, such as pointers that would aid your program in
referencing various sections of this file. Reference this file with a
statement in your resource script that looks like this:

helptext TEXT proghelp.txt

For helptext (the name of the resource) and TEXT (the type of the resource),
you can use any names you want. I've capitalized TEXT simply to make it look
like the ICON, CURSOR, and BITMAP statements. What we're doing here is
making up our own type of resource, called TEXT.

During program initialization (for example, during processing of the
WM_CREATE message), you can obtain a handle to this resource:

hResource = LoadResource (hInstance,
            FindResource (hInstance, "TEXT", "helptext")) ;

The variable hResource is defined with type HANDLE. Despite its name,
LoadResource does not actually load the resource into memory just yet. The
LoadResource and FindResource functions used together like this are
essentially equivalent to the LoadIcon and LoadCursor functions. In fact,
LoadIcon and LoadCursor use the LoadResource and FindResource functions.

You can use numbers rather than names for the resource name and resource
type. The numbers can be converted to far pointers in the FindResource call
using MakeIntResource. The numbers used for the resource type must be
greater than 255. (Windows uses numbers between 1 and 9 when calling
FindResource for existing resource types.)

When you need access to the text, call LockResource:

lpHelpText = LockResource (hResource) ;

LockResource loads the resource into memory (if it has not already been
loaded), locks it using the GlobalLock function, and returns a far pointer
to it. When you are finished accessing the memory, unlock the segment:

UnlockResource (hResource) ;

This allows Windows to move the segment in memory. When you're finished with
the resource, you can free it from memory:

FreeResource (hResource) ;

The resource will be freed when your program terminates, even if you don't
call FreeResource.

Normally, user-defined resources are not discardable unless you include the
DISCARDABLE keyword before the filename in the resource script. But if you
use the pointer returned from LockResource to alter as well as read the
data, don't make the resource DISCARDABLE. Note also that the same resource
is shared among all instances of the program. If each instance needs its own
copy of the resource, you should make the resource discardable, use
LockResource to obtain a pointer to the resource, use GlobalAlloc to obtain
a global memory block of the same size, use GlobalLock to lock that block,
and then copy the contents of the resource into the global memory block.

Let's look at a sample program that uses three resources--an icon, a string
table, and a user-defined resource. The POEPOEM program, shown in Figure
8-3, displays the text of

Edgar Allan Poe's "Annabel Lee" in its client area. The user-defined
resource is the file POEPOEM.ASC, which contains the text of the poem. The
text file is terminated with a backslash (\).

 POEPOEM.MAK

#-----------------------
# POEPOEM.MAK make file
#-----------------------

poepoem.exe : poepoem.obj poepoem.def poepoem.res
     link poepoem, /align:16, NUL, /nod slibcew libw, poepoem
     rc poepoem.res

poepoem.obj : poepoem.c poepoem.h
     cl -c -Gsw -Ow -W2 -Zp poepoem.c

poepoem.res : poepoem.rc poepoem.ico poepoem.asc poepoem.h
     rc -r poepoem.rc

 POEPOEM.C

/*-------------------------------------------------
   POEPOEM.C -- Demonstrates User-Defined Resource
                (c) Charles Petzold, 1990
  -------------------------------------------------*/

#include 
#include "poepoem.h"

long FAR PASCAL WndProc  (HWND, WORD, WORD, LONG) ;

char   szAppName [10] ;
char   szCaption [35] ;
HANDLE hInst ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
     {
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;

     if (!hPrevInstance)
          {
          LoadString (hInstance, IDS_APPNAME, szAppName, sizeof szAppName) ;
          LoadString (hInstance, IDS_CAPTION, szCaption, sizeof szCaption) ;


          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
          wndclass.lpfnWndProc   = WndProc ;
          wndclass.cbClsExtra    = 0 ;
          wndclass.cbWndExtra    = 0 ;
          wndclass.hInstance     = hInstance ;
          wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
          wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
          wndclass.lpszMenuName  = NULL ;
          wndclass.lpszClassName = szAppName ;

          RegisterClass (&wndclass) ;
          }
     else
          {
          GetInstanceData (hPrevInstance, szAppName, sizeof szAppName) ;
          GetInstanceData (hPrevInstance, szCaption, sizeof szCaption) ;
          }

     hInst = hInstance ;

     hwnd = CreateWindow (szAppName, szCaption,
                          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

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

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

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
     {
     static HANDLE hResource ;
     static HWND   hScroll ;
     static short  nPosition, cxChar, cyChar, cyClient, nNumLines, xScroll ;
     char          szPoemRes [15] ;
     char far      *lpText ;

     HDC           hdc ;
     PAINTSTRUCT   ps ;
     RECT          rect ;
     TEXTMETRIC    tm ;

     switch (message)
          {
          case WM_CREATE :
               hdc = GetDC (hwnd) ;
               GetTextMetrics (hdc, &tm) ;
               cxChar = tm.tmAveCharWidth ;
               cyChar = tm.tmHeight + tm.tmExternalLeading ;
               ReleaseDC (hwnd, hdc) ;

               xScroll = GetSystemMetrics (SM_CXVSCROLL) ;

               hScroll = CreateWindow ("scrollbar", NULL,
                              WS_CHILD | WS_VISIBLE | SBS_VERT,
                              0, 0, 0, 0,
                              hwnd, 1, hInst, NULL) ;

               LoadString (hInst, IDS_POEMRES, szPoemRes, sizeof szPoemRes)
;
               hResource = LoadResource (hInst,
                           FindResource (hInst, szPoemRes, "TEXT")) ;

               lpText = LockResource (hResource) ;

               nNumLines = 0 ;

               while (*lpText != '\\' && *lpText != '\0')
                    {
                    if (*lpText == '\n')
                         nNumLines ++ ;
                    lpText = AnsiNext (lpText) ;
                    }
               *lpText = '\0' ;

               GlobalUnlock (hResource) ;

               SetScrollRange (hScroll, SB_CTL, 0, nNumLines, FALSE) ;
               SetScrollPos   (hScroll, SB_CTL, 0, FALSE) ;
               return 0 ;

          case WM_SIZE :
               MoveWindow (hScroll, LOWORD (lParam) - xScroll, 0,
                    xScroll, cyClient = HIWORD (lParam), TRUE) ;
               SetFocus (hwnd) ;
               return 0 ;

          case WM_SETFOCUS :
               SetFocus (hScroll) ;
               return 0 ;
          case WM_VSCROLL :
               switch (wParam)
                    {
                    case SB_TOP :
                         nPosition = 0 ;
                         break ;
                    case SB_BOTTOM :
                         nPosition = nNumLines ;
                         break ;
                    case SB_LINEUP :
                         nPosition -= 1 ;
                         break ;
                    case SB_LINEDOWN :
                         nPosition += 1 ;
                         break ;
                    case SB_PAGEUP :
                         nPosition -= cyClient / cyChar ;
                         break ;
                    case SB_PAGEDOWN :
                         nPosition += cyClient / cyChar ;
                         break ;
                    case SB_THUMBPOSITION :
                         nPosition = LOWORD (lParam) ;
                         break ;
                    }
               nPosition = max (0, min (nPosition, nNumLines)) ;

               if (nPosition != GetScrollPos (hScroll, SB_CTL))
                    {
                    SetScrollPos (hScroll, SB_CTL, nPosition, TRUE) ;
                    InvalidateRect (hwnd, NULL, TRUE) ;
                    }
               return 0 ;

          case WM_PAINT :
               hdc = BeginPaint (hwnd, &ps) ;

               lpText = LockResource (hResource) ;

               GetClientRect (hwnd, &rect) ;
               rect.left += cxChar ;
               rect.top  += cyChar * (1 - nPosition) ;
               DrawText (hdc, lpText, -1, &rect, DT_EXTERNALLEADING) ;

               GlobalUnlock (hResource) ;

               EndPaint (hwnd, &ps) ;
               return 0 ;
          case WM_DESTROY :
               FreeResource (hResource) ;
               PostQuitMessage (0) ;
               return 0 ;
          }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
     }

 POEPOEM.RC

/*----------------------------
   POEPOEM.RC resource script
  ----------------------------*/

#include "poepoem.h"

poepoem     ICON  poepoem.ico
AnnabelLee  TEXT  poepoem.asc

STRINGTABLE
     {
     IDS_APPNAME, "poepoem"
     IDS_CAPTION, """Annabel Lee"" by Edgar Allen Poe"
     IDS_POEMRES, "AnnabelLee"
     }

 POEPOEM.ICO  -- Please refer to the book.

 POEPOEM.H

/*-----------------------
   POEPOEM.H header file
  -----------------------*/

#define IDS_APPNAME 0
#define IDS_CAPTION 1
#define IDS_POEMRES 2

 POEPOEM.ASC

It was many and many a year ago,
   In a kingdom by the sea,
That a maiden there lived whom you may know
   By the name of Annabel Lee;
And this maiden she lived with no other thought
   Than to love and be loved by me.

I was a child and she was a child
   In this kingdom by the sea,
But we loved with a love that was more than love --
   I and my Annabel Lee --
With a love that the wing\ged seraphs of Heaven
   Coveted her and me.

And this was the reason that, long ago,
   In this kingdom by the sea,
A wind blew out of a cloud, chilling
   My beautiful Annabel Lee;
So that her highborn kinsmen came
   And bore her away from me,
To shut her up in a sepulchre
   In this kingdom by the sea.

The angels, not half so happy in Heaven,
   Went envying her and me --
Yes! that was the reason (as all men know,
   In this kingdom by the sea)
That the wind came out of the cloud by night,
   Chilling and killing my Annabel Lee.
But our love it was stronger by far than the love
   Of those who were older than we --
   Of many far wiser than we --
And neither the angels in Heaven above
   Nor the demons down under the sea
Can ever dissever my soul from the soul
   Of the beautiful Annabel Lee:

For the moon never beams, without bringing me dreams
   Of the beautiful Annabel Lee;
And the stars never rise, but I feel the bright eyes
   Of the beautiful Annabel Lee:
And so, all the night-tide, I lie down by the side
Of my darling -- my darling -- my life and my bride,
   In her sepulchre there by the sea --
   In her tomb by the sounding sea.

                                       [May 1849]
\

 POEPOEM.DEF

;------------------------------------
; POEPOEM.DEF module definition file
;------------------------------------

NAME           POEPOEM

DESCRIPTION    'Demo of User-Defined Resource (c) Charles Petzold, 1990'
EXETYPE        WINDOWS
STUB           'WINSTUB.EXE'
CODE           PRELOAD MOVEABLE DISCARDABLE
DATA           PRELOAD MOVEABLE MULTIPLE
HEAPSIZE       1024
STACKSIZE      8192
EXPORTS        WndProc

In the POEPOEM.RC resource script, the user-defined resource is given the
type TEXT and the name AnnabelLee:

AnnabelLee  TEXT  poepoem.asc

During WM_CREATE processing in WndProc, a handle to the resource is obtained
using FindResource and LoadResource. The resource is locked using
LockResource, and a small routine replaces the backslash (\) at the end of
the file with a 0. (This is for the benefit of the DrawText function used
later.) In most cases it's not a good idea to write on a user-defined
resource directly, because the same resource is shared among all instances
of the program. However, later instances of POEPOEM will not encounter
problems with the change we've made. The resource is then unlocked using
GlobalUnlock.

The resource is also locked and unlocked during processing of WM_PAINT to
write the text to the display using DrawText. Note the use of a child window
scroll bar control rather than a window scroll bar. The child window scroll
bar control has an automatic keyboard interface, so no WM_KEYDOWN processing
is required in POEPOEM.

POEPOEM also uses three character strings, the IDs of which are defined in
the POEPOEM.H header file. For the first instance of the program, the
IDS_APPNAME and IDS_CAPTION strings are loaded into global static variables
using LoadString:

LoadString (hInstance, IDS_APPNAME, szAppName, sizeof szAppName) ;
LoadString (hInstance, IDS_CAPTION, szCaption, sizeof szCaption) ;

However, for subsequent instances of POEPOEM, the strings are copied from
the previous instance:

GetInstanceData (hPrevInstance, szAppName, sizeof szAppName) ;
GetInstanceData (hPrevInstance, szCaption, sizeof szCaption) ;

GetInstanceData is faster than LoadString if the string resource has been
discarded from memory. The pointers (szAppName and szCaption) are near
pointers to static global variables. Windows uses these pointers in
combination with the data segment address of the previous instance and the
data segment address of the current instance to copy the contents of the
variables.

Now that we've defined all the character strings used in POEPOEM as
resources, we've made it easier for translators to convert the program into
a foreign-language version. Of course, they'd also have to translate the
text of "Annabel Lee"--which would, I suspect, be a somewhat more difficult
task.