 Microsoft Windows Device Driver Kit
 =============================================================================


 1. Documentation Error: Control and NEXTBAND Function

 Question:

 Page 278 of the "Microsoft Windows Software Development Kit Adaptation
 Guide" shows the following:

    Control(lpDevice, NEXTBAND, 0, lpBandRect): Return

 Is the third parameter really "0" for scaling devices? It seems that
 scaling factors information was being returned through this third
 parameter by Windows Version 1.00 printer drivers.

 Response:

 The documentation is incorrect: The third parameter for Control should
 be (LPPOINT)lpScalePt.


 2. How to Implement BitBlt() in Your Windows Device Driver

 There is a file in the Software Library named BITBLTRD.ARC that
 contains a discussion of how to implement the BitBlt() function in
 your Windows device driver.

 BITBLTRD.ARC can be found in the Software Library by searching on the
 filename, the Q number of this article, or S12170. BITBLTRD.ARC has
 been archived with the PKARC utility. You will need to unarchive it
 with the PKXARC utility. A copy of PKXARC can be found on the
 Microsoft OnLine Utilities Disk 2.


 3. Windows DDK: Writing a Mouse Driver

 Question:

 I have the following questions about mouse drivers for Windows:

 1. Does Windows expect relative or absolute coordinates?

 2. Does Windows use any defaults for the mouse? If yes, does the
    driver not set them (e.g. resolution)?

 3. The threshold speed is set in two places. In one place it is 2,2
    for x and y, and in the other it is 128,64. Which is the correct
    one?

 Response:

 The following are answers to your questions:

 1. Windows currently ignores the following fields in the MOUSEINFO
    structure:

       msRelative
       msRate
       msXRes
       msYRes

    The msRelative value is claimed to be nonzero if there is motion
    relative to the previous position of the mouse (i.e., just changed
    position). However, the relative devices seem to leave this value
    as zero and the absolute devices seem to make it nonzero.

    This feature is under review and will be considered for inclusion
    in a future release.

 2. Windows does not default to any fields. They are just ignored (see
    above).

 3. The threshold speed is defined in two places (through EQUs). Only
    one set of definitions is ever referenced, and that is in the
    MOUSEINFO structure. Therefore, we accelerate mouse motions that
    are greater than two pixels in each direction.

 Our documentation does not tell you the following:

 1. When the mouse interrupt routine passes data to the USER, the
    registers are defined as follows:

        ax = flags
           0001h =  mouse moved
           0002h =  left button pressed
           0004h =  left button released
           0008h =  right button pressed
           0010h =  right button released
           8000h =  absolute move
        bx = dX
        cx = dY
        dx = # of buttons, assumed to be two

 2. For absolute pointing devices, dX and dY are normalized to be
    0:65535 before they are passed to Windows.


 4. PRINTER.H File Missing from Windows/286 DDK

 Question:

 I have been studying the Windows/286 Version 2.10D Device Driver Kit
 (DDK) OEM adaptation guide in preparation for writing a Windows
 printer driver.

 However, in attempting to create the printer drivers (Epson, IBMColor)
 from the source files that have been provided (as suggested in the
 documentation), I have noted that a required file, PRINTER.H, has been
 omitted, and is not contained on any of the nine included disks. As a
 result, when attempting to create one of the sample printer drivers,
 many of the source files will not compile.

 Response:

 The PRINTER.H file was omitted from the disks. It is located in the
 Software Library as PRINTERH. PRINTERH can be found in the
 Software/Data Library by searching on the keyword PRINTERH, the Q
 number of this article, or S12323. PRINTERH was archived using the
 PKware file-compression utility.


 5. Beta: Must Hook INT 0Ah as INT 71h

 **********************************************************************
   CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL
   CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL
 **********************************************************************

 In Windows Version 3.00 protected mode (286p and 386p), you cannot
 hook Interrupt 0Ah (IRQ 2). Instead, you must hook it as Interrupt 71h
 (IRQ 9). This is because Windows does not reflect INT 71h from
 protected mode down to real mode.

 Hooking Interrupt 0Ah works in real mode because the AT BIOS handles
 INT 71h by issuing a software INT 0Ah [see Page 418 of the "MS-DOS
 Encyclopedia" (Microsoft Press, 1988)]. Thus, INT 71h is effectively
 transformed into INT 0Ah.

 This conversion preserves compatibility between XT-class machines and
 AT-class machines (XTs don't have master/slave 8259 PICs). However,
 because 8086-based machines cannot be run in protected mode, there is
 no need to incur the performance penalty associated with trapping INT
 71h as INT 0Ah.

 (This performance penalty arises from the fact that Windows 3.00 has
 to trap the INT 71h in protected mode and reflect that interrupt down
 to real mode to the BIOS, which issues INT 0Ah, which in turn has to
 be reflected back to protected mode so your ISR can handle it.)

 Microsoft Windows Software Development Kit
 =============================================================================


 1. OutputDebugString() Works in Debug & Retail Windows 3.00

 The Windows version 3.00 function OutputDebugString() is documented as
 being available only in the debugging version of Windows. However, it
 also works in the retail version of Windows (which it should not do).
 Thus, to output debug-mode-only information, you cannot use use this
 function; instead, you need to have conditional compilation such as
 the following:

 #ifdef DEBUG
     OutputDebugString (lpMessage);
 #endif

 Microsoft has confirmed this to be a problem in Windows 3.00. We are
 researching this problem and will post new information here as it
 becomes available.


 2. Speeding Up the TextOut Function

 Question:
    How can I improve the speed of the Windows TextOut function?

 Response:
    To improve the speed of the TextOut function, do the following:

    1. Compute the upper left-hand corner of the first character cell
 of TextOut using client coordinates.
    2. Convert the client coordinates to screen coordinates (i.e.,
 ClientToScreen--physical units).
    3. Align the screen coordinates on 8-bit boundaries using modular
 arithmetic (/ or %). The screen is handled scan line by scan line and
 is word aligned. By byte aligning the text, bit rotations on TextOut
 (to fold the text onto the screen using bitblt) are avoided.
    4. Convert the aligned screen coordinates back to client coordinates.
    5. The speed increase that this gives you will be less noticeable if
 you are doing the TextOut calls while the screen is in transparent
 mode rather than opaque mode. To change the screen from transparent to
 opaque mode, use the SetBkMode() function call.



 3. Type Ahead in Dialog Boxes

 Question:

 Can I make my application type ahead so that the dialog never comes
 up?

 Response:

 To produce the desired effect, create the DialogBox without the
 WS_VISIBLE attribute. This way, the dialog is not required to display
 itself before accepting and processing input.


 4. WM_TIMER and WM_PAINT Message

 Question:

 Can several WM_TIMER messages appear in the queue, or are they
 combined into one similar to the WM_PAINT message?

 Response:

 WM_TIMER messages are combined into one WM_TIMER message just like the
 WM_PAINT message. The following occurs:

 1. When the time interrupt goes off, a timer flag is set.

 2. When the application's message queue is empty, a routine checks to
    see if the timer or paint flag is set. If it is set, a WM_TIMER or
    WM_PAINT message is sent.


 5. Intersegment Self-Relative Function

 Question:

 What is the meaning of the following error message?

 Intersegment self-relative fixup near 005A in segment _TEXT in
 c:\windows\lib\SLIBW.LIB(winstart) offset 6C44H
 Intersegment self-relative fixup near 005E in segment _TEXT in
 c:\windows\lib\SLIBW.LIB(winstart) offset 6C49H

 Response:

 Self relative fixups can occur if you are trying to make a near
 call to a function that is in another segment.  For example,
 if you have a code segment with the name FOO_TEXT, and you are
 trying to reference code in segment _TEXT, you will receive
 a self relative fixup error.

 Intersegment self-relative fixups are not allowed by LINK4. To support
 these fixups, the linker would have to compute the distance between
 the fixup location and the target address, which are in different
 segments. This procedure would only make sense if the segments are
 fixed and addresses are determined by <(base * 16) + offset>.

 In Windows, neither of these situations is necessarily true since code
 can be DISCARDABLE/MOVABLE.



 6. Getting Double-Click Message

 Question:
    I have a window defined with the CS_DBLCLKS style. I want to
 receive both the WM_LBUTTONDOWN and WM_LBUTTONDBLCLK messages, but
 when both messages are present in the message switch statement, only
 the WM_LBUTTONDOWN message is acknowledged. If I remove the
 WM_LBUTTONDOWN message, then the WM_LBUTTONDBLCLK message is
 acknowledged. It seems when I double-click, a WM_LBUTTONDOWN message
 is sent on the first click and the second click is not acknowledged.
 What must I do to receive a double-click message ahead of a single
 button down message?

 Response:
    The following is a modified version of the HELLO application that
 demonstrates one of the ways to process the double-click message:

 /* Procedures that make up the window class. */

 long FAR PASCAL HelloWndProc( hWnd, message, wParam, lParam )
 HWND hWnd;
 unsigned message;
 WORD wParam;
 LONG lParam;
   {
   PAINTSTRUCT ps;
   MSG msg;           /* added */
   long oldtime;      /* added */
   long dblclkTime;   /* added */
   char temp[80];     /* added */

   switch (message)
     {

     case WM_SYSCOMMAND:
       switch (wParam)

     case IDSABOUT:
       DialogBox( hInst, MAKEINTRESOURCE(ABOUTBOX), hWnd, lpprocAbout );
       break;

    /*****************************************************/
    /************ everything below was added *************/
    /*****************************************************/

     case WM_LBUTTONUP:
       oldtime = GetCurrentTime();
       dblclkTime = (long)GetDoubleClickTime();
       while(GetCurrentTime() - oldtime < dblclkTime)
         {
         /* Note: the window STYLE must have the CS_DBLCLKS style set. */

         if (PeekMessage((LPMSG)&msg,NULL,0,0,TRUE))
           {
           if (msg.message != WM_LBUTTONDBLCLK)      /* pass onto the app. */
             {
             TranslateMessage((LPMSG)&msg);
             DispatchMessage((LPMSG)&msg);
             sprintf(temp,"%x was pressed",msg.message);
             MessageBox(hWnd, (LPSTR)temp,(LPSTR)"double click", MB_OK);
             return(0L);
             }
           }
         else
           {
           while(!PeekMessage((LPMSG)&msg, NULL,
                                         WM_LBUTTONUP, WM_LBUTTONUP, TRUE))
             MessageBox(hWnd,(LPSTR)"HIT!",(LPSTR)"double click", MB_OK);
           return(0L);
           } /* end else */
         }   /* end while */

       MessageBox(hWnd,(LPSTR)"too long",(LPSTR)"double click", MB_OK);
       break;

     case WM_LBUTTONDBLCLK:
     case WM_RBUTTONDBLCLK:
     case WM_LBUTTONDOWN:
     case WM_RBUTTONDOWN:
       break;

    /*****************************************************/
    /********** everything above this was added **********/
    /*****************************************************/

     case WM_DESTROY:
       PostQuitMessage( 0 );
       break;

     case WM_PAINT:
       BeginPaint( hWnd, (LPPAINTSTRUCT)&ps );
       HelloPaint( ps.hdc );
       EndPaint( hWnd, (LPPAINTSTRUCT)&ps );
       break;

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


 7. Printing without Form Feeds

 Question:

 Can a Windows application spool to the printer without a form feed?
 When I issue a STARTDOC and ENDDOC without NEWFRAME (which issues a
 form feed), nothing happens.

 Response:

 A Windows application cannot spool directly to the printer; a NEWFRAME
 must be present. NEWFRAME starts the spooler running. As a side
 effect, most implementations issue a form feed. The best you can do is
 to drive the spooler directly, independently of a printer driver.


 8. Windows SDK: How to Get a Pointer to the Stack

 Question:

 How can I get a pointer to the stack?

 Response:

 C pushes its parameters in reverse order. The first parameter is
 always the last one pushed, is always on the top of the stack, and
 always has the same address relative to the start of the frame. You
 can use the following code to get to the stack:

    far foo()
    {
    int x;
    int far *y = &x;
    }

 Note that this pointer will not be valid when the function is exited,
 since the stack contents will change.


 9. CVW Warning CV0013: "Access Denied"

 Question:

 What is the meaning of the following CVW warning message?

    CV0013 Warning : Access denied

 Response:

 This message is the C run-time library "errno" value 13 as defined in
 ERRNO.H. CodeView got this status, not the debugee (the application
 being debugged). That is, CodeView was denied access to a file for
 some reason. Probably the file didn't exist or CodeView didn't have
 write permission.

 Check for misspelled or otherwise incorrect filenames, directories, or
 drives.


 10. Resolving Far Calls with Movable Flag

 Question:

 If I am using the middle model of compilation (-AM) and a module's
 code segment has been renamed through the -NT switch and the segment
 is declared movable in the DEF file, will the compiler generate FAR
 calls inside and outside the segment? What happens if I yield control
 to Windows from within that segment through PeekMessage()? Does
 Windows keep that code segment locked? If not, how does Windows
 resolve CSs that may have been pushed on the stack due to intrasegment
 FAR calls (generated by the compiler) if the segment moves?

 Response:

 Windows does not necessarily keep the code segment locked when using
 medium model. The Microsoft C Compiler uses BP as a "frame pointer."
 Local variables and parameters are always accessed using offsets from
 the BP register. The BP register is initially even and the Windows
 stack is word aligned. When a FAR call is made, BP is increased by
 one. If the code segment is discarded, the stack is walked and
 patched. By determining if BP is odd or even, Windows can tell whether
 the call is FAR or NEAR.

 When a long return address is on the stack, it has a pushed DS and BP,
 and since the BP is increased by one for FAR frames, FAR frames may be
 detected by walking the task chain and BP stack-frame chains.

 When the Windows prolog is set up, it does the following:

    extern far pascal foobar();

    cProc foobar,<FAR,PASCAL>
    "cBegin"
    Prolog: push ds ;Fixed/Moveable Multiple Data Segment Support
    pop ax
    nop
    inc bp ;Far Frame Marker/Moveable Code Support
    push bp
    mov bp,sp
    push ds ;Data Context Switch Code
    mov ds,ax ; "

    ...

    "cEnd"
    Epilog: sub bp,2
    mov sp,bp
    pop ds ;Data Context Switch Code
    pop bp
    dec bp ;Far Frame Cleanup
    ret


 11. "OpenFile" Versus "Open"

 Question:

 When should I use the Windows call "OpenFile"? Should I still use the
 DOS "open" call afterward (to set attributes such as BINARY access)?
 If so, should I immediately close the file after the "OpenFile" call,
 i.e., before calling "open"?

 Response:

 The use of Windows "OpenFile" creates a MS-DOS file handle. The file
 already is in a binary "raw" mode when created. When you use
 "Openfile", it eventually performs a straight DOS 3DH function call.

 The C run-time "open" functions do not necessarily open in binary
 "raw" mode; you may have to explicitly set the binary attribute.
 Windows "Openfile" does this automatically.

 Use the Windows "Openfile" function any time you want a return value
 of a MS-DOS file handle.


 12. Windows SDK: Fatal Exit 409 with Floating Point Math

 Problem:

 I am getting a "FatalExit code = 0x409 Segment contents trashed" error
 when I use the -FPi switch and library.

 Response:

 This error occurs because when your application starts up, it goes out
 and determines if an 80x87 chip exists. If the chip doesn't exist, the
 application then modifies the code segment to NOT use 80x87
 instructions; thus, the code segment has changed. When Windows moves
 or discards this segment, it will detect that the code segment
 contents changed since loading, and you get the fatal exit 409.

 When you run the debugging version of Windows on a machine that does
 not have an 80x87 chip, you can use the alternate math library. To do
 so, compile with the -FPa switch and use the xLIBFA library on your
 link line. You can also avoid this fatal exit by setting
 EnableSegmentChecksum=0 in the [kernel] section of your WIN.INI file.
 Documentation on this WIN.INI switch is on Page 656 of the "Microsoft
 Windows Software Development Kit Programmer's Reference" for Versions
 2.00, 2.03, and 2.10.


 13. Kanji Conversion

 Question:

 I want to write an application that can be converted to Kanji. What do
 I have to take into consideration?

 Response:

 Kanji uses two-byte characters. Therefore, when doing string
 comparisons and string pointer operations (searching forward and
 searching backward) you must make sure that you do not simply use
 pointer-- or pointer++. Instead, you should use the AnsiNext() and
 AnsiPrev() Windows calls.

 Valid first bytes for Kanji characters are 80 - 9f and c0 - ff. Valid
 second bytes for Kanji characters are 40 - ff.


 14. Expected Cleanup for Objects

 Question:

 My application creates a number of bitmaps with CreateBitmap() and
 CreateBitmapIndirect(). I was using Heapwalker and discovered that the
 bitmaps actually "belong" to GDI, not to my application, and they are
 not deleted when my application stops running. This means that the
 memory occupied by the bitmaps is never freed. Am I expected to delete
 any bitmaps created by my application when the application terminates?
 Are there other data structures that my application can create that
 have to be explicitly deleted by the application because the system
 does not reclaim their storage automatically?

 Response:

 It is the responsibility of each application to delete all objects it
 has created, i.e., brushes, pens, fonts, bitmaps, etc. Use the
 DeleteObject() routine.


 15. LoadLibrary() and LoadModule() Return hInstance, Not hModule

 The "Microsoft Windows Software Development Kit Programmer's
 Reference" Version 2.00 implies on Page 355 that LoadLibrary() returns
 the handle to the module. This is incorrect. It returns the handle to
 the instance, hInstance.


 16. Windows Private Dialog Class Requirements

 A private dialog class must have its cbWndExtra field set to
 DLGWINDOWEXTRA. Messages that are not handled by the class procedure
 must be passed to DefDlgProc(), not to DefWindowProc().

 Private dialog classes provide a way to customize the handling of
 dialog messages. Windows stores certain values in "extra bytes" that
 are part of the dialog's window structure. To allocate these bytes,
 make sure that the cbWndExtra field is set to DLGWINDOWEXTRA when the
 class is registered.

 Any messages that are not processed by the class procedure must be
 passed to DefDlgProc(). It will handle any special processing that
 Windows requires for dialogs. Any messages that it does not process
 will be passed to DefWindowProc().


 17. TILER.ARC: Windows Version 2.x Tiling Program

 There is a file named TILER.ARC in the Software Library that includes
 a Windows application that tiles open windows for Windows Version 2.x,
 giving them the appearance of Windows Version 1.x windows. This
 application was written by Michael Geary.

 This file can be found in the Software Library by searching for the
 filename TILER.ARC, the Q number of this article, or S10016. TILER.ARC
 has been archived with PKARC so you will need to unarchive it with
 PKXARC. The PKXARC utility is located on your OnLine Utilities Disk 2.


 18. Windows: Directory Names with 8-Bit Characters

 The following information pertains only to international versions of
 Windows:

 If you try to double click on an EXE file that is in a directory that
 has an 8-bit character in it (any character with decimal value greater
 than 127), Windows will not be able to find it. If you select Run from
 the File menu and enter the full pathname, Windows will find the EXE
 file and start the application.

 This is a problem in Windows, because if you have your own code that
 does an INT 21 4B, it will not find the file either. The same code
 works at the DOS level.

 The workaround is to put the full pathname of the executable file in
 the INT 21 4B function call.

 Microsoft has confirmed this to be a problem in Versions 2.00, 2.03,
 and 2.10. We are researching this problem and will post new
 information as it becomes available.


 19. Global Lock Count Changes in Windows 3.00

 The global lock count mechanism has been changed in Windows version
 3.00 protected modes (that is, standard mode and enhanced mode).
 GlobalLock only affects the lock count of discardable objects and the
 default data segment (DGROUP); moveable objects are not affected.
 Thus, repeatedly calling GlobalLock() and GlobalFlags() on a
 GMEM_MOVEABLE object does not show any changes to its lock count.

 The following are reasons and explanations concerning this design
 change:

 1. In Real Mode, GlobalLock fixes the segment:offset of a global
    memory object. It also increases the lock count, as reported by
    GlobalFlags.

 2. In protected mode, the far pointer returned by GlobalLock is a
    selector:offset, not a segment:offset. Because the selector value
    doesn't change, GlobalLock does not actually fix the memory object
    in the physical address space. Thus, GlobalLock in protected mode
    doesn't change the lock count, unless the object is discardable or
    is a default data segment.

    In the case of a discardable object, the lock count is meaningful,
    because Windows needs to know when the object can be discarded
    (which is when its lock count is zero).

 3. However, some applications have used the GlobalLock lock count as a
    "reference count" (that is, as an indication of how many times
    GlobalLock was called). If the lock count for an object goes to
    zero, these applications might consider the object as a candidate
    for being manually discarded, perhaps after copying the data to
    disk.

    Unfortunately, this use of GlobalLock as a reference count keeper
    does not work in protected mode. Applications that symmetrically
    pair calls to GlobalLock with calls to GlobalUnlock don't need to
    know the lock count, and therefore, are unaffected by this change in
    behavior.

 4. How does an application keep track of reference counts now, given
    that the GlobalLock approach doesn't work for nondiscardable
    objects in protected mode? The application should really keep track
    of reference counts itself, which shouldn't be hard to do because
    the application in need of this functionality will have a table of
    global handles anyway.

    However, if the application cannot be modified to maintain its own
    reference counts, then there is a new Windows function that will
    accomplish the same: GlobalFix. GlobalFix does the following two
    things:

    a. It fixes the object in the protected mode linear space.

    b. It increments the "lock count", as returned by GlobalFlags.

 5. Let's look at GlobalFlags. In real mode, it returns the GlobalLock
    lock count. In protected mode, if the object is discardable,
    GlobalFlags also returns the GlobalLock lock count. In protected
    mode, if the object is nondiscardable, GlobalFlags returns the
    GlobalFix reference count. Stated another way, GlobalFlags always
    returns the lock/fix count. However, in protected mode
    Global[Un]Lock does not affect the count, only Global[Un]Fix does.

    Note: In real mode, the GlobalFlags lock count actually indicates
    the sum of GlobalLock's and GlobalFix'es. So, if you are doing
    GlobalLock's and GlobalFix'es in pairs, then the GlobalFlags lock
    count actually is twice the logical reference count, if in real
    mode.

 6. If your application needs to keep track of reference counts, and
    you want Windows to do the work for you, you must accompany every
    GlobalLock with a GlobalFix so that you can depend on GlobalFlags'
    lock/reference count. Using GlobalFix just to keep track of the
    reference count, however, is a big overkill, if that's all you want
    it to do. Remember, GlobalFix also fixes the object in the
    protected mode linear address space. The price you pay for having
    Windows keep track of the reference count (by using GlobalFix) is

    a. You have to stick a Global[Un]Fix next to every Global[Un]Lock

    and, much worse:

    b. You establish sandbars in the linear address space.

    An application should either keep track of reference counts on its
    own or always pair GlobalLock calls with matching GlobalUnlock
    calls; the use of GlobalFix should be avoided.

    Very few applications should need to fix global objects in linear
    space, so few applications should need GlobalFix.

 9. If you need to unconditionally unlock and free a global memory
    object of any type, you can do so with code similar to the
    following:

         /* 1.  Make it discardable if necessary. */
         if (GlobalFlags shows that it's nondiscardable)
             GlobalRealloc (GMEM_MODIFY it to be discardable);

         /* 2. Remove any lock counts that might be on it. */
         while (GlobalUnlock != 0) /* keep unlocking it */
             ;

         /* 3. Free it. */
         GlobalFree()


 20. Maximum Brush Size

 Question:

 How big can a brush be?

 Response:

 The theory and code says that you can create any size brush. However,
 the brushes are realized by the OEM driver. The only requirement the
 driver must satisfy is that it must be able to handle 8 x 8 brushes.

 If the driver realizes variable sizes, passing any bitmap is
 acceptable. The driver decides what to do with brushes larger than
 8 x 8.

 Currently, our sample drivers use the top left 8 x 8 piece of the
 pattern; however, if the OEM wants to use the whole pattern, that also
 is proper. We limited our current device drivers to 8 x 8 because of a
 performance trade-off when we were targeting Windows for the 8088
 versus the 80286.

 The amount of pattern realized is a display driver-dependent issue.


 21. Windows SDK: Case Sensitivity in Atoms

 If you add the same string twice to the atom table, but you use a
 different case, the string is only stored once; only the first one is
 present.

 Both the KnowledgeBase (Q33912) and the "Microsoft Windows 2.00
 Software Development Kit Update" for Versions 2.03 and 2.10 mention
 that atoms are case insensitive.

 This means that when you use the AddAtom() function, the case is
 ignored when atoms are compared. Therefore, if you call AddAtom("DIR")
 and then AddAtom("dir"), you will have the single atom "DIR" (with a
 reference count of 2). If you call AddAtom("dir") first, you will then
 have the single atom "dir".

 Similarly, the other atom-handling functions are case insensitive. For
 example, calling FindAtom("dIr") will find the atom "dir", "DIR", or
 "Dir", and so on.


 22. Placing Caret after Edit-Control Text

 To place the caret at the end of the text in an edit control and set
 the focus to the edit control, you may do the following

    hEdit = GetDlgItem( hDlg, ID_EDIT );
    SetFocus =( hEdit );
    return FALSE;

 where ID_EDIT is the integer ID of the item to be retrieved.

 It is also possible to force the caret to a desired position within
 the edit control. The following code fragment demonstrates this. The
 length of the text in the edit control is returned with
 WM_GETTEXTLENGTH, then the caret is positioned two places to the left
 of the end of the text (textlength - 2), as follows:

    hEdit = GetDlgItem( hDlg, ID_EDIT );
    SetFocus = ( hEdit );
    textlength = SendMessage( hEdit, WM_GETTEXTLENGTH, 0, 0L );
    SendMessage( hEdit, EM_SETSEL, 0, MAKELONG( 0, textlength-2 ) );
    return FALSE;

 It is important to note that you must return FALSE when using SetFocus()
 for an edit control; otherwise, the SetFocus() will be ignored by
 Windows.


 23. SEPARATOR on Main Menu

 Question:

 Can I use the word SEPARATOR to get a vertical bar to appear on the
 main menu? If not, how can I get the vertical bar to appear on the
 main menu?

 Response:

 You cannot use the word SEPARATOR to get a vertical bar on the main
 menu bar. If you use the word SEPARATOR in a pop-up menu, you will get
 a horizontal bar. If you use MENUBREAK, the menus will start on
 another column.

 To get a vertical bar to appear on the main menu, put the vertical bar
 symbol in the actual menu string.


 24. BeginPaint() Coordinates

 Question:

 Is the rcPaint in the PAINTSTRUCT returned by BeginPaint() given in
 client, device, or logical coordinates?

 Response:

 BeginPaint() returns logical coordinates in the rcPaint member of the
 PAINTSTRUCT structure.


 25. Use of Interrupt 3F with Dynamic Link Libraries

 Question:

 What is Interrupt 3F used for in Windows? When is an actual INT 3F
 instruction executed?

 Response:

 INT 3F is used by the Windows dynamic link loading mechanism to bring
 in nonresident code, such as from a dynamic link library.

 For example, when Windows loads a library, it loads in the header file
 of the executable. In the header are INT 3Fs. When the application
 calls the library routine, Windows searches the header file to see if
 the routine is already loaded. If it is not loaded, the INT 3F
 instruction still exists. Windows then loads the code out of the .EXE
 file off the disk and changes the INT 3F instruction to be the address
 where the code was loaded into memory. It then executes that code.

 This process applies not only to dynamic link libraries but also to
 regular application code. Whenever a routine is called, there is the
 chance that it was discarded by the Windows memory manager. If so, the
 INT 3F dynamic load mechanism will be called to read the appropriate
 code segments back into memory.


 26. Controlling SYMDEB Message-Report Level

 Question:

 Is there any way to turn off the segment tracing (the messages that a
 segment has been loaded) that SYMDEB does? In a program that I am
 testing, many segments are loaded and swapped, and the trace messages
 dramatically slow down the code.

 Response:

 Use the /w option on the command line at SYMDEB startup. This sets the
 memory-allocation reporting level. The reporting level defines what
 kinds of memory allocation and movement messages SYMDEB is to display
 when Windows loads and moves program segments. The reporting levels
 are as follows:

 Level     Information Reported

   0         None
   1         (Default) general allocation messages
   2         Movement messages
   3         Allocation and movement messages


 27. Windows SDK: SendMessage() Versus SendDlgItemMessage()

 Question:

 The programmer's reference guide says to use SendMessage() to tell a
 dialog box to add strings to a list box. However, we must use
 SendDlgItemMessage() to accomplish this task. This method seems to be
 necessary for all dialog controls. When should we use SendMessage()
 and when should we use SendDlgItemMessage()?

 Response:

 Use SendDlgItemMessage() when the control is inside a dialog box; use
 SendMessage() when the control is not inside a dialog box. Never use
 PostMessage() to send a message to any control.


 28. GDI Objects Not Cleaned Up on Exit from Windows

 Question:

 What items/objects/resources are not destroyed automatically from
 memory by the Windows KERNEL when an application is closed? I want to
 know that all such items created by my application are cleaned out
 when I close Windows down, leaving it clean for other applications.

 Response:

 Everything is cleaned up except for GDI objects; you need to remove
 these.


 29. Waiting for Completion Not Supported in Windows

 Question:

 Does Windows support the DOS function 4D wait service?

 We are using an INTDOSX for a function 4B. We are expecting a return
 code from this loaded process (it is an assembly-language program). We
 follow the 4B with a 4D (wait) to get the return code from the loaded
 standard application. The application sends a "0" return code
 (AX=4C00, int 21), but we are receiving a return code of "1". This
 shows up in OUTREGS.S.AX and in the AX register.

 Response:

 Windows does not support a function 4D wait service. This function is
 not possible under the child-process architecture because you do not
 wait for your process; rather, you yield to it. Function 4D will
 always return "1" under Windows regardless of what process was called
 to make the exit.

 Child applications should use either SendMessage() or PostMessage() to
 inform their parents that they are finished.


 30. Fatal Error Messages 0x0140 and 0x0240

 The FatalExit messages "Local heap is busy" (0x0140) and "Critical
 section problems" (0x0240) indicate internal errors in Windows' memory
 management routines; 0x0140 indicates an incorrect attempt to enter or
 leave a critical section in the local memory manager; 0x0240 indicates
 that the total number of free handles don't match up.

 These errors shouldn't occur unless something gets trashed; this is
 usually an indication that an application is destroying memory by
 writing to an area where it shouldn't.



 31. How to Reset the Bank Line in Windows

 Question:

 What can we do to affect the bank-line setting (especially to move it
 down)? How can we predict where the bank line will be set? How is it
 determined?

 Response:

 The way Windows currently computes the bank line is as follows:

 1. The bank line starts from the load point of the KERNEL. This is
    typically 2K above where Windows loads.

 2. From here, add 288K (256K if HIMEM is installed).

 3. If the remainder is greater than 272K, install large-frame EMS;
    otherwise, install small-frame EMS.

 Windows/386 scans the area above 640K to find out what address ranges
 it can use. The default upper limit is E000. This limit can be raised
 on some machines with usable memory between E000 and F000 using
 LASTEMMSEG. The Model 80 and the Compaq DeskPro cannot do this.
 Windows/386 uses an additional 14K. This extra amount can cause a
 switch from large frame to small frame.

 To alter where the bank line is set, a new switch was added in Windows
 Version 2.10 as follows:

    Movable EMS Line

 The syntax is as follows, where "nnnn" is the number of kilobytes by
 which you want to move the line:

    win /l[+-]nnnn

 This number is currently limited to -16 <= nnnn <= +256. In practice,
 raising the line by much above 100K usually drops you into small-frame
 EMS.

 The /l flag is incompatible with the /n flag. Don't use both.

 The "+" in /l[+-]nnnn is optional.


 32. Compiler Support for Dynamic Linking

 Question:

 I am writing a compiler that will allow you to take advantage of the
 dynamic linking and loading capabilities of Windows. What features do
 I need to include with my compiler to take advantage of the dynamic
 linking/loading process?

 Response:

 To make use of the dynamic linking/loading feature implemented in
 Microsoft Windows, several modifications must be made to the
 programming language compilers. Since these changes are within the
 actual object code generated, both native MS-DOS compilers and cross
 compilers should be modified.

 The following is a description of the changes required to support this
 new feature:

 1. Compilers should provide a command-line switch to direct the
    compiler to generate the following prolog epilog code sequences
    (the switch is recommended so that the compiler can be used to
    generate both new style, dynamic modules for Windows, and
    older style, nondynamic modules for current versions of MS-DOS):

       Prolog                  Epilog
       ------                  ------

       mov ax,ds               lea sp,bp-2
       nop                     pop ds
       inc bp                  pop bp
       push bp                 dec bp
       mov bp,sp               ret [# parms]
       push ds
       mov ds,ax

 2. The language syntax should be enhanced to allow the specification
    of a keyword on procedure declarations that will cause the Pascal
    parameter passing convention (that is, push arguments to the stack
    from left to right; callee takes parameters off stack) to be used
    if other conventions are used.

 3. Provide a command-line switch to instruct the compiler to generate
    code that assumes SS is not equal to DS.

 4. The compiler must support mixed-model programs (for example,
    NEAR/FAR keywords).

 5. The compiler library modules should be recompiled with FAR
    procedures. We suggest that you provide both NEAR and FAR
    libraries.

 6. To allow developers to create Windows library modules, the compiler
    run-time libraries must support SS not equal to DS.

 Compilers implementing the above changes will generate programs and
 libraries that can be dynamically linked and loaded by Microsoft
 Windows. We strongly recommend that these changes be implemented, as
 this will allow application developers full access to the new
 operating-system functions with Microsoft Windows.


 33. Windows SDK: Heap Placement in Memory

 Question:

 How does the system decide where to put the heap? I need to reserve
 memory at the end of DGROUP for use by the compiler; I also need to
 know that this memory will not be affected by the heap or the stack.

 Response:

 The Windows system uses LocalInit(ds,0,size_in_bytes) to align the
 heap. The first 16 bytes in DS are the NULL segment. It contains a
 block of "reserved pointers." These are the heap pointers. The heap is
 located in DS by subtracting the size of the heap from the end of
 DGROUP and filling in the start of the heap pointer with the resulting
 offset from the end of DGROUP. This procedure avoids data-stack/heap
 collision.


 34. Message Queue Limits

 Question:
    What is the limit on the number of PostMessages Windows can handle
 for an application?
    I have an application that uses PostMessage to post messages to
 another application. It posts them all in a row as it reads them from
 a file. While posting these messages, my sending application never
 yields the processor and, as a result, I may be exceeding the maximum
 number of messages that can be posted to an application without it
 having control.
    My receiving application always seems to get the first eight
 messages posted to it, but messages beyond that are lost. I know I am
 posting the messages correctly because if I do a yield after each
 PostMessage and let the receiving application have control, it can
 receive and process an unlimited number of messages.

 Response:
    The maximum number of messages in the event (hardware) queue is 30.
 The limit for consecutive messages for the task (application) queue is
 8.
    You can change the size of your application's message queue by
 using the SetMessageQueue function. The SetMessageQueue function must
 be called before any messages are generated for the application,
 including messages generated by such functions as CreateWindow.


 35. EMS Issues in Windows Using Intel PS/2 Above Board

 Question:

 How can the customer control Windows' use of main and extended memory
 regarding placement of the bank line, and so on?

 Response:

 There are several issues involved here. In all cases, please read the
 entire contents of PS2EMM.TXT and EMM.TXT. These two files outline
 what is necessary to use these drivers and which cards they work with.
 Do not mix drivers with different cards.

 The EMM.SYS driver supplied with Windows/286 does NOT work with the
 Intel cards for PS/2s. You must instead use the driver that Intel
 supplies with the card. However, EMM.TXT does explain how to configure
 the Intel-supplied driver for optimal operation with Windows.

 Sample Command Line for Intel PS/2 Above Board Card

 The command line in the CONFIG.SYS for the Intel PS/2 Above Board with
 two megabytes on the card set for large frame LIM 4.0 EMS support in a
 Model 50 should read as follows:

 device=ps2emm.sys MOD50 EXP=1600 RD H=96 EXPF=C400 MCF=256 MCL=640

 Explanation of Command-Line Switches

 1. MOD50 - sets the EMS card and driver for the Model 50; use "mod60"
    for the Model 60.

 2. EXP=1600 - the amount of expanded memory in the computer. There is
    2048K of memory on the EMS card. With HIMEM.SYS and no network
    software loaded, Windows will typically show about 384K of
    conventional memory free. Therefore, set the first 256K of memory
    as nonbankable and let the motherboard supply it. 64K of memory at
    1 megabyte will be used for HIMEM.SYS. The balance of the memory on
    the card will be used as expanded memory.

    Starting at 256K, let the EMS card supply 384K of bankable memory,
    up to the conventional DOS memory limit of 640K. This maximizes the
    amount of expanded memory available for applications that are run
    under Windows. This value will not be the same if the customer runs
    DesqView because DesqView does not have much overhead that it needs
    to load into lower DOS memory to run. EMM.TXT gives an example of
    disabling all motherboard memory. If the customer is only running
    Windows and not DesqView, there is NO value to disabling all
    motherboard memory. It wastes 384K of memory that could instead be
    used as expanded memory.

    The math for determining the amount of expanded memory is as follows:

       2048K of memory on the EMS card - 384K (the amount of mappable
       conventional memory supplied by the card for Windows to bank
       applications with) - 64K of extended memory located at 1
       megabyte in which HIMEM.SYS is loaded.

    The total (2048K - 384K - 64K = 1600K) is used as expanded memory.

    This math may be slightly different depending on where Windows ends
    up setting the EMS swap line. It may be adjusted accordingly as
    detailed below.

 3. RD - Relocate the Intel expanded memory driver in expanded memory
    instead of conventional DOS memory. This will free up additional
    memory for Windows and DOS applications to run in.

 4. H=96 - This switch sets the number of expanded memory handles at
    96. The default, if left blank, is 64. The maximum handle count is
    255. Each handle eats up some memory, but if you do not have enough
    of them, you may degrade performance when EMS is being used. There
    is no firm estimate here because it depends on the applications
    being run and the number of expanded memory handles each one uses.

 5. EXPF=C400 - This switch sets the address in the memory range
    between 640K and 1 megabyte in which to locate the LIM 3.2
    expanded-memory page frame. In this case, it is set at C400, which
    should work in systems with VGAs. However, it is advisable to run
    the IBM PS/2 reference disk and verify that the adapter addresses
    allow this page-frame location. If not, the adapter address ranges
    should be adjusted and the EXPF= entry should be modified to
    reflect a workable situation.

    This page-frame address has nothing to do with LIM 4.0 support,
    which Windows/286 uses to run many applications simultaneously by
    banking them in conventional DOS memory mapped onto the EMS card.
    In some severely constrained situations, it is possible for a
    customer not to be able to free up the required 64K of memory for
    the page frame in upper memory and still be able to be run multiple
    applications simultaneously by banking conventional memory through
    the LIM 4.0 support.

 6. MCF=256 and MCL=640 - These switches set the beginning address of
    mappable conventional memory (MCF = mappable conventional first)
    and the ending address (MCL = mappable conventional last) to allow
    384K of conventional memory to be banked for multitasking.

 There are other options available. Consult EMM.TXT, PS2EMM.TXT, and
 the manuals for additional information.

 Moving the EMS Swap Line in Windows

 To take full advantage of this configuration, it may be necessary to
 move the EMS swap line in Windows. The only way to do this is by
 starting Windows/286 with the following command line, which can be
 used in a start up Windows batch file, WIN.BAT, which in this case is
 located in the root directory on Drive C:

 @echo off
 c:
 cd\win
 win /l-16 %1 %2
 copy c:\win\win.saf c:\winsaf.saf
 copy c:\win\win.ini c:\win\win.saf
 c:
 cd\

 "win /l-16" starts Windows and lowers the EMS swap line as far as it
 will go. The range of values here is -16K (win /l-16) to +200K (win
 /l200). Please note that the switch is a lowercase "L", not an
 uppercase "I" or the number one. "%1 %2" restores the ability to pass
 command-line parameters directly to Windows.

 This batch file also stores the last two versions of the WIN.INI file
 in case you or an application alters the WIN.INI and you do not like
 what it does to your system.

 If you run heapwalker, you should now see that the EMS swap line is
 moved down in memory. Since Windows cannot move the swap line down any
 further than this, there is no benefit to having any additional
 conventional memory made bankable in the command line for EMM.SYS.
 There is also another "gotcha" here. Many programs and printer drivers
 need to load global memory segments, as do DLLs. Depending on the
 amount of memory-resident software and CONFIG.SYS drivers loaded,
 these figures may vary considerably.

 The bottom line here is play carefully with the movable swap line. It
 is advisable to run heapwalker to see that there is enough global
 memory below the swap line for everything that wants to live there.

 Special Considerations when Using Excel Version 2.10

 Excel Version 2.10 has two features that are of peripheral interest
 here. First, there is a new WIN.INI entry under the Excel section that
 should read "block=2" if there is expanded memory in the system. This
 will guarantee that data is loaded in pieces that will fit in expanded
 memory and leave additional conventional memory available for other
 uses. The second is the "swapsize=" entry in the Excel section of the
 WIN.INI. This allows you to adjust the amount of conventional memory
 Excel needs to open. It may be set down to 128. This value is in
 kilobytes. We do not recommend changing this unless absolutely
 necessary. More information on this is available in the new
 configuration manual that accompanies Excel Version 2.10.

 Note about Setting Up Windows for EMS

 When setting up Windows for EMS, be sure that there are no entries for
 HIMEM.SYS or SMARTDrive in the CONFIG.SYS file; otherwise, the Windows
 setup program will not install HIMEM.SYS or the USERF.EXE that uses
 the HIMEM.SYS area of memory.



 36. Use of NEWFRAME and NEXTBAND

 The NEXTBAND section on Page 25 of the "Microsoft Windows 2.00
 Software Development Kit Update" incorrectly states that
 Escape(...NEWFRAME...) should be called after the last NEXTBAND on a
 page. When this command sequence is followed, extra blank pages are
 generated.

 NEWFRAME and NEXTBAND are exclusive; an application must do one or the
 other, but not both. Note that not all nonbanding devices support
 NEXTBAND, so the application must check if the device is a banding one
 to determine which escape to use. Banding devices can be detected by
 checking the RC_BANDING bit in the GetDeviceCaps() RASTERCAPS field.

 When using NEWFRAME, the call must be placed at the bottom of every
 page to output the page and start a new page.

 Please note that ENDDOC and ABORTDOC also are exclusive. ABORTDOC is
 used when you wish to abort the document (e.g. the application has
 detected an error or you cancel the print command). Some output may
 have already been sent to the device at the time of the ABORTDOC, but
 any additional output will be discarded. ENDDOC is used to close and
 send a completed document.


 37. Windows SDK: Further Description of Atoms

 Question:

 I have the following questions on Atoms that expand on the description
 of Atoms:

 1. Is it reasonable to use Atoms to store strings of data?

 2. Why is there no Atom for a null string?

 3. How are Atoms stored?

 4. What is the penalty for declaring many Atoms and then only using a
    few?

 5. Are there reasonable limits to the number of strings that can be
    stored as Atoms?

 6. Is there any way to share Atoms between two instances of a program?

 7. Is there a limit to the number of characters in a string that is
    being stored as an Atom?

 8. Are Atoms movable?

 9. Do Atoms get swapped?

 Response:

 The answers to your questions are as follows:

 1. Yes, you can store constant strings of data using Atoms.

 2. There is no Atom for a null string because none is defined.

 3. Atoms are hashed with bucket chaining used to resolve collisions.
    The memory for the Atoms is allocated out of the caller's DS. The
    overhead per Atom is 9 bytes (4 for the memory arena, 5 for the
    Atom structure).

 4. The only consequences of declaring many Atoms and then only using a
    few are the consumption of memory and the increased chain of
    collisions.

 5. The absolute limit to the number of strings that can be stored as
    Atoms is the size of the caller's data segment.

 6. To share Atoms between two instances of a program, have a shared
    library .EXE file with a single data segment that holds the Atoms
    to be shared. The Windows user interface code is an example of
    this.

 7. The limit to the number of characters in a string being stored as
    an Atom currently is 255.

 8. Atoms are constants, and therefore are not movable.

 9. Atoms do not get swapped.


 38. Making a DEBUG Version of Windows

 To make a DEBUG version of Windows, perform the following steps:

 1. Find the disk that has the debugging versions of KERNEL.EXE,
    GDI.EXE, and USER.EXE. They are in a directory called DEBUG.

 2. Copy these DEBUG versions of KERNEL.EXE, GDI.EXE, and USER.EXE to
    your SETUP disk and run SETUP.

 3. Copy the *.SYM files from the floppy disk to the directory that you
    will be doing your debugging in.

 4. To use SYMDEB, make sure that the line to your AUX or COM1: port is
    initialized by sending something to it. To do this, you may place
    the following code in your AUTOEXEC.BAT file:

    MODE COM1:9600,N,8,1         (this initializes the port)
    COPY CONFIG.SYS COM1         (this verifies that it's working)

 5. To use SYMDEB at the command line, type in the following:

    C:> symdeb myapp.sym kernel.sym win.com myapp

    The system will then display the processor you are using and give
    you a minus sign as the prompt, like the DOS version of DEBUG.

 6. To load the symbol table, type the following:

    -xo myapp!

 7. To start running devices off the AUX port, type the following:

    -=COM1

    This will redirect input from the COM1: port.

 8. To set up breakpoints or issue other SYMDEB commands, use the
    keyboard on the remote (debugging) terminal. To get past the
    Windows initialization routines and into your program, issue the
    following commands:

    -bp winmain
    -g


 39. EM_SETFONT Documentation Error in Programmer's Reference

 On Page 509 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" for Version 2.10, the EM_SETFONT message is
 referenced. This message does not exist and should not be listed in
 the documentation.


 40. INT 3 Trap on Clipboard Read When Creator App. Gone, EMS Only

 Question:

 When running LIM 4.0 under Windows Version 2.10, and debugging an
 application, I encountered an unusual problem that can be duplicated
 with any application that sends data to the clipboard. To duplicate
 the problem, do the following:

 1. Run Notepad.

 2. Type a few characters, select them, and copy to the clipboard.

 2. Close Notepad.

 If you now load a new copy of Notepad and do an Edit/Paste (any
 program that reads the clipboard will do), you get an INT 3 trap into
 the debugger.

 However, the clipboard data is just fine. If no debugger is loaded,
 the INT 3 vector just points to an IRET and everything works
 correctly. If from the debugger we stuff a NOP in place of the INT 3
 instruction, everything proceeds fine and the data is there.

 This peculiarity seems to occur any time the program that copied
 something to the clipboard is gone, and another program reads the
 clipboard. The specific identities of those programs is irrelevant. We
 thought that once a global memory block was given to the clipboard, it
 was the clipboard's forever. You do seem to recover from this case. Is
 this behavior normal and expected?

 Response:

 This INT 3 was put in during development to signal that this was a
 special case that needed extra care. This INT 3 should have been taken
 out when that section of the code was completed, but it was
 accidentally left in. It was probably overlooked because, as you
 mention, the normal INT 3 vector points to an IRET. Go ahead and
 replace the instruction that the debugger stops at with a NOP, and
 everything will proceed as normal.


 41. Windows SDK: Using User-Defined Resources

 Question:

 When using user-defined resources, the routine LockResource must be
 called to get a pointer to the actual resource. However, there is no
 equivalent UnlockResource() routine to unlock it. What needs to be
 done? Does GlobalUnlock() work?

 Response:

 The rules are as follows:

 1. Match LockResource() calls with GlobalUnlock() calls.

 2. Match LoadResource() calls with FreeResource() calls.


 42. Checksum Calculation for Segmented-Executable File

 Question:

 How is the new .EXE header's 32-bit checksum calculated? The manual
 says that it is the sum of all the 32-bit doublewords in the file, but
 I can't seem to get this to work.

 Response:

 Until Version 5.00.09 of LINK4, a problem caused the checksum to be
 calculated nondeterministically. The checksum that was calculated
 depended on what was on the stack, so it would tend to be the same for
 a particular OS and linker version, but would change if either of
 those did.

 The source code for a utility that will calculate and verify the
 checksum in a segmented-executable file is located in the Software
 Library. It is called VSUM. Be sure to compile with the -DM_WORDSWAP
 switch if you are compiling under DOS. The NEWEXE.H file is also
 needed and is included with VSUM. These files can be found in the
 Software Library by searching on the filename VSUM.ARC, the Q number
 of this article, or S12121.

 The usage for VSUM is as follows:

    VSUM [segmented executable file.exe]


 43. Windows SDK: Split Scrolling Using Two Windows

 Problem:

 I have a parent window that is a tiled window with both horizontal and
 vertical scroll bars. I want the child window to be a title, one line
 long, across the top of the parent window. I would like the child to
 be stationary, i.e., not to scroll off the screen when I (vertically)
 scroll the tiled window.

 Response:

 You should create two child windows: the first should be one line and
 the second should be the rest of the client area. You should then do
 all of your scrolling in the second child.


 44. SYMDEB Hangs When BACKSPACE Key Used (Use the y+ Option)

 SYMDEB hangs if you use the BACKSPACE key while in the program when a
 command-line editor or keyboard enhancer is being used. If you are
 using CED or DOSEDIT, remove it, reboot your machine, and try again.

 You can also enter the y+ command at the "-" prompt. This will allow
 you to then use the BACKSPACE key.


 45. Windows SDK: Contiguous Global Memory

 Question:

 I need to allocate several pieces of memory in a Windows application.
 A variable number of memory blocks may need to be allocated, possibly
 exceeding 64K of memory; therefore, we are using GlobalAlloc(). As a
 further restriction, it seems that these memory blocks need to be
 contiguous. Is there any way to guarantee contiguous memory, or must I
 attempt to allocate a very large global memory block and control a set
 of offsets myself? If I use GlobalReAlloc(), will I lose the data
 already stored within the memory block?

 Response:

 There is no way to guarantee contiguous memory unless the chunk is one
 memory object. These objects have headers, and even if they are butted
 together in memory, the data are separated by their headers. If you
 don't know how big the chunk needs to be to begin with, you will want
 to do a GlobalReAlloc(). When you GlobalReAlloc() to a larger size,
 the original data is intact at the start of the object, with the
 additional memory allocated at the end of the original object. This
 means that you can originally allocate only as big an object as you
 need at the time. Make sure that your object is not locked at the time
 you do the GlobalReAlloc(). This will give the GlobalReAlloc() the
 best chance of success.


 46. Fatal Exit with GetTextExtent and MetaFiles

 Problem:

 There seems to be a problem with GetTextExtent. When I use an hDC for
 a MetaFile, I get a Fatal Exit that displays the following:

 VALIDATEHANDLE+004A
 GETOVERALLTEXTEXTENT+0013
 GETTEXTEXTENT+0019
 etc.

 However, it works as documented with a window hDC. I have altered my
 software to get the extent needed by using the window hDC.

 Response:

 GetTextExtent() only can work properly using a display context that is
 specific to a particular device. Since the hDC for a MetaFile does not
 specify a device, GetTextExtent() cannot return a reasonable result
 when passed such a handle. GetTextExtent() should only be passed
 handles to display contexts that are specific to a particular device.


 47. SetSysModalWindow

 Question:
    What does SetSysModalWindow do? Is there another way, other than
 DestroyWindow, to stop a SystemModal window in the system?

 Response:
    To see an example of a SystemModal dialog, click on the MS-DOS "A"
 icon without a disk in Drive A so a "system error" message appears.
 Notice that no other window can get the focus.
    To see an example of a Modal dialog box, bring up the MS-DOS
 "About" box. You can continue with other applications, but you cannot
 do anything within the MS-DOS Executive.
    To see an example of a Modeless dialog box, bring up the MS-DOS
 "Get Info" menu item. You then can continue to do things with the
 MS-DOS Executive.
    System Modal dialog boxes should be made only if it is required for
 the user to respond immediately (i.e., if the system might crash
 otherwise). The only valid way to make a dialog box SystemModal is to
 use DialogBox; therefore, you should use EndDialog to destroy it. Do
 not use CreateDialog and DestroyWindow to implement a SystemModal
 dialog.
    The following sample code demonstrates how to do it:

 BOOL FAR PASCAL About( hDlg, message, wParam, lParam )
 HWND hDlg; unsigned message; WORD wParam; LONG lParam;
 {
    if (message == WM_COMMAND) {
    EndDialog( hDlg, TRUE );
    return TRUE;
    }
    else if (message == WM_INITDIALOG) {
    SetSysModalWindow(hDlg);
    return TRUE;
    }
    else return FALSE;
 }
 --------------- end of sample code.


 48. Windows SDK: Intercepting Messages for Active Windows

 Question:

 How can I intercept all messages for all active windows?

 Response:

 Use PeekMessage() in a loop with a handle of -1. Remember that not all
 messages are intended for your application; those that are not must be
 forwarded to their intended recipient.


 49. LB_SETSEL on All Strings in a Multiple-Selection List Box

 To clarify the documentation concerning LB_SETSEL on Page 546 of the
 "Microsoft Windows Software Development Kit Programmer's Reference,"
 you can select or deselect all the strings in a multiple-selection
 list box.

 To select all strings, type the following:

     SendMessage(..., LB_SETSEL, 1, -1L)
                                 ^ any nonzero value

 To deselect all strings, type the following:

     SendMessage(..., LB_SETSEL, 0, -1L)
                                 ^ must be zero


 50. GetCharWidth() Works Only in MM_TEXT Mode

    There is a problem in Windows Versions 2.03 and 2.10 that prevents
 GetCharWidth() from working in any map mode other than MM_TEXT.
    Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10. This problem has been corrected in the next version.



 51. ChangeMenu(): Cannot Directly Add a MENUITEM to a MENUITEM

 Problem:

 I am trying to change my menu (I only have one, not including the
 System menu). I simply want to append an item to the last item of the
 menu. The menu definition I have included below shows what I desire to
 happen. The Return to... option will appear on the top level of the
 menu. I have had luck appending MENUITEMs to POPUP menus, but no luck
 appending MENUITEMs to the last top-level MENUITEM.

 ProcoderMenu MENU
 BEGIN

   POPUP "&Options",
   BEGIN
     MENUITEM "&Drop down active line",     IDM_DROPDOWN
     MENUITEM "&No-Drop down active line",  IDM_NODROPDOWN
   END

   /**************************************************************/
 /* would like it appended right here.                         */
   /**************************************************************/
   MENUITEM "&Return to ???", IDM_TRANSFER

 END

 The code I am using to try to add the MENUITEM to the MENUITEM
 with identifier IDM_TRANSFER is as follows:

     hMenu = GetMenu(hWnd);
     ChangeMenu(hMenu, NULL, OutData, IDM_TRANSFER, MF_APPEND);
     break;

 Response:

 Window menus are defined recursively. The handle to the menu gets you
 a handle to the menu bar, and pop-up menus within the menu bar are
 also menus with handles associated with them. To add items to a pop-up
 menu, you must first call GetSubMenu() to retrieve a handle to the
 pop-up. Then when ChangeMenu() is called, the pop-up menu is where
 items are added rather than the menu bar. This works correctly for
 pop-up menus; however, you are trying to add a menu item to a
 top-level menu item that is not a pop-up. This cannot be done. Items
 can only be added to the top-level menu bar or to pop-ups.

 To give the effect of adding a menu item to a menu item, you must
 delete the menu item and replace it with a pop-up that has one item.
 The code below shows how to add menu items to both POPUPs and how to
 replace top-level menu items with pop-up menus.

 The following is the definition of the menu that is already part of
 the window, followed by the menu definition that contains two pop-up
 menus that will be used to replace two top-level menu items:

 original MENU
 BEGIN
     POPUP "one"
       BEGIN
           MENUITEM "one",   44
           MENUITEM "two",   45
           MENUITEM "three", 46
       END
     MENUITEM "two",         47
     POPUP "three"
       BEGIN
           MENUITEM "one",   48
           MENUITEM "two",   49
           MENUITEM "three", 50
       END
     MENUITEM "four",        51

 END

 added MENU
 BEGIN
     POPUP "new two"
       BEGIN
           MENUITEM "ha",   37
           MENUITEM "ha",   38
           MENUITEM "ha",   39
       END
     POPUP "new four"
       BEGIN
           MENUITEM "ha",   40
            MENUITEM "ha",   41
           MENUITEM "ha",   42
       END
 END

 The following code adds one MENUITEM to each of the two existing POPUP
 menus, which are located in positions 0 and 2 in the top-level menu:

     HANDLE hMenu,hSub;

 /* get handle to window menu */
     hMenu = GetMenu(hWnd);

 /* get handle to first popup */
     hSub = GetSubMenu(hMenu,0);

 /* add item */
     ChangeMenu(hSub,0,(LPSTR)"new item",22,MF_APPEND);

 /* get handle to second popup */
     hSub = GetSubMenu(hMenu,2);

 /* add item */
     ChangeMenu(hSub,0,(LPSTR)"new item",23,MF_APPEND);

 The following code replaces the top-level menu items with pop-up
 menus. The menu items that are being replaced are in positions 1 and 3
 in the top-level menu and have identifiers 47 and 51:

     HANDLE hMenu,hWindowMenu,hPopup;

 /* get handle to menu with popups in it */
     hMenu=LoadMenu(hInst,"added");

 /* get handle to window menu */
     hWindowMenu=GetMenu(hWnd);

 /* delete menu item */
     ChangeMenu(hWindowMenu,47,"a",47,MF_DELETE|MF_BYCOMMAND);

 /* get handle to first popup to add */
     hPopup=GetSubMenu(hMenu,0);

 /* insert the popup in appropriate place in top-level menu  /
     ChangeMenu(hWindowMenu,
                1,
                "new two(popup)",
                hPopup,
                MF_POPUP|MF_BYPOSITION|MF_INSERT);

 /* delete other menu item */
     ChangeMenu(hWindowMenu,51,"b",51,MF_DELETE|MF_BYCOMMAND);

 /* get handle to second popup to add */
     hPopup=GetSubMenu(hMenu,1);

 /* last item in menu, so append */
     ChangeMenu(hWindowMenu,
                NULL,
                "new four(popup)",
                hPopup,
                MF_POPUP|MF_BYPOSITION|MF_APPEND);

 /* draw menu bar: contents have changed */
     DrawMenuBar(hWnd);


 52. New Version of LIBENTRY.ASM in Software Library

 Previous versions of LIBENTRY.ASM are incorrect because they did not
 use Windows Prolog/Epilog code. Under certain low-memory conditions,
 where the DLL makes calls in its LibMain procedure that can cause
 memory movement, the system can lock up.

 Also, if the call to LocalInit in libentry fails, the current
 LIBENTRY.ASM will not clean up the stack properly.

 This problem has been corrected in the archived file LIBENTRY (which
 contains the corrected LIBENTRY.ASM) located in the OnLine Software
 Library. Use this file as a replacement for the LIBENTRY.ASM that is
 shipped with the SDK.

 This problem will be corrected in the final release of the Windows
 version 3.00 SDK. However, all versions that you have received prior
 to and including the May 1990 general release are incorrect.

 LIBENTRY can be found in the Software Library by searching on the
 keyword LIBENTRY, the Q number of this article, or S12561. LIBENTRY
 was archived using the PKware file-compression utility.


 53. When To Use LocalFreeze(), LocalMelt(), and LocalCompact()

 Question:

 When would you use LocalFreeze()/LocalMelt() or LocalCompact()?

 Response:

 The LocalFreeze() function keeps the local heap from being compacted.
 The LocalMelt() function allows compaction of the heap. Use these
 functions when you have pointers defined in the local heap and your
 application requires an allocation of local memory. By using this
 procedure, the pointers to previously allocated blocks will remain
 valid during an allocation request.

 The LocalCompact() function can force a compaction of non-locked
 blocks.


 54. Windows SDK: Unable to Initialize Radio Button Group

 Problem:

 When we bring up our dialog box, the first button does not get the
 focus. Although we can change the order of other radio button groups
 within the dialog and they will retain their settings, the first radio
 button group will not.

 Response:

 This problem involves the following:

 1. Radio buttons become checked when given the focus.

 2. Radio buttons also set (or clear) their WS_TABSTOP bit when they
    get (or lose) the focus.

 3. The dialog manager scans the child windows of the dialog box to
    determine which child window to set the focus to before the
    WM_INITDIALOG message is sent. If the WM_INITDIALOG message returns
    TRUE, the focus will be set to this window. If WM_INITDIALOG
    returns FALSE, the focus is not set by the dialog manager.

 Rearranging the dialog template works correctly; however, if you want
 the focus to be on the initially checked radio button when the dialog
 is brought up, you must do the following:

 1. Instead of calling CheckDlgButton() to check the first radio button
    in the group, call SetFocus(GetDlgItem()). This procedure will set
    the WS_TABSTOP bit, and check the radio button.

 2. Return FALSE from the WM_INITDIALOG message.


 55. Windows SDK: Restoring Objects in a Metafile DC

 Problem:

 I am creating a metafile that uses different pens and brushes. I use
 SelectObject() to select my pen and brush into the metafile DC. The
 handle returned for both the old pen and the old brush is 1.

 When I am finished with the metafile, I re-select the old pen and
 brush so I can delete them. Since the handle is the same, the first
 procedure works properly, but the second dies with an "Invalid Handle"
 error.

 Response:

 Metafiles do not have default objects in their hDC when they are
 created. When you do a SelectObject() into the hDC of a metafile, if
 the function is successful, it will return 1. Otherwise, it will
 return NULL.

 To delete or free the resources that you have selected into the
 metafile, do a DeleteObject(hObject), rather than
 DeleteObject(SelectObject(hDC,hOldObject).


 56. MakeProcInstance() Not Needed in DLLs

 It is not necessary to call MakeProcInstance() for callback functions
 and dialog functions in Windows dynamic link libraries. There is only
 one data instance in a DLL. The three NOPs in the prolog code of
 functions EXPORTed from DLLs are replaced by a MOV, AX ####
 instruction by the loader. The loader fixes up the #### with the
 address of the data segment whenever the DLL is loaded or moved.


 57. Windows SDK: GetCurrentPosition() Returns Logical Coordinates

 Question:

 When I try to get the location of the mouse cursor using the
 GetCurrentPosition() function, my machine always returns zeros. Is
 there a problem with this function?

 Response:

 GetCurrentPosition() returns the logical coordinates of the current
 drawing position, which is changed by calls to functions such as
 MoveTo() and LineTo(). To receive the current position of the mouse,
 save the position passed in the lParam of a WM_MOUSEMOVE message or
 any of the button messages.


 58. Windows SDK: Building an Icon Resource Dynamically

 Question:

 How can I build an icon resource in memory dynamically? I cannot use
 LoadIcon() because the icon will not be coming from my .EXE file.
 Instead, it will be defined by the application designer in an
 interactive design process and will be stored as an application
 description file. What is the format of an icon resource?

 Response:

 There is no method to build an icon resource in memory dynamically at
 this time. Current methods lack the .EXE header information Windows
 needs to do the proper memory management necessary for shared
 resources.


 59. Windows SDK: Sending Character Messages to Other Applications

 Problem:

 I am unable to send keystrokes to another application. I have tried
 several variations of SendMessage(hWndOldApp, WM_CHAR,...).

 Response:

 To send keystrokes to another application, use
 PostMessage(hWnd,WM_KEYUP,...) and PostMessage(hWnd,WM_KEYDOWN,...).


 60. Pascal Libraries and Windows

 Question:

 I am trying to build a Windows application and write a Windows
 executable library (.EXE) using Pascal. I keep getting duplicate
 identifier references when I link. What is the problem?

 Response:

 The run-time libraries for Pascal are produced with stack checking
 turned on. The LIBAUX routine does not define the STKHQQ variable.
 Modules reference STKBQQ, which references STKHQQ. Consequently,
 entx6l is brought in to satisfy the STKHQQ reference.

 LIBAUX was not written with Pascal in mind. However, there is a
 version of LIBAUX in the OnLine Software Library that works with
 Pascal (compile your program {$stackck-}). This file can be found in
 the Software Library by searching on the filename LIBAUX.ARC, the Q
 number of this article, or S12073.


 61. Windows SDK: Printing Strings of Mixed Fonts

 Problem:

 I am sending strings to a printer DC using TextOut(). A different font
 is selected prior to each call to TextOut(). The number of spaces
 appended is between each string and is not predictable. However, each
 time the document is printed, the same number of spaces appear at the
 same positions, regardless of what printer is being used.

 Response:

 Features such as boldface and underlining do not always have the same
 TEXTMETRIC values. Do a call to GetTextMetrics() before sending a
 string to the TextOut() function. Using the values returned by
 GetTextMetrics(), calculate the (X,Y) positioning of the string and
 use that (X,Y) pair in the call to TextOut().


 62. Opening and Reading Binary Files

 Question:

 What is the best way to open and read a file of binary data in a
 Windows environment? The file is to be loaded as code into a
 special-purpose board. Since the loading process will be a lengthy
 one, I want to use a method that is not disrupted by Windows
 memory-management actions.

 Also, how do I lock the data buffer for the program segment below?

    FILE *stream;
    ...
    stream = fopen(...);

 Response:

 The OpenFile function handles volume names and should be used whenever
 possible.

 You will be able to use the stream I/O routines if the stack size is
 defined in the .DEF file. You need not lock the stream. However, if
 the stream routine takes a pointer as an argument, you need to confirm
 that pointer's validity by using LockData and UnlockData.

 Please make sure you are using the libraries from C Version 4.00 or
 higher.


 63. Accelerators Return False Information

 Problem:

 We would like to define accelerators for the F10 key in the following
 manner:

 VK_F10, F10, VIRTKEY, /* F10 key */
 VK_F10, SF10, VIRTKEY, SHIFT /* Shift F10 */
 VK_F10, CF10, VIRTKEY, CONTROL /* Control F10 */

 However, when SHIFT-F10 or CTRL-F10 is entered, two WM_COMMAND
 messages appear, instead of the expected one WM_COMMAND message. The
 first message is the F10 key; the second message is the SHIFT-F10 or
 CTRL-F10. As a result, further processing is needed to distinguish
 which event is occurring.

 Response:

 This is an order-dependency problem. The only workaround for this is
 to do a GetKeyState function call to determine which other key was
 held down.


 64. WinSDK Prog Ref 2.00 UPDATE.DOC: DLGTEMPLATE Dialog Template

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 617 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 DLGTEMPLATE   Dialog Template
 Field                 Description

 dtResourceName[]      Specifies a zero-terminated string that
                       specifies the name of the dialog box's menu.

 dtClassName[]         Specifies a zero-terminated string that
                       specifies the name of the dialog box's class
                       name. If a standard dialog box is desired, it
                       should be a one-byte NULL string.

 [After "dtilY", add a new field:]
 Field                 Description

 dtilClassName()       A zero-terminated string that specifies the
                       control's class. It may be one of the following:

                          BUTTON
                          EDIT
                          STATIC
                          LISTBOX
                          SCROLLBAR


 65. Loading Dynamic Link Libraries as Other Than .EXE Files

 To utilize the automatic dynamic link library (DLL) load mechanism,
 dynamic link libraries must be named as .EXE files. This means they
 cannot be renamed to .DLL files as they are in Microsoft OS/2.

 The only way to circumvent this naming convention requirement is to
 manually load the dynamic link library yourself.

 If a dynamic link library is named as an .EXE file and is executed
 directly (by double-clicking on it within the MS-DOS Executive), there
 is no way to unload it without terminating Windows.

 If the library is renamed to something other than an .EXE file and an
 application that uses the library is run, a dialog box will appear,
 instructing you to insert the disk with the .EXE file in Drive A.

 If you want to rename the library file to something other than an
 .EXE, you can use the LoadLibrary() call to read it in from the disk,
 then make GetProcAddress() calls on each library function. The
 functions can then be called through the pointers returned from
 GetProcAddress().


 66. Use of nIDEvent Parameter to SetTimer() and KillTimer()

 Below is a brief description of the ways you use the nIDEvent
 parameter when calling SetTimer() and KillTimer().

 Method 1

 If you want WM_TIMER messages to be sent to your window, pass in a
 window handle for the first parameter (hWnd) and a unique value (which
 you arbitrarily choose) for the nIDEvent parameter, as follows:

    #define TIMER_ID 1       /* 1 is an arbitrary value */

    SetTimer(hWnd, TIMER_ID, wMsecInterval, NULL);
    ...
    KillTimer(hWnd, TIMER_ID);

 Method 2

 If you want one of your functions to be called rather than having a
 WM_TIMER message sent to your window procedure, call SetTimer() as
 before, but also pass in a function pointer to your timer routine; you
 must use MakeProcInstance() to get a value for this function pointer.
 The following is an example:

    SetTimer(hWnd, TIMER_ID, wMsecInterval, lpfnMyTimerProc);
    ...
    KillTimer(hWnd, TIMER_ID);

 Method 3

 An alternate method of setting a timer with your own timer function is
 to pass in NULL for the window handle. This tells SetTimer() to ignore
 the nIDEvent parameter; instead, it creates a timer ID and gives it to
 you as a return value. The following is an example:

    nTimerID = SetTimer(NULL, NULL, wMsecInterval, lpfnMyTimerProc);
    ...
    KillTimer(hWnd, nTimerID);

 When you want to kill the timer, use the nIDEvent value you passed
 into SetTimer() [or, in the third example, the value that SetTimer()
 returned to you].

 Charles Petzold's book "Programming Windows" (Microsoft Press, 1988)
 contains a good discussion on timers in Chapter 6; Pages 201-212
 discuss methods of using the SetTimer() call.


 67. Windows SDK: Freeing Versus Discarding Memory

 The following is an explanation of the difference between freeing
 memory and discarding memory.

 When you (Global/Local)Free memory, you are throwing away the memory
 block and getting rid of the handle, too; it no longer takes up space
 in any kind of handle table.

 When you discard memory, windows decrements the memory usage flag by
 one, but keeps the handle. Windows will not throw out the memory until
 someone issues a (Global/Local)Compact and the memory usage count is
 at zero. Later, you can try to do a (Global/Local)Lock on that handle.
 If you get a non-Null value back, you have a valid address to that
 memory again and you do not have to read the information back in. If
 you get back a Null address, the memory has been discarded and you
 will have to perform the following steps:

 1. Do another (Global/Local)Alloc for Global memory.

 2. Do another GlobalLock().

 3. Read the information into the memory.


 68. Windows SDK: Making Code MOVEABLE and Not DISCARDABLE

 Question:

 Can I make a block of code MOVEABLE but not DISCARDABLE?

 Response:

 No. This is a limitation in the current memory manager in that global
 movable objects whose use counts go to zero are automatically
 candidates for discarding.


 69. Device Control Block XoffLim Field Usage in Windows

 The documentation of the XoffLim field of the Communications Device
 Control Block, on Page 616 of the "Microsoft Windows Software
 Development Kit Programmer's Reference," is ambiguous. The following
 is a clarification:

 The XonLim and XoffLim values in the DCB set logical boundaries within
 the receive queue. The values specified for these fields are the
 number of bytes from the bottom of the queue for XonLim, and the
 number of bytes from the top of the queue for XoffLim. Thus if XonLim
 and XoffLim are both set to 100 in a queue of 2000 bytes, these
 boundaries are set at byte 100 and byte 1900 respectively. If the
 number of free bytes available in the queue drops below the value set
 in XoffLim (i.e. if the queue contains more than 1900 characters, in
 the example above), an XOFF is sent to stop transmission. The XON
 character is sent to restart transmission when the number of
 characters in the queue drops below the value set in XonLim (in the
 example given, if the queue contains under 100 characters). Therefore,
 transmission will always be on when the number of data bytes in the
 queue is less than XonLim, and will always be off when the number of
 free bytes left in the queue is less than XoffLim.


 70. Windows SDK: How to Set HEAPSIZE and STACKSIZE Parameters

 Question:

 How should the HEAPSIZE and STACKSIZE parameters in the .DEF file be
 set?

 Response:

 Your question relates to your application's data segment (DS), which
 cannot exceed 64K. The following three items use this space:

 1. Static data

 2. Stack space

 3. Heap space

 Static data relates to any hard-coded constants and strings, except
 those in your RC file, which go into a segment of their own.

 The HEAPSIZE will grow dynamically if it is set to greater than zero.
 Otherwise, no heap is allocated. The HEAPSIZE is determined by the
 total size of LocalAlloc(s). Start with the minimum you require. Extra
 heap will be provided automatically by Windows (up to the 64K limit).

 The STACKSIZE is determined by the number of parameters in a function
 call, and how far they become nested; factors that are difficult to
 determine, especially if you have a recursive function. You must fine
 tune your application. If you are doing library calls, you must also
 allow room for these factors because library functions use the stack
 of the caller. If you are using any GDI functions, you must have at
 least 4K.


 71. WH_CALLWNDPROC Hook: Message Structure Upside Down

 The message structure that is pointed to by the lParam in the callback
 function for the WH_CALLWNDPROC hook is upside down.

 A normal MSG structure, as defined in WINDOWS.H, has the following
 elements:

     typedef struct tagMSG {
         HWND hwnd;
         WORD message;
         WORD wParam;
         LONG lParam;
         DWORD time;
         POINT pt;
     } MSG;

 The elements in the message structure pointed to by the lParam of the
 callback function of a WH_CALLWNDPROC hook are in the following order:

     typedef struct mymsg {
         LONG lParam;
         WORD wParam;
         WORD message;
         HWND hwnd;
     } MYMSG;
     typedef MYMSG FAR *LPMYMSG;

 To use this information, define the above data types and define the
 lParam in the hook function to be of the LPMYMSG type, as follows:

 /*  this is the WH_CALLWNDPROC callback function  */
     DWORD FAR PASCAL ( nCode, wParam, lParam )
     int nCode;
     WORD wParam;
     LPMYMSG lParam; /* this reflects the correct ordering of elements */
     {
         .
         .
         .
         .
         .
         .
     }


 72. GetInputState() Is Faster Than PeekMessage()

 Question:

 Our Windows application performs some VERY intensive calculation tasks
 and a method of interrupting the task is needed. Is there is a faster
 way of checking to see if there is a message other than
 Get/PeekMessage()?

 Response:

 To quickly determine if there are any keyboard or mouse messages for
 your application without having to do a Get/PeekMessage() call, you
 can use the GetInputState() function. It will return TRUE if either a
 keyboard or mouse event has occured. If you need to distinguish
 between a mouse or a keyboard message, the return value will be 2 for
 the keyboard and 1024 for the mouse.

 There are problems if your application loses the focus, so only use
 this function under tight loop conditions.


 73. Windows SDK: Shutting Down Windows from within an Application

 Question:

 We need to be able to shut down Windows from within an application. Is
 there a message that an application can send to the MS-DOS Executive
 that will cause it to shut down?

 Response:

 You can shut down the entire system by sending the MS-DOS Executive a
 WM_SYSCOMMAND message using PostMessage(). The wParam of this message
 should be the predefined value SC_CLOSE.


 74. Windows SDK: Creating Fonts for Write

 Question:

 Is it possible to create fonts for Write? I know that Write queries
 the printer driver for its properties and fonts in order to be
 WYSIWYG. Each font we have created can be used with Paint, but is
 unknown for Write.

 Is it possible for an ISV to create supplemental fonts and use them
 with Write?

 Response:

 Write uses a rather picky algorithm for choosing its fonts. It tries
 to match "corresponding" fonts on the display and the printer. Its
 algorithm is tuned for the Windows base fonts TMSR, COUR, and HELV. It
 tries to pick fonts that will appear to be the same physical size on
 both the display and the printer, and that match the aspect ratio of
 each device. In general, it's extremely difficult to get Write to
 accept new fonts. At a minimum, the fonts must be designed in pairs:
 one for each device. This also implies that you must build font pairs
 for the CGA, EGA, VGA, etc.


 75. MDI Interface in Software Library

 Microsoft has placed sample code to help you with the MDI interface in
 the Software Library.

 We have placed the executable and all dependent files in the archive
 format file MDI.ARC. The source code is provided by Microsoft as an
 example only. We do not claim the code is error free or complete.

 This file can be found on the Software Library by searching for
 MDI.ARC, the Q number of this article, or S10064.


 76. DeviceMode and Fatal Exit 406

 Problem:

 We are having a problem accessing DeviceMode for the PostScript driver.

 Our production application usually gets "FatalExit406, bad module
 handle." The sample application gets RIP0007 or "FatalExit140."

 We present the user with a list box containing the printer names and
 ports from [devices]. PSCRIPT is the only driver with which we are
 having trouble; CONTROL and other applications access PSCRIPT with no
 problems.

 Response:

 The system is crashing deep inside the CreateDialog code when it tries
 to create a window for an edit item. This type of window causes space
 to be allocated from the PostScript driver's local heap, which
 apparently is not being set up correctly.

 A workaround is to make sure the PostScript library module is loaded
 before doing a CreateIC or CreateDC. The following code fragment
 demonstrates how to do this:

    hModule = GetModulehandle((LPSTR) "pscript");
    if (!hModule)
    hModule = LoadLibrary((LPSTR) "pscript.drv");
    if (hModule < 32)
    {
    Error();
    return;
    }


 77. HEXCALC.ARC: an Instructive Pop-Up Calculator for Windows/PM

 The following is a summary of the HEXCALC program found in the
 "Microsoft Systems Journal" and in the Software Library:

    Issue: January 1988 Vol. 3 No. 1 Page 39
    Author: Charles Petzold
    Filename: HEXCALCW.C and HEXCALCP.C

 HEXCALC.ARC contains both of these files and can be found in the
 Software Library by searching for the filename HEXCALC.ARC, the Q
 number of this article, or S10026. HEXCALC.ARC has been archived with
 PKARC so you will need to unarchive it with PKXARC. The PKXARC utility
 is located on your OnLine Utilities Disk 2.

 HEXCALC is a pop-up window with 29 child window push-button controls.
 It is a ten-function infix-notation hexadecimal calculator with a full
 keyboard and mouse interface. It works with 32-bit unsigned long
 integers and does addition, subtraction, multiplication, division,
 remainders, bitwise AND, OR, and Exclusive-OR, as well as left and
 right bit shifts.

 A Windows version (HEXCALCW.C) and a Presentation Manager version
 (HEXCALCP.C) of the program are included for comparison.


 78. Windows SDK: GetBitmapBits() Format

 Question:

 What is the format of lpBits returned by GetBitmapBits()? How do
 BITSPERPIXEL and PLANES affect the arrangement of the returned data?

 Response:

 The format of lpBits is device dependent for colored devices. For
 monochrome devices, please refer to the bitmap structure on Page 265
 of the "Microsoft Windows Software Development Kit Programmer's
 Reference" manual.


 79. C Version 5.10 Run-Time Library Functions That Call malloc()

 The following C run-time routines call malloc, either directly or
 indirectly:

 <startup code if linked with SETARGV.OBJ to get wildcard expansion>
 strdup()
 getcwd()
 searchenv()
 putenv()
 tempnam()
 calloc()
 realloc()
 freect()
 setvbuf()
 getc(), getchar(), fgetc(), fgetchar(), gets(), fgets(), getw()
 ungetc()
 putc(), putchar(), fputc(), fputchar(), puts(), fputs(), putw()
 fread(), fwrite(), fseek(), fsetpos()
 printf(), fprintf(), sprintf(), vprintf(), vfprintf(), vsprintf(),
 scanf(), fscanf(), sscanf(), vfscanf(), vscanf(), vsscanf()
 execv(), execve(), execvp(), execvpe(),
 execl(), execle(), execlp(), execlpe(),
 spawnv(), spawnve(), spawnvp(), spawnvpe(),
 spawnl(), spawnle(), spawnlp(), spawnlpe(),
 stat()
 system()

 This list is valid for C Version 5.10.


 80. Windows SDK: Retrieving Solid Device Colors

 Question:

 How do I retrieve the RGBs of all of the solid colors available on the
 current system display?

 Response:

 Windows does not provide an easy way to do this; to retrieve the RGBs
 of all of the solid colors available, do the following:

 1. Use EnumObjects() to enumerate the pens.

 2. Check the logical pen structure in your enumeration function for
    SOLID pens and save their RGB values. The total number of saved
    RGBs should be the same as the number of colors supported by the
    device found by using the GetDeviceCAPS() function.


 81. Keeping Menu Items Visible

 Question:

 How can I keep my pop-up window from moving down the screen so any
 pulled-down menu items are not cut off?

 Response:

 Examine the WM_MOVE message to make sure it is within the size of the
 screen. Add the length of the longest pop-up menu and make sure it
 does not exceed the screen length. The following sample code can be
 inserted into the sample Hello source code; it shows how to use the
 MoveWindow function:

 Replace the HELLO.RC files About style with the following:

 STYLE  WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX

 Replace the HELLO.C file's About procedure with the following:

 BOOL FAR PASCAL About( hDlg, message, wParam, lParam )
 HWND hDlg;
 unsigned message;
 WORD wParam;
 LONG lParam;
 {
     BOOL   bBadMove;
     RECT   WindowRect;
     int    nRealX, nRealY;
     int    nWidth, nHeight;

     switch (message) {
        case WM_COMMAND:
           EndDialog( hDlg, TRUE );
           return TRUE;
        case WM_INITDIALOG:
           return TRUE;
        case WM_MOVE:
             bBadMove = FALSE;
             GetWindowRect(hDlg, (LPRECT)&WindowRect);
             nWidth = WindowRect.right - WindowRect.left;
             nHeight = WindowRect.bottom - WindowRect.top;
             if ((nRealX = WindowRect.left) < 0) {
                 bBadMove = TRUE;
                 nRealX = 0;
             }
             if ((nRealY = WindowRect.top) < 0) {
                 bBadMove = TRUE;
                 nRealY = 0;
             }
             if (WindowRect.right > GetSystemMetrics(SM_CXSCREEN)) {
                 bBadMove = TRUE;
                 nRealX -= (WindowRect.right - GetSystemMetrics(SM_CXSCREEN));
             }
             if (WindowRect.bottom > GetSystemMetrics(SM_CYSCREEN)) {
                 bBadMove = TRUE;
                 nRealY -= (WindowRect.bottom - GetSystemMetrics(SM_CYSCREEN))
             }
             if (bBadMove)
                 MoveWindow(hDlg, nRealX, nRealY, nWidth, nHeight, TRUE);
             break;
             return TRUE;

       default:
           return FALSE;
     }
 }

 The above code produces the following modifications:

 1. The About's style is modified from being a dialog frame to being
    one with a caption bar in the RC file. This means it can be moved
    around.

 2. The About procedure is modified to check the WM_MOVE.


 82. Selecting a Command from the Top-Level Menu Bar

 Question:

 How can I have my application execute a command when a top-level menu
 item is selected instead of displaying a pop-up menu?

 Response:

 In the menu definition in your RC file, change the directive for the
 top-level item from POPUP to MENUITEM. Then, when the mouse is used to
 click the item in the menu bar or the keyboard shortcut is used, the
 WM_COMMAND message will be sent for that command.

 The following portion of the RC file for the sample SHAPES application
 has been modified so that the clear menu item is on the top level:

    shapes MENU
    BEGIN
      POPUP "Shape"
        BEGIN
          MENUITEM "Ellipse\t^E"  , IDDELLIPSE
          MENUITEM "Rectangle\t^R", IDDRECT
          MENUITEM "Star\t^S"     , IDDSTAR
          MENUITEM "Triangle\t^T" , IDDTRIANGLE
        END
      MENUITEM "Clear  ^C"    , IDDCLEAR
    END

 Note: The "\t" is taken out of the line so that it does not tab.

 For more information, see Pages 36-40 of the "Microsoft Windows
 Software Development Kit Programmer's Reference" manual.


 83. Shrinking Heap Space

 Question:

 I understand that when an application does a LocalAlloc() and there is
 not enough memory within the application's data segment, Windows will
 grab memory from the global heap to append this to the application's
 data segment. How do you then return the memory that was temporarily
 requested? The following is an example:

 1. Initial HEAPWALK shows free 12000.

 2. After a 4K LocalAlloc() and LocalLock(), HEAPWALK shows lock 4000
    and free 8000.

 3. If the program then does the same for another 10K piece, HEAPWALK
    shows lock 4000, free 8000, and lock 10000.

 4. If the program then deallocates the 4K and 10K blocks with unlock
    and free, HEAPWALK shows free 12000 and free 10000.

 How do I release the second free 10K block?

 Response:

 You may use the LocalShrink() function to compact and shrink the data
 segment to the smallest size possible. LocalShrink() can't move FIXED
 or locked blocks when compacting the local heap. Therefore, there may
 still be free space in the heap, and the size of the heap may not be
 as small as requested after calling LocalShrink(). However, this
 function will compact as much as possible, given this constraint, and
 in its attempt to reduce the local heap to the size requested.


 84. Error Message: "Cannot Reuse String Constants" in RC.EXE

 The error "Cannot Reuse String Constants" will be returned by the
 Resource Compiler if you have used the same ID value to define two
 different string constants.

 For example, the following error is returned when compiling the file
 MY.RC:

    MY.RC... Error in line 70 - Cannot Reuse String Constants
    1 error detected

 The file MY.RC may contain the following lines:

    StringTable
    begin
       1, "one"
       2, "two"
       3, "three"
       1, "four"
    end

 Note that "one" and "four" have the same value. This error may be less
 noticeable if you are using both decimal and hexadecimal notation in
 your RC file. In the following example, 0x010 and 16 have the same
 value and generate the error:

     0x010, "hex 10"
     10, "ten"
     11, "eleven"
     15, "fifteen"
     16, "sixteen"


 85. Windows SDK: WIN.INI Switches for Debugging

 You may use the following switches for debugging in the WIN.INI file:

    [kernel]
    EnableHeapChecking = [0|1]
    EnableFreeChecking = [0|1]
    FreezeGlobalMotion = [0|1]
    LRUSweepFrequency  = N milliseconds
    EnableSegmentChecksum = [0|1]

 For more information, refer to Pages 656-657 of the "Microsoft Windows
 Software Development Kit Programmer's Reference." Also, refer to other
 articles in this database by querying on the above switch names.


 86. Windows SDK: Printing Text Only

 Question:

 What is the "normal" suggested method to print text reports under
 Windows? I currently have a print routine for graphics that can use
 banding. If I am to use this routine, should I band the output, too?
 Is there an easier method to print text reports?

 Response:

 Microsoft GDI is oriented toward high-quality graphics output. As
 such, all text produced within the Windows environment is graphical in
 nature, so that proportional, even text is output. GDI does not have
 any simple, line-oriented text system; in particular, note that there
 is no special meaning to control codes within strings. GDI does not
 respond to <CR><LF> in any way.

 As a result, there is no simple, text-only, output through GDI. You
 could easily write a library that performed such output, but there is
 no direct support within Windows/GDI for such output.


 87. Deselecting a List Box in Place

 Question:

 Sending message LB_SETCURSEL with wParam equal to -1 deselects the
 current selection and scrolls the box to the top. Is there a way to
 deselect and not scroll to the top?

 Response:

 The following program segment shows the best way to do this:

    pos = GetScrollPos(hwndListbox);
    SendMessage(hwndListbox, WM_SETREDRAW, FALSE, 0L);
    SendMessage(hwndListbox, LB_SETCURSEL, -1, 0L);
    SendMessage(hwndListbox, WM_VSCROLL, SB_THUMBPOSITION, (long)pos);
    SendMessage(hwndListbox, WM_SETREDRAW, TRUE, 0L);
    UpdateWindow(hwndListbox);


 88. How to Create a Menu-Bar Item Dynamically

 Sometimes it may be desirable to add and delete menus within an
 application dynamically, rather than defining the menu in the .RC
 file.

 Menus can be created, altered, and deleted using the following
 functions:

 1. CreateMenu() creates an empty menu (menu bar) which can then be
    added to.

 2. ChangeMenu() is used to append, insert, delete, and make other
    changes to the menu (menu bar) and/or its pop-up menus.

 3. SetMenu() sets a given window's menu to the one specified.

 4. DrawMenuBar() redraws the menu for the specified window.

 Therefore, use CreateMenu() to create your menu, use ChangeMenu() to
 add your menu items, use SetMenu() to attach this menu to your window,
 and call DrawMenuBar() to display the menu.

 The following code example should be put in the sample Hello
 application in the HelloWndProc procedure (add to HELLO.C):

 #define HI_MENU     100
 #define THERE_MENU  101

  BOOL  added_hi = FALSE;
  BOOL  added_there = FALSE;
  HMENU hMenu;

  case WM_CHAR:
      if (!added_hi) {
        hMenu = CreateMenu();
        ChangeMenu( hMenu, NULL, (LPSTR)"Hi", HI_MENU, MF_APPEND );
        SetMenu( hWnd, hMenu);
        DrawMenuBar(hWnd);
        added_hi = TRUE;
      }
      break;
  case WM_COMMAND:
       switch (wParam) {
         case HI_MENU:
          if (!added_there) {
              hMenu = GetMenu(hWnd);
              ChangeMenu( hMenu, 1, (LPSTR)"There", THERE_MENU, MF_APPEND;
              DrawMenuBar(hWnd);
              added_there = TRUE;
           }
           break;
         case THERE_MENU:
           MessageBox (hWnd, (LPSTR)"no more menus allowed",
                            (LPSTR)"Menu Test", MB_OK );
           break;
         default:
           return(DefWindowProc(hWnd, message, wParam, lParam));
           break;
       }
       break;

 Once you press a key, you will get a WM_CHAR message that will add a
 menu item to the main menu bar. If you click the "Hi" menu item, you
 will get a WM_COMMAND message that will then generate another menu
 item called "There." Click the "There" menu item and you will get a
 message box.


 89. Scroll Bars and Edit Controls

 Question:
    How can I keep my scroll bar in sync with the edit control, given
 the following problem?
    I have an edit control (created using CreateWindow with class
 "Edit" and additional styles ES_MULTILINE and ES_AUTOVSCROLL). I have
 a scroll bar sibling to the edit control. If I click the down arrow
 of the scroll bar, the parent gets a WM_VSCROLL message. The parent
 window then passes the WM_VSCROLL message to the edit control (by
 means of SendMessage), and the edit control scrolls down one line as
 expected.
    However, if I hold the mouse button down in the down arrow of the
 scroll bar until five WM_VSCROLL messages have been sent to the
 parent, the edit control often scrolls down by more than five lines,
 even though only five WM_VSCROLL messages were passed to it.
    This is a problem because I have adjusted my scroll bar's thumb
 position by only five lines. It is now out of sync with the edit
 control. (If you run TEMPLATE.EXE, line 14 should never scroll
 higher than the last line in the edit control. In this example I have
 assumed that each line is 14 units high when I calculated the scroll bar
 range.)

 Response:
    You can generate an application from the HELLO sample source and
 the given code that produces a scrollable edit control in the About
 dialog box. The changes you will need to make to HELLO are as follows:

    1. Replace the ABOUTBOX dialog in HELLO.RC with the given dialog.
    2. Add the #define'd identifiers to the HELLO.H file.
    3. Add the global variables to HELLO.C.
    4. Replace the dialog function (About) in HELLO.C.

    In general, to avoid the miscounting (multiline edit controls have
 various strange behaviors), do not make the edit control do any
 counting. The parent should handle the scrolling directly by resetting
 the text in the edit item. The following example shows some of this.
    The read-only behavior you want will require that your parent
 window watch the mouse, probably capture it, and keep it out of
 the edit control.
    Your final question is answered on Page 533 of the Version 2.00
 "Software Development Kit Programmer's Reference" guide. The EM_SCROLL
 simply is not implemented for multiline edit controls. The following
 is sample code:

    /* GLOBALS */
    HWND    vScroll, ViewWind;
    int POSMIN = 1;
    int POSMAX = 10;
    int posV   = 0;
    int pos    = 0;
    char *temp;
    char *fred = {"This is an example of a *scrollable edit control
    window. As you move the scroll bar the text is automatically
    scrolled within the edit control."};

    /* DIALOG FUNCTION */
    BOOL FAR PASCAL About( hWndDlg, message, wParam, lParam )
    HWND hWndDlg;
    unsigned message;
    WORD wParam;
    LONG lParam;
    {
    HWND hwndSB;

    switch(message)
         {
          case WM_INITDIALOG:
            ViewWind = GetDlgItem(hWndDlg,IDVIEW);
            SetDlgItemText(hWndDlg, IDVIEW, (LPSTR)fred);
            vScroll = GetDlgItem(hWndDlg,IDDSBV);
            SetScrollRange(vScroll,SB_CTL,POSMIN,POSMAX,TRUE);
            SetScrollPos(vScroll, SB_CTL, posV, TRUE);
            pos = posV;
            return TRUE;

          case WM_COMMAND:
            switch (wParam)
            {
            case IDOK:
                EndDialog(hWndDlg, TRUE);
                break;

             default:
                break;
            }
             return TRUE;

        case WM_VSCROLL:
            posV = pos;
            hwndSB = HIWORD(lParam);
            switch (GetMenu(hwndSB))
            {
            case IDDSBV:
                pos = GetScrollPos(hwndSB, SB_CTL);
                switch (wParam)
                {
                case SB_LINEUP:
                    pos -=  LINEINC;
                    break;
                case SB_LINEDOWN:
                    pos +=  LINEINC;
                    break;
                case SB_PAGEUP:
                    pos -= PAGEINC;
                    break;
                case SB_PAGEDOWN:
                    pos += PAGEINC;
                    break;
                case SB_THUMBPOSITION:
                    pos = LOWORD(lParam);
                    break;
                case SB_TOP:
                    pos = POSMIN;
                    break;
                case SB_BOTTOM:
                    pos = POSMAX;
                    break;
                }
                pos = max(POSMIN, min(POSMAX, pos));
                SetScrollPos(hwndSB, SB_CTL, pos, TRUE);
                posV = pos - posV;
                temp = &fred[0] + (13 * (GetScrollPos(hwndSB, SB_CTL)-1));
                SetDlgItemText(hWndDlg, IDVIEW, (LPSTR)temp);
                break;

            default:
                return FALSE;
            }
             return TRUE;

          default:
             return FALSE;
         }
    return TRUE;
    }

    /* DIALOG */
    ABOUTBOX DIALOG LOADONCALL MOVEABLE DISCARDABLE 34, 76, 118, 170
    STYLE WS_DLGFRAME | WS_POPUP
    BEGIN
         DEFPUSHBUTTON "OK"   IDOK,   35, 132, 40, 26,  WS_TABSTOP |   WS_CHIL
         CONTROL "" IDDSBV, "scrollbar",SBS_VERT | WS_GROUP  | WS_TABSTOP
                                                 | WS_CHILD, 92, 15, 8, 79
         CONTROL "" IDVIEW, "edit", ES_LEFT | ES_MULTILINE | WS_BORDER
                                            | WS_CHILD, 35, 15, 55, 79
    END

    /* IDENTIFIERS */
    #define IDVIEW    0x1001
    #define IDDSBV    0x1002

    #define LINEINC    0x0001
    #define PAGEINC    0x0005


 90. The

 The [minalloc] SEGMENTS statement option described on Page 81 of the
 "Microsoft Windows Software Development Kit Programming Tools" manual
 Versions 2.00 and 2.10 is not supported in Version 5.01.17 of LINK4.EXE,
 which is shipped with Versions 2.00 and 2.10 of the Windows SDK. Any
 attempts to use this option will result in a syntax error.


 91. Windows SDK: Changing the System Font Size

 Question:

 We are building a monochrome high-resolution (1280 x 1024) Windows
 driver for our display controller board and we need to use a larger
 system font than the default. We are presently using an 8 x 8 hardware
 font, which is difficult to see at that resolution.

 How do we tell Windows we would like to use our own 8 x 16 hardware
 font so that it can size menus, etc., appropriately?

 Response:

 At start-up, GDI will ask the display driver if it has fonts. In
 particular, it will look for fonts suitable for use as the "system"
 font and "OEM" font. You must provide font information blocks about
 those fonts at EnumFonts() time. Thus, you can use your hardware font
 and report the actual pixel sizes to GDI, which will keep that
 information for use by anyone who needs the system or OEM fonts.

 NOTE: You must be able to draw that same font (or any fonts you
 provide yourself within your device driver) onto main-memory bitmaps.
 GDI has no notion of some fonts being used on the (dedicated) frame
 buffer and other fonts being used on host-memory bitmaps. Often, this
 requires that you read your hardware font off the board and into host
 memory where your display driver can move the bitmaps onto compatible
 bitmaps allocated by GDI. Within these guidelines, and with that
 provision, you are free to provide the system, OEM, and as many
 additional fonts as you can acquire (say, from Bitstream, Xiphias, or
 Conographics).


 92. Windows SDK: Dynamic Links to Discardable Code

 Question:

 How can I be sure that LONG calls from my main code segment properly
 access the entry point of functions in dynamic code segments that may
 have been loaded at variable positions or moved?

 Response:

 The same dynamic link, used on dynamic libraries, is used on your
 EXPORTed functions. The -Gw flag causes the compiler to generate
 Windows prolog/epilog code for all functions and is required for all
 exported functions. The -Gw switch and all compiler switches affect
 that entire compile.

 When creating discardable code segments, you should use the following
 rules:

 1. Always use the -Gw compiler switch.

 2. All entry points to this module should be typed FAR PASCAL.

 3. All entry points must be EXPORTed.

 4. All entry points that will require access to global variables and
    constant space, must be called from a long pointer returned from a
    MakeProcInstance() call.


 93. Windows SDK: Usage of DEVICEDATA Escape Parameter

 Question:

 Is the DEVICEDATA escape useful for printing text directly to the
 printer without having to use the device driver?

 Response:

 The term "#define DEVICEDATA 19" was previously called PASSTHROUGH but
 was later renamed. Using the DEVICEDATA escape, which is essentially a
 "pass-through" to the device, will make your application absolutely
 device dependent.

 The primary use of the DEVICEDATA escape is to download information
 that needs to be in the printer (but that you know is not there), or
 to set printer state. The DEVICEDATA escape will not interact with the
 GDI in a reasonable way, but it allows device-driver writers to
 provide escapes directly into a printer for functions supported there.
 In particular, if you set the state in a printer to something other
 than what GDI thinks it to be, the results could be unpredictable. For
 example, you could do a simple dump of text strings to a device.
 However, you must be sure that the printer is in text mode (i.e., not
 in graphics mode) before you attempt this procedure.

 A better way to perform a "quick and dirty" print is to set the
 requested print quality to Draft. Time-conscious drivers would
 probably use intrinsic fonts for draft-quality printing (we will make
 no assertion that there are any drivers out there that actually do
 this; it is part of the semantics of draft-quality printing).

 To summarize, the DEVICEDATA escape is a simple way to send a string
 of bytes to the device, but not a simple way to print on the device.
 It is device-dependent as to whether those bytes get printed, are
 interpreted as a downloaded font, reset printer characteristics, etc.
 The DEVICEDATA escape is not a substitute for using the GDI interface.


 94. Windows SDK: Why Dialog Boxes Do Not Get WM_CREATE Messages

 Question:

 Why don't dialog boxes receive a WM_CREATE message?

 Response:

 Microsoft Windows dialog boxes are not child windows in the
 traditional sense of a system with nested, hierarchical windows. In
 traditional systems, child windows are expected to receive every
 message that would be sent to a parent.

 Microsoft Windows dialogs share many attributes of child windows in
 such traditional systems, but differ in that they include a data
 structure that specifies how to decorate their screen area with
 various "controls" (note that the dialog does create a new window,
 which it decorates with those controls). This creation is at the heart
 of the CreateDialog() and DialogBox() calls, so Microsoft Windows
 dialogs are, in a very real sense, created by Windows for the client
 application. Once Windows dialogs are created, messages for the window
 are passed to the procedure specified in the call that creates the
 dialog.

 Dialog procedures do get a message at create time--the INITDIALOG
 message. This message is sent to the dialog procedure (through the
 creating task's input-polling loop) during dialog-box creation, but
 before the dialog window is created or made visible. Do not make calls
 while processing this message except housekeeping-type calls (such as
 setting global flags in the application to indicate that the dialog
 box is present). In particular, because the dialog's window is not yet
 created, do not do anything to that window.

 Another aspect of this paradigm will help explain why messages are not
 automatically sent arbitrarily into a hierarchy of "child" windows.
 Because Microsoft Windows child windows are simple, cheap, shortcuts
 to decorating your client area (as controls are predefined ways to
 decorate a fill-in form), notification of interesting events (such as
 "parent moved") are not transmitted to grandchildren. Windows will
 check for child windows, and notify a window's children that they
 should move themselves, but it does not check for grandchildren.
 Parent windows that create children, that also create child windows,
 are responsible for managing those grandchildren (and any more distant
 descendants) because of Microsoft Windows' conservative notion of
 inheritance. This keeps the implementation manageable.


 95. Windows SDK: Multitasking with NetBIOS

 Question:

 Does Windows implement an INT 15H handler to facilitate multitasking
 in the presence of a NetBIOS network? If not, will it implement this
 feature in the future?

 Response:

 No, Windows does not use the INT 15H handler at all. INT 15H is
 specific to the IBM AT. Windows will never use an INT 15H handler
 because it is designed to work on a number of different OEM machines.


 96. Speed Differences Among win /3, win /2, and win /r

 Question:

 Why do both win /3 and win /r run slower than win /2?

 Response:

 Win /3 is slower than win /2 because it runs in 80386 protected mode
 and virtualizes all devices and I/O operations. Win /3 also provides
 demand-paged virtual memory using the hard disk to swap paged-out RAM;
 this feature involves extra validity checking and disk swapping when
 page faults occur, which takes some extra time.

 Win /r is slower because it has limited memory space (the 640K maximum
 provided by MS-DOS) and therefore must perform a lot of moving and
 discarding of code and data segments when the memory space is
 "overcommitted" by the system and running applications.

 Win /2 is fastest because, although it runs in the protected mode of
 the 80286 and is somewhat slowed down by that, it has MUCH more memory
 available for applications to run in, so there is less moving and
 discarding of code and data segments. The speed gains of win /2
 compared to win /r depend on how much extended memory is on your
 machine and how much is needed by the system and all running
 applications; where win /r would have to discard and move some
 segments to make room for a new application to run, win /2 simply
 allocates more of the global heap which consists of all available
 extended memory.

 Some of these factors apply to Windows 2.x (2.03, 2.10, 2.11) as well
 as Windows 3.00. For example, Windows/386 Version 2.x runs in
 protected mode and virtualizes devices and I/O, which slows things
 down. However, Windows/286 Version 2.x runs in real mode and gains
 speed from that. On the other hand, Windows/386 Version 2.x provided
 LIM 4.0 EMS to applications, which allowed more applications to fit in
 memory at the same time; however, Windows/286 doesn't provide its own
 EMS memory for applications, instead relying on an already-installed
 EMS board or "LIMULATOR" to provide it. If the system doesn't have any
 EMS memory, Windows/286 is forced to discard and move memory more
 often.


 97. Windows SDK: Sharing File Handles

 Problem:

 I created a program that initializes a file with the OpenFile()
 function using the OF_CREATE|OF_WRITE options, and receive the handle
 x'0005'. I then allocate global memory and place that handle into the
 global memory, and use SetClipboardData() using a format returned by
 RegisterClipboard().

 Another task opens the Clipboard and retrieves the handle correctly,
 but when it tries to write to the handle, MS-DOS returns AX=0000,
 indicating that it did not write information.

 Upon return to MS-DOS out of Windows, the directory confirms a
 zero-length file.

 Response:

 Applications cannot share file handles. This is a feature of the DOS
 filing system.

 The file handle table is part of each application's environment; the
 file handle itself is an offset into this table. Although an offset of
 5 might be valid for two applications, the file information at that
 offset in the respective handle tables will be very different.

 We recommend passing the filename to the second application.


 98. Windows SDK: Using BringWindowToTop() without Changing Focus

 Question:

 How can I make a particular window the top-most window in a pile of
 pop-up windows without changing the focus? The BringWindowToTop()
 function seems to put the focus on whichever window was most recently
 brought to the top.

 Response:

 This feature is by design. To change the focus, you will have to issue
 a SetFocus() call immediately after the BringWindowToTop().
 Also, you can instead call:
 SetWindowPos(hWnd,NULL,0,0,0,0,
     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);


 99. Windows SDK: BitBlt() from Monochrome to Color Bitmap

 Question:

 The documentation for BitBlt() states that when blting from a
 monochrome bitmap to a color bitmap, the "black" bits will use the
 background color, and the "white" bits the foreground color. I
 understand that the background will be the destination, but what is
 meant by the foreground color?

 Response:

 The background is the background color. The foreground is the given
 text color. Both are set by GDI functions in the target DC.


 100. Windows SDK: Resources in Windows Libraries

 Question:

 I am writing a library application in which I would like to have
 strings defined in a resource file for translation purposes. Are
 libraries allowed to have resource files with STRINGTABLEs, menus,
 dialog boxes, etc.? I wrote a function that is called from the library
 application entry-point routine, which loads a string from the
 resource file and then puts up a message box using that string. Is it
 all right to do this?

 Response:

 Yes, Windows libraries can have resources bound into the executable
 just as a Windows application can. The handle to the library module
 should be used as the handle to the instance when referencing the
 bound resources.


 101. Windows SDK: Freeing Global Memory and Resources

 Problem:

 I have allocated global memory. However, when I quit my application,
 the memory is not freed.

 Response:

 Your application is allocating global memory, but not freeing the
 memory before it terminates. It is required that all applications free
 their global resources before terminating. Global resources include
 memory, pens, brushes, regions, bitmaps, etc.


 102. How to Detect If You Are Running under Windows/386

 Question:

 We need a mechanism (for example an interrupt call) that will always
 tell us when we are running under Windows/386. What can you suggest?

 Response:

 There is an article in the Software Library that contains information
 on ways in which programs can properly interact with Windows/386. This
 file can be found in the Software Library by searching for the file
 386API.ARC, the Q number of this article, or S12024.

 For your convenience, the following is the relevant section:

 Windows/386 Version 2.10 API

 Detecting Windows/386

 If a program intends to use the Windows/386 API it must first make
 sure that Win386 is running. To do this, issue the following:

                 mov             ax, 1600h
                 int             2Fh
                 cmp             al, 80h
                 je              Not_Running_Win386
                 test            al, al
                 jz              Not_Running_Win386
                 (Otherwise win386 is around)

 If 0 or 80h is returned in AL, Windows/386 is not running. Any other
 value means that Win386 is running. Do not rely on a specific value
 being returned. Any value other than 0 or 80h means that Windows/386
 is running. If you need to determine which VM the program is running
 in you should get the current VM ID.


 103. Documentation Error; Usage of WNDCLASS Style: CS_SAVEBITS

 Question:

 Page 634 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" states that there is a "style" type of
 CS_SAVEBITS defined. If I set this style, does this mean I do not have
 to maintain my own off-screen bitmap? How do I access the bitmap that
 is saved?

 Response:

 The documentation is slightly misleading as it is currently worded. If
 the CS_SAVEBITS style is included when registering a POPUP window, a
 bitmap copy of the screen image that the window will obscure is saved
 in memory when a window is displayed.

 The bitmap will be redisplayed at its original location and no
 WM_PAINT messages will be sent to the obscuring windows if the
 following is true when the window is removed from the display:

 1. The memory used by the saved bitmap has not been discarded.

 2. Other screen actions have not invalidated the image that it is
    storing.

 As a general rule, you should not set this bit if your window will
 cover more than half the screen; a lot of memory is required to store
 color bitmaps.

 The window will take longer to be displayed because memory needs to be
 allocated. The bitmap also needs to be copied over each time the
 window is shown.

 Use should be restricted to small windows that come up and are then
 removed before much other screen activity takes place. Any memory
 calls that will discard all discardable memory, and any actions that
 take place "under" the window, will invalidate the bitmap.


 104. MessageBox, Focus, and Windows Libraries

 Question:

 I am writing a library application in which I would like to put up
 messages using the Window's MessageBox function. It seems to work, but
 I have never been told it is all right to do this. I am passing the
 MessageBox function a NULL window handle as the window to receive
 focus when the MessageBox function returns. Are library applications
 allowed to call MessageBox?

 Response:

 Yes, you can create a message box from a Windows library. The best way
 to use this function is to get a handle to the window that currently
 has the focus and call MessageBox with that handle as the parent. When
 the focus is restored, the window that had focus prior to the
 MessageBox call will regain the focus. The following is an example:

    MessageBox(GetFocus(),(LPSTR)"Demonstration",(LPSTR)"DEMO",MB_OK);


 105. Zooming Other Applications in Windows

 It is possible for one application to zoom, or maximize, another
 application. You first need to find the handle to the application's
 window by using the EnumWindows() function. Then use the PostMessage()
 function to post a message to that application's message queue. An
 example of the PostMessage() call is as follows:

    if(fWindowToZoom)
       PostMessage(hWndToZoom,WM_SYSCOMMAND,SC_ZOOM,0L);

 This code will ask a non-Windows application to zoom, as well as
 asking any Windows applications to zoom.


 106. Path Searching Order for LoadLibrary

 Question:
    Can you tell me the order of paths in which Windows will search for
 a dynamic-link library named by the LoadLibrary(...) function? For
 example, if I call LoadLibrary( (LPSTR)"FOO.LIB" ), where can I place
 the file FOO.LIB and Windows so that I can find them later?

 Response:
    The LoadLibrary() function will follow the path variable in the
 same manner that the OpenFile function follows the environment
 variables (i.e.) it will look in the following locations:

   1. The current directory
   2. The location that WIN.COM was executed from
   3. The path variable


 107. Windows SDK: Windows and Real Time

 The Windows package does not support real-time programming.

 In no sense can Microsoft Windows be considered a "real-time" system.
 It is a message-driven, event-polling system, with nonpreemptive
 scheduling. The following is additional information:

 1. It is possible to write a Windows application that sits on some
    interrupt in order to watch board-level activity. In general, this
    facility is used by manufacturers of boards to write drivers that
    watch and respond to interrupts used by the board. It is extremely
    dangerous to allocate/sit on any interrupt used by Windows itself
    (keyboard, mouse, etc.).

 2. Dedicated systems (those that will not run general-purpose Windows
    applications) may sit on the timer interrupt, as long as Windows
    is eventually notified of the timer ticks. Because of the
    nonpreemptive aspect of the Windows system, it is insensitive to
    delays in the arrival of timer ticks. Trying to run more than one
    time-critical application that sits on the timer interrupt is
    likely to fail.

 3. You must arrange for a Windows "library" of routines to provide
    access to information available from a board. The issue is one of
    how to divide the work between the following portions:

    a. The driver portion, which watches the interrupt, logs data, and
       notifies clients [through PostMessage()] of the availability of
       data

    b. The client portion, which obtains data from the driver portion,
       and processes, displays, and stores data

    Drivers may be implemented as Windows libraries, and clients of the
    device may be implemented as Windows applications. The driver
    portion can be made NEAR real time; the application portion is
    going to be message driven.


 108. Windows SDK: Blink/Intensity for EGA Colors

 Question:

 Is there some way to get blink and high/low intensity colors for text
 in Windows using an EGA board? (I have installed Windows with the EGA
 > 64K option--do I need to do something special as regards drivers?)
 Using SetTextColor(), I can only get eight colors and can think of no
 way to blink text without using InvertRect() and a timer.

 Response:

 You are doing everything correctly, but the Windows EGA driver only
 supports eight colors. The normal blinking that occurs is usually a
 hardware feature of the TEXT modes. Windows does not provide an easy
 way to blink text without using InvertRect() and a timer.


 109. Running Non-Windows Applications in a Window

 Question:

 How can I run an old application using a smaller font so I can see the
 entire screen in a smaller window? Is it also possible to run some old
 applications that use the EGA 43-line mode in a window?

 Response:

 To run with a smaller font on an EGA-type display, you can substitute
 the LOFONT.FON file for the FONTS.FON file in your Windows start-up
 directory. This works, though there's some degradation in appearance
 of dialog-box controls (the top and bottom scanlines of radio buttons
 and check boxes are lost) for ALL your Windows applications. This gives
 more lines in ALL the system-drawn items (title bars, menu bars,
 shell's directory listing, etc.). It also allows your old applications
 using the ANSI driver to show up in smaller windows.

 The second part of your question is not possible: the 43-line mode of
 the EGA is not supported in the standard EGA BIOS, and is NOT readable
 from the board. As a result, WINOLDAP cannot save or restore
 applications running in 43-line mode. This should not apply to running
 old applications within large or "character-dense" windows on OTHER
 displays, but the terminal emulator portion of WINOLDAP does not
 currently allow other display sizes. This feature is under review and
 will be considered for inclusion in a future release.

 NOTE: When using EDLIN with a Genius display in a large window, the
 display DID use more than 25 lines, and scrolled up as needed. However,
 EDLIN is a line-oriented application; we do not believe that WINOLDAP
 understands the ANSI sequences to set screen size (it looks hard coded
 in at least one assembler file), though it may allow scrolling on
 arbitrarily large windows.


 110. Why Amount of Contiguous Free Memory Varies

 Question:

 We run Lotus 1-2-3 under Windows. Lotus 1-2-3 displays the amount of
 free memory for spreadsheets, and this number varies by up to 30K. Why
 does the amount of free contiguous memory vary?

 Response:

 The reasons for variable amounts of free contiguous memory are complex
 and interdependent. The following are some guidelines and explanations
 that may help you understand the problem:

 1. The Windows kernel allocates various data structures related to the
    current environment. A copy of the current directory's contents,
    for example, is kept in nondiscardable storage. With that data in
    memory, the shell can redraw its client area without accessing the
    disk (which is useful on floppy-only systems). As a result, the
    size of the current directory, for example, can make a difference
    in the amount of memory available.

 2. There are additional data structures deeply embedded in the kernel
    that may or may not be allocated at various times, depending on the
    state of the system. Your application has no control over these
    data structures. Their use is so complex that we cannot provide a
    flowchart or state diagram that would help you understand them.

 3. The composition of standard Windows applications also affects memory
    management because applications that use fixed segments can cause
    fragmentation in memory. Memory fragmentation occurs when there are
    "holes" between segments and Windows cannot move them because they
    are marked "fixed" in the .DEF file.

 The result is that a 30K variation in available memory is quite
 possible. That difference is enough to cause shelling out to a huge,
 size-stressing application (such as Lotus 1-2-3) to fail on occasion.

 In this case, an alternative may be to load 1-2-3 on each use as a
 "bad application" (using .PIF files to tell Windows to swap itself
 out) and not try to shell out with Windows still loaded. The use of
 expanded memory (EMS), beginning with Windows Version 2.03, improves
 Windows' ability to handle large applications.


 111. Fatal Exit 0x04FF: Unable to Load Code Segment

 Question:

 What causes the FatalExit error 0x04ff, "INT 3F handler unable to load
 segment"?

 Response:

 The system was unable to load a code segment. This might be caused by
 an .EXE file that cannot be read (e.g. you may have a bad disk
 sector).

 Another possibility is that you may have an out-of-memory condition.
 This could occur if you have PRELOADed segments and FIXED segments in
 your .DEF file and the smaller segments were loaded before the larger
 ones. Then, when the system tries to load the larger segments and
 cannot move the smaller ones because they are FIXED, it finds that
 there is no place to load the larger segments.

 PRELOADed segments are loaded in the order that they are declared in
 the .DEF file, so larger segments should be declared first.


 112. Windows SDK: SS_USERITEM Style for Static Text

 Question:

 What is the meaning of the dialog item style SS_USERITEM (for static
 text)? Is it similar in meaning to the BS_USERBUTTON style for button
 controls? If so, how does it work?

 Response:

 The dialog item style SS_USERITEM, used with static text, is
 essentially useless. In this aspect, it differs from the BS_USERBUTTON
 style for buttons. The SS_USERITEM style for static text is included
 for symmetry. From a symmetry and design point of view, examining the
 BS_USERBUTTON style of buttons may help to explain why including
 SS_USERITEM makes sense even though it is useless.

 The code in the Windows system that handles a BS_USERBUTTON button
 control watches the mouse as it moves over the button. It waits for
 mouse-button actions while the mouse cursor is over the screen area of
 the button. For example, if the button needs to be painted, enabled,
 disabled, or highlighted, the code handling the button sends messages
 to the dialog procedure indicating what needs to happen. The
 BS_USERBUTTON style indicates to the dialog system that the dialog
 procedure itself will do all the painting, etc., associated with that
 button.

 The code handling the SS_USERITEM style static-text controls generates
 no messages to the creating application. It is difficult to understand
 why static text would be used (since it never needs to be disabled,
 highlighted, etc.); if you created such a static-text control, it
 might never even be painted. This style never had any semantic actions
 attached to it and will do nothing for you.


 113. GetSystemMenu() Returning NULL

 GetSystemMenu() will return NULL when called with a nonzero second
 argument if the system menu has not been modified.

 GetSystemMenu() is required to return the handle "original, unmodified
 system menu" when called with a nonzero second argument. However, if
 the system menu has not been modified, GetSystemMenu(hWnd, TRUE) will
 return NULL. The following code will always allow you to change the
 system menu from its initial state:

 HMENU hMenu;
 HWND  hWnd;

  if ((hWnd = GetSystemMenu(hWnd, TRUE)) == NULL)  /* Reset menu if changed */
    hWnd = GetSystemMenu(hWnd, FALSE); /* Not yet changed, get initial menu */


 114. Windows SDK: Portable Bitmaps and GDI

 Problem:

 I understand that I cannot infer the format for SetBitmapBits() data
 from the number of planes and the number of adjacent color bits. In
 general, I must know the name of the display driver to be correct.

 Response:

 You do not need to know the actual format of the bits in the data
 structure, or the name of a display driver. The Get/SetBitmapBits()
 functions are intended for saving and restoring bits to and from
 bitmaps that you created. You created the bitmap, so you know its size
 and format.

 GDI drivers for raster devices are all capable of interpreting data
 from binary maps. To use that capability, do the following:

 1. Create a bitmap of the desired size (width/height) that is
    one-bit-per-pixel and one plane.

 2. Select that bitmap into a DC created with the CreateCompatibleDC()
    function.

 3. BitBlt() from the source of the bits onto the compatible DC. On
    color devices, this automatically will perform a conversion of
    nonbackground pixels to "on," and background pixels to "off" in the
    binary map during the conversion.

 4. GetBitmapBits() from the compatible DC.

 5. Save those bits into a file.

 Later, to reuse those bits, do the following:

 1. Create a bitmap of the required size (width/height) and
    one-bit-per-pixel and one plane.

 2. Select that bitmap into a DC created with the CreateCompatibleDC()
    function.

 3. SetBitmapBits() onto the "compatible" DC.

 4. BitBlt() from the compatible DC to the display (or printer, or
    whatever device onto which you want to put that bitmap). On color
    devices, this will automatically convert all "on" bits to the
    foreground color (current text color) and all "off" bits to the
    current background color.

 This method succeeds because the drivers have agreed to convert that
 single bitmap format into their own format. You do not know exactly
 which bits in the map correspond to which pixels on the map (if you
 study the bitmap data structure and the TIFF document, you may be able
 to guess). Similarly, you do not know if those bits are converted to
 some other format enroute to the target device.

 This technique for saving/restoring any color data (as from an EGA)
 may fail, as we have not yet defined a multiple-bit-per-pixel bitmap
 format that all raster devices must support. Even knowing the exact
 name of the display driver (i.e., the file from which the module
 called "display" was derived), and searching in that file for the text
 string that defines the device to which it is attached, may fail. You
 do not know (unless you wrote the driver) whether the bitmap format
 has changed during revision of the driver. The only bitmaps that will
 work portably are those that are created with the Icon Editor, or
 binary bitmaps of the sort defined above.


 115. Windows SDK: Placement of Dialog Boxes on Display

 Problem:

 I made a child dialog box with a pop-up window as its parent, but the
 child dialog box is created at the wrong coordinates in the pop-up
 parent. Instead of appearing at 0, 0; it shows up in the pop-up window
 at coordinates in the pop-up equal to the origin of the pop-up window
 within its tiled parent.

 Response:

 The following items describe the placement of dialog boxes on the
 display according to the style of the dialog boxes:

 1. WS-CHILD: The CreateDialog() and DialogBox() functions require a
    handle to the parent and the dialog box is displayed relative to
    the parent's window.

 2. WS-POPUP: If CreateDialog() or DialogBox() is called with a handle
    to a parent window (non-NULL), the dialog box is displayed relative
    to the parent's window. If CreateDialog() or DialogBox() is called
    with NULL as the handle to the parent, the dialog box is displayed
    in device coordinates.


 116. Windows SDK: Coordinates Usage by Region Functions

 Question:

 I am calling CreatePolyRgn() with logical coordinates and doing a fill
 with FillRgn(). This procedure works, suggesting that regions take
 logical coordinates. However, when I try to create a region and select
 it as a clipping region, Windows wants device coordinates. What
 coordinates do region functions take?

 Response:

 This question cannot be answered. CreatePolygonRgn() does not care
 what coordinates you give it; it simply creates a region with the
 specified characteristics. Regions are objects, and objects are not
 represented in memory relative to a particular coordinate system
 (although they may appear differently when displayed in different
 systems).

 However, we can answer the question, "In what coordinates will the
 SelectClipRgn() function use the region?" SelectClipRgn() will use the
 region in device coordinates. As already noted, FillRgn() uses the
 region it receives in logical coordinates.

 InvalidateRgn(), ValidateRgn(), InvertRgn(), and PaintRgn() also use
 the regions they receive in logical coordinates.


 117. Windows SDK: Using Information Contexts

 Question:

 Is it possible to create an information context (IC) that is
 compatible with the screen display? If so, what parameters should I
 pass to CreateIC()?

 Response:

 Information contexts were created to avoid the overhead associated
 with loading and initializing large data structures within a device
 driver. This is especially important for devices that must allocate
 buffers for printing or must create font bitmaps from character
 outlines. Inquiries can be made about the device's capabilities when a
 driver is loaded for information purposes, but no initialization is
 done. An information context for the display has no similar advantages
 because the display is guaranteed to be running: therefore, it is of
 less interest.

 It is possible to get an IC for the display by using the name
 "display" to the CreateIC() call (the subtype is not used). But, again,
 for the display, there are no substantial savings over simply doing
 GetDC() or BeginPaint().

 CompatibleDC() calls are also useful. They are created with a
 nominal-sized bitmap (precisely 1 x 1 pixel), and are essentially the
 equivalent of ICs because they do not have large buffers associated
 with them.


 118. Windows SDK: Clipping, Devices, and GDI

 Question:

 The function GetDeviceCaps(hDC,CLIPCAPS) on the Epson FX-80 returns
 zero (0). I was very surprised that this printer does not support
 clipping. Is this true?

 Response:

 GDI does some things, and devices do other things. GetDeviceCaps()
 reports what the device can do; the GDI manual reports what GDI will
 do. The GDI interface is richer than many of the devices attached to
 it. The difference between what the device supports directly and what
 the GDI interface definition guarantees defines what functions GDI
 must "simulate" by using more primitive functions that ARE supported
 in a device.

 The Epson printer doesn't support clipping, which is precisely what
 the GetDeviceCaps() function indicates. The EPSON FX-80 doesn't
 support clipping to any particular portion of the page, even in
 graphics mode.

 GDI makes promises about what can be accomplished when you use a
 device attached to GDI. In particular, if you set a clipshape while
 you're printing, you can be SURE that GDI will honor that clipshape.
 GDI works with the information returned in the GetDeviceCaps()
 function to figure out what the device needs help with. In this case,
 the PRINTER doesn't do clipping itself. The clipping you want is
 provided by GDI. The GetDeviceCaps() function can help you figure out
 how much additional work GDI will have to perform to accomplish some
 function on an attached device that is not directly supported by that
 device. You may want to modify what you ask for to keep things moving
 fast.

 Note also that DC stands for "device context." It happens that the
 most interesting DC is for the display [and, in fact, it is the device
 context for the display that is returned by GetDC(), and in the
 PAINTSTRUCT returned by BeginPaint()]. Printer DCs are NEVER contexts
 for the display; they are device contexts.


 119. GDI, Fonts, and WYSIWYG

 Question:

 I have drawn text to fit perfectly in a box on the screen. When I
 print the same text on the Apple LaserWriter, it extends beyond the
 box. How can I get the text on the screen to better match the printed
 output without using stroke fonts? (I get the same results with either
 TextOut or DrawText.) Also, is there a method to set up the hDC (or
 whatever) so that, on the screen, DrawText will have the same width
 and height as on the printer?

 Response:

 Using GDI, printer, displays, and fonts for What-You-See-Is-What-You-Get
 (WYSIWYG), requires that you understand the model of fonts in GDI and
 the capabilities of printers and displays. The following three
 references may be of use:

 1. "Pocket Pal-A Graphic Arts Production Handbook" from International
    Paper Company, discusses issues related to type and typesetting.

 2. "Bookmaking" by Marsha Lee, discusses issues related specifically
    to making books.

 3. "Phototypesetting, A Design Manual" by James Craig.

 These books contain information about type, fonts, and stringing
 characters together to make text. Problems of faster-image sampling
 are described in standard graphics textbooks. The following
 information may also help you:

 1. Printer manufacturers produce excellent fonts on their printers.
    So, use printer fonts.

 2. GDI must provide some fonts for use with font-deficient display
    drivers.

 3. Displays (even high-end ones) have much lower resolution than
    printers.

 The best displays attached to micros are approximately 120
 dots-per-inch (DPI), while the typical laser printer is 300 DPI. To
 produce the same size image (i.e., a 9-point capital A), the printer
 will illuminate/paint more of its pixels than will the screen. In
 particular, the printer will more closely match the requested size
 than the display. This leads to integer round-off errors (sampling the
 "ideal" character into bitmaps for each dot resolution is the
 rasterization you have heard about).

 Most graphics output devices are raster devices. Because of integer
 round offs associated with sampling the ideal "A" for differing device
 resolutions, the origin of characters and words and the ending of
 lines on displays will seldom be the same as on printers. For example,
 using a 75-DPI display and 300-DPI printer, the display might choose a
 6 pixel-width for the character "A", while the printer might choose a
 25 or 23 pixel-width for that same character. Because of this mismatch
 in sizes, text on the display will require adjustment to achieve
 WYSIWYG for a particular printer.

 GDI provides various approaches to find the information needed to do
 the adjustments. The following applications may be more useful:

 1. Windows Notepad application (does a less than ideal job)

 2. Windows Write application (does better--but look at the length at
    which various screen lines word wrap with different fonts)

 3. Aldus's PageMaker (does an excellent job of handling text)

 The first two applications come with the Windows operating
 environment. Your application accesses the same information as
 PageMaker, which is designed to direct you to the items you need to
 study.

 Regarding the use of fonts with GDI, GDI asks each device whether it
 can support any fonts. With devices that provide some intrinsic fonts,
 driver-based fonts, GDI enumerates the fonts available; it returns
 logical records describing those fonts, and when an application asks
 GDI for a font (using CreateFont, SelectObject), GDI checks for fonts
 available in the device and does a match. GDI selects a font similar
 to what was desired. If the match is poor, GDI will use its own fonts
 (it has a few, mostly for displays, but limited in range).

 The ideal application for using fonts is one that gives you exactly
 what you requested on all devices. More realistically, you get
 something similar, within the limits of what is available for that
 device. Unfortunately, the best available font for that device is not
 an exact replica of the "ideal" font you specified to CreateFont.

 How do you imitate the appearance of the printer's fonts on displays?
 We recommend assuming that the printer has more fonts and greater
 resolution than the display. The following list will give more details
 about using GDI to answer the question posed above:

 1. Open a Device Context and enumerate the fonts available on the
    printer. You will recurrently refer to this printer for actual
    string lengths, but you need to know what the fonts are on the
    printer to begin. Use information from GetDeviceCaps with the
    Textcaps parameter to discover how the device can alter the
    appearance of the fonts it provides. Together, EnumFonts and
    GetDeviceCaps will allow you to determine what fonts the device can
    provide you.

 2. Provide a user interface in your application to allow you to choose
    one of those fonts (this will result in "printer-centered
    WYSIWYG"). Also, provide a choice between sizes and other
    attributes you want to provide access to.

 3. When you have selected a font, create a Device Context for the
    display. SelectObject the "logical image" of the desired font
    (i.e., what the printer will be using) into the display DC, and use
    GetTextMetrics to retrieve a TEXTMETRIC data structure for the
    selected font. The TEXTMETRIC describes what the device will
    actually do. Use the information in the TEXTMETRIC data structure
    to learn about the widths and sizes of the characters to be used on
    the display. Do the same with the printer.

 4. Check whether either of the devices supports the GDI escape codes
    that enhance the usability of fonts. One escape code, for example,
    returns the width table for proportionally spaced fonts. These
    escapes are defined in the update for the Version 1.03 Software
    Development Kit. Some currently available drivers support the codes
    (use the escape code QUERYESCSUPPORT to discover whether the device
    supports a particular escape code). The tables will help in
    deciding the physical extent of character strings you send to the
    printer and how to match that extent on the display.

 5. If the devices do not support those GDI escapes, use GetTextExtent
    on the printer, with the desired font selected, to get the extent a
    string it will occupy when printed.

 6. Or, use GetTextExtent on the display DC to discover how the display
    appearance must be adjusted to achieve the appearance on the
    display of the image of that string on the printer. If you are
    operating in pixel mode, you must map the desired extent on the
    display (i.e., the image on the display of the extent the string
    will occupy on the printer) onto the display. You must decide how
    to adjust the physical extent on the display with the
    SetTextJustification and SetTextCharacterExtra commands to
    distribute needed pixels throughout the image of the string on the
    display. If the extent must be adjusted so the space occupied on
    the display is smaller than the extent you will get using the
    display with the selected font, you must place individual words (or
    even characters) to get the proper appearance (SetTextJustification
    and SetTextCharacterExtra do not work with negative numbers).

 The portions of GDI that you must understand fully to achieve WYSIWYG
 include the following:

 1. CreateFont, CreateFontIndirect

 2. SelectObject

 3. GetTextMetrics with TEXTCAPS parameter, and the TEXTMETRICS data
    structure

 4. EnumFonts

 5. Escape with EXTTEXTOUT, GETEXTENDEDTEXTMETRICS, GETEXTENTTABLE,
    GETPAIRKERNTABLE, GETTRACKKERNTABLE, ENABLEPAIRKERNING,
    ENABLERELATIVEWIDTHS, SETKERNTRACK, QUERYESCSUPPORT parameters

 6. GetTextExtent

 You must also understand the various coordinate transformation
 functions:

 1. SetMappingMode

 2. SetViewportOrigin

 3. SetViewportExtent

 4. SetWindowOrigin

 5. SetWindowExtent

 This is a complex area, and GDI provides much support. No other
 graphics language in common use provides as much richness and support
 for the device-independent use of fonts as GDI. As witness to the
 possibilities, examine the use of GDI by Aldus's PageMaker (it makes
 Epson dot-matrix printers look good), and do WYSIWYG on those
 printers. It is not a simple matter to use these capabilities well,
 and it is not possible for someone unfamiliar with typography to get
 it right. You will need to write many example programs, and think
 about all the entities in the system (printer drivers, display
 drivers, GDI, GDI's font mapper, and the fonts themselves).


 120. Windows SDK: One-Time Initialization

 Problem:

 Several actions seem unwise when processing the WM_CREATE message for
 a window. Windows does not seem to provide a message that the
 application receives only once that can be used to initialize the
 application. Currently, we use a static flag (initially FALSE), to do
 our one-time initialization while handling the WM_PAINT message if the
 flag is FALSE, and reset the flag. This works, but it is complicated.

 Response:

 To do one-time initialization, do the following:

 1. Modularize all your datastructure/file/object/resource
    initialization into code called by the WM_CREATE handler in your
    window procedure.

 2. Do all your painting in your code called by the WM_PAINT handler in
    your window procedure.

 The static nature of WinMain() and the fact that it is linearly
 executed by each instance prior to entering the message-getting loop
 provides some opportunities to perform initialization directly within
 the WinMain() code, prior to that message loop.

 In particular, you can check for required resources at the time the
 first instance is being created. In our examples, this is always done
 in the if (!hPrevInstance) {} statement block at the top of WinMain().
 This is an appropriate time for a device driver to discover whether
 the device is installed, etc.

 The next most logical place to perform initialization from WinMain()
 is after CreateWindow() and ShowWindow() have each returned. But you
 are, in fact, already in message-getting mode by that time (each of
 those calls causes a message to be "injected" directly into the window
 procedure for the application, so you might as well move right into
 message-handling for doing any initialization for each instance).

 Note that the calls to CreateWindow(), ShowWindow(), and
 UpdateWindow() result in three messages arriving at your instance
 window procedure before the instance task enters the message loop.
 CreateWindow() injects WM_CREATE, ShowWindow() injects WM_SHOWWINDOW,
 and UpdateWindow() injects WM_PAINT (if the window needs to be
 painted).

 Your comments about dangers and limitations to processing during the
 WM_CREATE are well taken. Let us look at what is happening in more
 detail:

    When the CreateWindow() code directly messages to your window
    procedure, there is no window yet. Certainly, there is a data
    segment for the current instance. Therefore, you can do any
    reasonable initialization that does not involve drawing on, asking
    about, or in any way using the window being created. It is
    completely reasonable to zero out any arrays, set flags, create or
    load resources, open files, create/initialize local heaps and the
    like within the processing of this message. This is the usual
    handling of the WM_CREATE message.

    When ShowWindow() directly messages to your window procedure, there
    is already a window, so window activities are safe. You could, for
    example, draw on the window during the processing of the
    WM_SHOWWINDOW message. In fact, doing so might obviate
    UpdateWindow() having to send a WM_PAINT message (although Windows
    tends to be pretty conservative, and errs on the side of "one more
    paint won't hurt"). In fact, there is not a lot of activity that
    makes sense during processing of the WM_SHOWWINDOW message when it
    occurs as a result of the call to ShowWindow() during
    initialization (indicated by lparam being 0). In your case, this
    would be a good time to create dialogs (because you can find out
    the window handle of the parent within this call). The real purpose
    of the call to ShowWindow() in your WinMain() is to get the window
    you have just created placed into a tile or icon; i.e., it asks
    Windows to adjust the screen (re-tiling before Version 2.00, placing
    for Versions 2.00 and later). Later calls to ShowWindow() [with
    nonzero lparams] are very useful for managing your children and
    pop-ups.

    When UpdateWindow() directly messages to your window procedure,
    there is already a window; it is already visible wherever it has
    been placed (tile, overlap, icon). The background has been painted
    (if the class has a background brush), and you're ready to paint.
    It makes sense to paint whatever areas need to be redrawn, and only
    do that much activity, whenever you get the WM_PAINT message.


 121. Changing the Top-Level Menu Text

 The correct method for changing the menu text of a top-level menu
 (i.e., a pop-up menu title on the menu bar) is as follows:

 1. Save the handle for the submenu, using the GetSubMenu() function.

 2. Remove the submenu, using ChangeMenu() and specifying MF_REMOVE.

 3. Add the submenu with the new menu text, using ChangeMenu() and
    specifying MF_INSERT.

 The following is an example:

    hmenu = GetMenu(hwnd);
    hmenu1 = GetSubMenu(hmenu, 0);
    ChangeMenu(hmenu, 0, (LPSTR)NULL, NULL, MF-BYPOSITION | MF_REMOVE);
    ChangeMenu(hmenu, 0, (LPSTR)"TEST!", hmenu1,
                         MF-INSERT | MF-BYPOSITION | MF-POPUP);

 The following method is incorrect:

    hmenu = GetMenu(hwnd);
    hmenu1 = GetSubMenu(hmenu, 0);
    ChangeMenu(hmenu, 0, (LPSTR)"TEST!", hmenu1,
                         MF-CHANGE | MF-BYPOSITION | MF-POPUP);

 When you specify a pop-up menu with MF-CHANGE, Windows assumes you are
 going to add a new pop-up. It destroys the old pop-up (if there is
 one), then adds the new pop-up. Because you specified the same pop-up,
 the old one is destroyed.


 122. Space Needed to Use LoadLibrary

 Question:

 How much memory is required for LoadLibrary() to function? What would
 cause it to fail?

 Response:

 The LoadLibrary() function does not require any prescribed amount of
 memory. Consider that LoadLibrary() will make room for the following:

 1. All non-DISCARDABLE segments (either FIXED or MOVEABLE)

 2. All PRELOADed segments (even if DISCARDABLE)

 3. The data segment

 4. The module .EXE header

 Note that the size of the library on the disk doesn't necessarily
 match the size in RAM. For example, a very small .EXE (for example, an
 8K .EXE consisting of 6K of code and 2K of data) might take 70K or
 more of RAM because the data segment needs 64K and the code needs 6K.

 On the other hand, a very large library might need much less memory
 space than disk space. This can occur when the library consists of
 many small-sized, DISCARDABLE code segments; since they don't all have
 to be in RAM at the same time, the impact on RAM is much less than the
 disk space.

 LoadLibrary() needs to find room in the DISCARDABLE segment area for
 the largest DISCARDABLE segment in your library. To do this, it might
 need to move the Code Fence; however, if a FIXED segment is loaded
 just under the Code Fence, Windows cannot adjust the Code Fence and
 the call to LoadLibrary() will fail.

 Also be aware that there must be enough RAM below the Code Fence to
 load everything in your library that is not a load-on-call DISCARDABLE
 segment; this includes PRELOADed DISCARDABLE segments.

 Another factor affecting the success or failure of a call to
 LoadLibrary() is the order in which segments are declared. PRELOADed
 segments are loaded in the order that they are declared in the .DEF
 file, so larger segments should be declared first.


 123. Windows SDK: Dynamic Link Libraries and Memory Models

 Question:

 Should the customized memory model -Alfw be used for dynamic link
 library (DLL) compilation, since data items have to be addressed in 32
 bits (residing in calling program stack) in addition to the need for
 long code and data pointers?

 Response:

 There is no need to use the "lf" memory-model options; the "w" is
 required, however. Dynamic libraries can have a data segment of their
 own and can be built into the small, medium, compact, or large memory
 models.


 124. Finding Which Dialog Control Has the Focus

 To find out which dialog control has the focus, use the following
 code:

 if (GetFocus() == GetDlgItem(hWndDlg, ID_xxxx))

 The above code indicates whether the given dialog control ID has the
 input focus. GetFocus returns the handle to the dialog control that
 has the focus.


 125. GetTextCharacterExtra Is Misspelled as GetTextCharExtra

 On Page 310 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual, the following word is spelled
 incorrectly:

    GetTextCharExtra

 To use this function, it must be spelled as follows:

    GetTextCharacterExtra

 This spelling complies with that used in WINDOWS.H and the Windows
 Libraries.


 126. Heapwalk Code Fence Describes Memory location

    Heapwalk's code fence describes the memory location that separates
 discardable and nondiscardable memory. As you watch Heapwalk, this
 location will move up and down to properly maintain enough room for
 loading discardable code. It will keep enough free space in the
 nondiscardable section (above the code fence) to reload the largest
 discardable segment.


 127. RC Switch Settings

 Question:

 RC has -E, -L, and -M switches; what are they for?

 Response:

 The -E switch was designed as a way for smart printer drivers to get
 around an efficiency routine we put in. By default, all libraries that
 call GlobalAlloc() get memory from above the EMS line EXCEPT libraries
 that are loaded through LoadLibrary(). (The majority of libraries
 loaded this way are printer drivers.) Libraries that are loaded by
 LoadLibrary() get non-banked memory by default. The -E switch changes
 the default situation for these LoadLibrary()-loaded modules so that
 by default they get memory from above the EMS line.

 The -L switch sets RC to compile an application that uses expanded
 memory according to LIM Version 3.2.

 The -M switch sets RC to compile an application that uses expanded
 memory so that multiple instances of the application will use
 different EMS banks.



 128. Proper Use of GrayString()

 Problem:
    There is a problem with painting using GrayString. During painting,
 the grayed string sometimes gets displayed and sometimes it does not.

 Response:
    This is not a problem; this behavior is caused by specifying
 GRAY_BRUSH as the brush to be used by GrayString. Although this would
 seem to be the logical brush to use, a gray brush is made up of a
 black-and-white dithered pattern. If this brush is used for graying,
 the alignment of the brush at the pixel level is critical. The brush
 is combined with the text pixels, then the pixels are grayed through
 staggering. Depending on the alignment, only staggered black bits or
 staggered white pixels will result. Regardless of whether the
 background is black or white, the string will appear sometimes, but
 not at others.
    Therefore, you should not use GRAY_BRUSH. If your background is
 white or colored, choose BLACK_BRUSH. If your background is black,
 choose WHITE_BRUSH. The sample below shows that colored brushes can be
 used to make off-colored text. Three GrayString calls are used: one
 with BLACK_BRUSH, the next with GRAY_BRUSH, and finally one with a red
 brush. Please note that the middle string sometimes appears and at
 other times disappears as the window is resized. When the string
 disappears, look carefully at the yellow rectangle and note that white
 characters appear. This is due to the behavior of the GRAY_BRUSH as
 described above.
    Listed below is a code fragment that can be used with the SDK
 sample application HELLO that illustrates these issues:

 void HelloPaint( hWnd )
 HWND hWnd;
 {
     HBRUSH hbrDef, hbrColor;
     PAINTSTRUCT ps;
     HDC  hDC=  BeginPaint( hWnd, &ps );
     char *szText= "This should be a grayed string";
     int  nTextLength= strlen( szText );
     DWORD dExtent= GetTextExtent( hDC, szText, nTextLength );
     WORD tHeight= HIWORD(dExtent);
     WORD tWidth=  LOWORD(dExtent);

      hbrColor = CreateSolidBrush( RGB(255,255,0) );
      hbrDef = SelectObject( hDC, hbrColor );
      Rectangle( hDC, 0, 0, 60, 50 );
      SelectObject( hDC, hbrDef );
      DeleteObject( hbrColor );

      GrayString( hDC, (HBRUSH)GetStockObject( BLACK_BRUSH ),
         NULL, (DWORD)(LPSTR)szText, nTextLength,
         0, 0, tWidth, tHeight );

      GrayString( hDC, (HBRUSH)GetStockObject( GRAY_BRUSH ),
         NULL, (DWORD)(LPSTR)szText, nTextLength,
         0, 15, tWidth, tHeight );

      hbrColor = CreateSolidBrush( RGB(255,0,0) );
      GrayString( hDC, (HBRUSH)hbrColor,
         NULL, (DWORD)(LPSTR)szText, nTextLength,
         0, 30, tWidth, tHeight );
      DeleteObject( hbrColor );

    ValidateRect( hWnd, (LPRECT)NULL );
    EndPaint( hWnd, (LPPAINTSTRUCT)&ps );
 }


 129. Memory Models Overview

 Question:

 In a previous (non-Windows) application, as the code grew, we switched
 from small to large model simply by changing the compiler flag and
 linking with the large versions of our purchased libraries. For our
 current Windows project, we must use mixed mode because the program
 eventually will be quite large. I do not understand how this will
 work. Presumably, some routines must be declared as FAR. Could you
 give some general pointers on how to use mixed model?

 Response:

 You do not need a mixed model; medium model is sufficient. A general
 overview of memory models is as follows:

              CODE              DATA
 MODEL    # SEG. POINTERS   # SEG. POINTERS    WinMain
 SMALL      1      NEAR       1      NEAR      NEAR PASCAL SS == DS
 MEDIUM    mult    FAR        1      NEAR      FAR PASCAL  SS == DS
 *COMPACT*  1      NEAR      mult    FAR       NEAR PASCAL SS != DS
 *LARGE*   mult    FAR       mult    FAR       FAR PASCAL  SS != DS

 Compact and large memory models are not recommended for use within
 Windows, as their data segments must be fixed for the Windows loader
 to load them properly.

 Having SS == DS tells the compiler that the stack segment and the
 default data segment are to be combined into a single segment; they
 must occupy less than 64K of combined memory. If SS != DS, different
 segments are used for the stack and data, so you can have 64K in the
 default data segment itself, regardless of the stack size. You are
 still limited to 64K of initialized data, regardless of whether SS !=
 DS or not.

 Since your code is increasing to more than 64K, compile using the
 medium-memory model and declare your routines as FAR for appropriate
 calling. If your data becomes greater than 64K, continue to use the
 medium model, but use GlobalAlloc() to obtain the extra memory you
 require.

 A mixed-memory model would involve defining your own combination of
 the above characteristics, e.g. multiple code with single data, but SS
 != DS (-Alnw). However, if you need additional memory, use
 GlobalAlloc().

 For more details, refer to Pages 185-189 in Section 8.4 of the
 "Microsoft C Compiler User's Guide."


 130. Icons and Cursors from Libraries

 Question:

 I have created an icon resource and defined it in my .RC file as
 follows:

 quest  ICON  quest.ico

 Later, I define a dialog box that has the following statement:

 ICON  "quest"   (location coordinates)

 The resulting .RES file is linked as part of a library routine.
 Everything works correctly when I call the library routine, except
 that the icon that is a part of the dialog box is not displayed and I
 get a "random" pattern in the icon area. I suspect some sort of
 pointer problem, since the icon changes when I run SHAKER and force
 the dialog box to be repainted.

 Is there some problem with defining an icon resource as part of a
 dialog box that, in turn, is part of a library routine?

 Response:

 The processing procedures for icons and cursors are installed by
 InitApp, a USER.EXE routine called by WINSTART. WINSTART is not called
 when libraries are loaded, so you do not get special icon or cursor
 processing. Since icons and cursors are "calculated resources," the
 default processing procedure does not handle them as you expect (i.e.,
 as they are handled in applications).

 There is a "magic" word at the beginning of each icon and cursor. The
 icon magic word tells whether or not the icon is machine-dependent.
 The cursor magic word defines the details of edit mode and save mode.
 You need to set a resource handler to remove the magic word. Once the
 magic word is removed, Load[Cursor/Icon] and Draw[Cursor/Icon] will
 work. Note that if you want machine-independent icons and cursors, you
 need to StretchBlt them to the appropriate size in addition to
 removing the magic word.

 Although you could write the resource handler yourself, the easier
 method (from a programming point of view) is to find out where Windows
 keeps the processing procedures for icons and cursors and install them
 in the library when the library is loaded. The following sample code
 will do exactly that (this routine is used as a dummy resource handler
 to allow us to retrieve the address of the application's resource
 handler):

    HANDLE FAR PASCAL ResHandler( hRes, hResFile, hResIndex )
    HANDLE hRes;
    HANDLE hResFile;
    HANDLE hResIndex;
    {
        return NULL;
    }

 This code resides wherever the Dynamic Link Library is loaded. It
 first makes certain that the loading is successful, then attempts to
 get the module handle from the library. Provided that succeeds, the
 resource handler is then retrieved from the application and given to
 the library, as follows:

   case LOADMYLIBRARY:
     hAppDLL = LoadLibrary( (LPSTR) lpszLibraryName );
     if (hAppDLL < 32)
         MessageBox( hWnd, (LPSTR)"Loading DLL failed!",
                                            (LPSTR)NULL, MB_OK );
     else {
       FARPROC lpResHandler;
       FARPROC lpOld;
       HANDLE hModule;

       hModule = GetModuleHandle ( ( LPSTR ) lpszLibraryName );
       if ( hModule != NULL ) {
         lpResHandler = MakeProcInstance( (FARPROC) ResHandler, hInst );
      /* Find address of default handler and install it in library. */
         lpOld = SetResourceHandler( hInst, RT_ICON, lpResHandler );
         SetResourceHandler( hInst, RT_ICON, lpOld );
         FreeProcInstance( lpResHandler );
         SetResourceHandler( hModule, RT_ICON, lpOld );
         }
       else
         MessageBox( hWnd, (LPSTR)"hModule failed",
            (LPSTR)"Resource Handlers not initialized", MB_OK );
       }

     break;


 131. FindResource() for Cursors and Menus

 If you are using FindResource() on a Version 3.00 CURSOR or MENU, you
 must use RT_GROUP_CURSOR and RT_GROUP_MENU so that they can be found.


 132. SPY Utility from BYTE Magazine

 There was an article on debugging Windows applications called "Spying
 on Windows" in the IBM PC special issue of BYTE magazine.

 Michael Geary, the author of the BYTE article and SPY utility, has
 generously provided the source code for SPY. The source code is
 available in ARC file format as SPY.ARC in the Software Library.
 This file can be found in the Software Library by searching on the
 filename, the Q number of this article, or S10015.


 133. Cursor, Icon, and Bitmap Resource File Formats

 Question:

 What is the data structure for a cursor?

 Response:

 The following is the format for the cursor, bitmap, and icon
 resources:

 byte  bFigure;
 byte  bIndependent;
 short xHotspot;
 short yHotspot;
 short cx;
 short cy;
 short widthbytes;
 short clr;
 followed by the bits of the AND mask which, in turn, is followed by the
 bits of the XOR mask

 bFigure                 Specifies whether the structure is a cursor,
                         bitmap, or icon. The value 1 indicates a cursor,
                         2 indicates a bitmap, and 3 indicates an icon.

 bIndependent            Specifies whether the object is device dependent
                         or device independent. The value 0 indicates
                         device dependent, the value 1 indicates device
                         independent.

 xHotspot                Specifies the x-coordinate of the cursor's or
                         icon's hotspot.

 yHotspot                Specifies the y-coordinate of the cursor's or
                         icon's hotspot.

 cx                      Specifies the x-extent of the cursor or icon.

 cy                      Specifies the y-extent of the cursor or icon.

 widthbytes              Specifies the bytes per row in the cursor, or
                         icon (note that the bytes must be aligned on a
                         word-boundary).

 clr                     Specifies the number of color planes (note that
                         this value is always set to 0).

 If the resource is a bitmap, a BITMAP data structure follows the
 bIndependent field. The fields xHotspot, yHotspot, cx, cs, widthbytes,
 and clr are not part of the bitmap resource file.


 134. Windows SDK: Transparent Bitmaps

 Question:

 How do you set the bits in a bitmap to be transparent?

 Response:

 Transparency is not an attribute of a bitmap. Transparency is an
 attribute of the background SetBkMode() or of the raster operation
 used to transfer the bitmap to the graphics device. Choosing an
 appropriate raster operation is the most common method of achieving
 transparency.


 135. Getting hInstance from hWnd

 Question:

 Since so many functions in Windows require a hInstance value, what is
 the best way of keeping track of this, and is there a way to get an
 instance handle from a window handle?

 Response:

 There are several ways of getting a proper instance handle for your
 various usages. The following are three different ways that should
 satisfy any of your possible needs:

 1. In your WinMain() function, you can save the hInstance to a global
    variable that would then be available to any other procedure in
    your application.

 2. In your WndProc() function, you can process the WM_CREATE message
    and retrieve the hInstance from the CREATESTRUCT pointed to by the
    lParam and store it in a static variable that belongs to that
    function alone.

 3. You can use GetWindowWord() to get the instance handle associated
    with a window handle at almost any time.

 The following C source code demonstrates all three methods of getting
 an instance handle. The methods used will store the hInstance in the
 variables hInst1, hInst2, and hInst3 respectively:

 /**********************************************************************
  * MinWin - Example Windows Program to demonstrate use of hInstance
  */
 #include <windows.h>

 char   szAppName[] = "MinWin";
 HANDLE hInst1;

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

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

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

         if (!RegisterClass (&wndclass))
             return FALSE;
     }

     /* 1) As a 'global' variable */
     hInst1 = hInstance;
     hWnd = CreateWindow (szAppName, szAppName,
         WS_OVERLAPPEDWINDOW,
         CW_USEDEFAULT, 0,
         CW_USEDEFAULT, 0,
         NULL, NULL, hInstance, NULL);

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

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

 long FAR PASCAL WndProc (hWnd, iMessage, wParam, lParam)
      HWND     hWnd;
      unsigned iMessage;
      WORD     wParam;
      LONG     lParam;
 {
     static HANDLE  hInst2;
     HANDLE         hInst3;

     /* 3) As a 'local' variable */
     hInst3 = GetWindowWord (hWnd, GWW_HINSTANCE);

     switch (iMessage) {
         case WM_CREATE:

             /* 2) As a 'static' variable */
             hInst2 = ((LPCREATESTRUCT) lParam)->hInstance;

             break;

         case WM_DESTROY:
             PostQuitMessage (0);
             break;

         default:
             return DefWindowProc (hWnd, iMessage, wParam, lParam);
     }
     return 0L;
 } /* WndProc */


 136. Windows SDK: Number of Bits Required for a Bitmap

 Question:

 When in MM_ANISOTROPHIC mode, how do you calculate the number of bits
 required for a bitmap to create a specific image on a display device?

 Response:

 You do not need to calculate the size yourself; instead, use
 CreateCompatibleBitmap(). You can then get or set bits in the bitmap
 using SetBitmapBits() or GetBitmapBits() and GetObject() (see the
 programmer's reference manual). However, if you want to calculate the
 size yourself, do a GetDeviceCaps() to get the size of the device in
 pixels and use the DPtoLP() and LPtoDP() routines to calculate the
 size you need.


 137. Windows SDK: Locking Discardable Objects

 Question:

 If there are other discardable objects in memory when you are
 allocating a new discardable object, do you purge other discardable
 objects to make room for the new discardable object? Also, is it
 possible to put in a call to determine whether an object is swapped or
 discarded?

 Response:

 GlobalLock() returns NULL if the object is discarded. There is no such
 thing as a swapped object. Locking a discarded object does not affect
 its lock count. Discarded objects always have a lock count of zero.
 Objects with a nonzero lock count are never discarded.


 138. Windows SDK: Using EndPaint() and BeginPaint()

 Question:

 It is unclear whether EndPaint() expects my fields of the lpPaint
 structure to have changed. Should an application change any fields in
 the PAINTSTRUCT parameter received from BeginPaint()? In particular,
 should the application change the fRestore field if the window has
 been completely restored?

 Response:

 The PAINTSTRUCT data structure is automatically updated by the
 BeginPaint() Windows procedure; if a window has been restored,
 BeginPaint() will make sure the fRestore field of the PAINTSTRUCT is
 changed. The application would be duplicating effort if it also
 changed the fRestore field of the PAINTSTRUCT after a window is
 restored. The EndPaint() procedure is required after BeginPaint(). The
 application only needs to be concerned about its own paint procedure
 that comes between BeginPaint() and EndPaint().


 139. Windows SDK: Handles Do Not Change

 Question:

 Is it safe to hold onto a window handle?

 Response:

 Yes, it is safe to hold onto a window handle (or any handle) of a
 global object; as long as that object exists, its handle will not
 change. The handle is a unique (see note below), fixed identifier for
 the object. Handles exist to allow easy, indirect access to an object
 that actually may be moving.

 NOTE: Handles are unique only among the objects to which they are a
 handle. A handle to a window will always be different from all other
 handles to windows, but could be the same as a handle to a brush (or
 any other object).


 140. Including Files Using Full Path in Resource Compiler

 The RC compiler will return a fatal error if a full path is specified
 in an #include statement. It is necessary to use double backslashes
 when specifying a path in the .RC file. It is also legal to use a
 single forward slash in place of the double backslash.

 For example, if an .RC file that contains the following statement is
 compiled

    #include "\include\foo.h"

 an error similar to the following is generated:

    ~rc1234.tmp(1) : FATAL : can't find 'includefoo.h'

 The RC compiler uses the preprocessor of the C compiler. Therefore,
 you must use either double backslashes or a single forward slash, as
 in the following examples:

    #include "\\include\\foo.h"

 or

    #include "/include/foo.h"


 141. Windows SDK: How to Create New Modules

 Question:

 In previous Windows releases, you could create new instances of the
 program through the NewModule() procedure. However, this procedure is
 no longer documented. How do we create new instances in the new
 Windows release?

 Is there a way to create a new instance and to run it as a separate
 task, or is the MS-DOS Executive the only code with that ability?

 Response:

 To create a new instance and run it as a separate task, use the normal
 DOS (Int 21) system call to spawn the application. Windows traps the
 new instance and handles it appropriately. The following is an
 example:

 #include "dos.h"
 /* Program segment prefix. */
 extern unsigned _psp;

 /* Environment block. */
 extern LPSTR environ;

 /* EXEC call command block. */
 struct {
         WORD  environment;
         LPSTR command;
         LPSTR fcb_5c;
         LPSTR fcb_6c;
         WORD showflag;
         } commandblock;

 /* DOS registers. */
 union  REGS   in;
 union  REGS  out;
 struct SREGS ins;

 LPSTR shapename;
 LPSTR shapeblock;

 ShapesCommand(hWnd, id)
 HWND hWnd;
 int id;
 {
   cCurrentShape = id;
   if ( id == SPAWN ) {

      LockData( in );
      shapename  = (LPSTR)"SHAPES.EXE";
      shapeblock = (LPSTR)&commandblock;

      in.h.ah =  0x4b;    /* Exec Int 21 function. */
      in.h.al =  0;       /* Fork function. */

      ins.ds  =  FP_SEG( shapename );  /* Program for instance. */
      in.x.dx =  FP_OFF( shapename );

      ins.es  =  FP_SEG( (LONG)shapeblock ); /* E.g. command block. */
      in.x.bx =  FP_OFF( (LONG)shapeblock );

      commandblock.fcb_5c = (LPSTR)(MAKELONG(_psp,0x5c));
      commandblock.fcb_6c = (LPSTR)(MAKELONG(_psp,0x6c));
      commandblock.command = (LPSTR)"Shapes command line.";
      commandblock.environment = FP_SEG(environ);
      commandblock.showflag = 0xFFFF;

      intdosx ( &in, &out, &ins );

      UnLockData( in );

      }
   InvalidateRect (hWnd, (LPRECT) NULL, (BOOL) TRUE);
 }


 142. Windows SDK: Definition of a Region

 Question:

 The term "region" is used throughout the Windows documentation.
 However, the term is not defined. What is a region, what is it used
 for, and how is a region defined or specified?

 Response:

 Unlike most graphics packages that can manipulate only simple
 geometric structures (usually rectilinear), Windows GDI has the unique
 ability to gather an arbitrary set of spatially coherent points into a
 structure called a region, and perform complex yet rapid manipulations
 and calculations on such structures. This feature not only will make
 your standard programs simpler and faster, but will let you perform
 operations that would otherwise be nearly impossible (for example, it
 is fundamental to the implementation of the Windows User Interface).

 You define a region by defining lines, by defining shapes such as
 rectangles and ellipses, or by defining other regions. The outline of
 a region should be one or more closed loops. A region can be concave
 or convex, can consist of one area or many disjoint areas, and can
 have "holes" in the middle.

 Many calculations can be performed on regions. For example, given any
 two regions, GDI can find their union, intersection, difference, and
 exclusive. There is a set of graphic operations on regions to draw
 them on the screen.

 When you select a region into a DC, a new clipping area is created.
 Any graphics you send to that DC will be clipped to the region you
 created. You define regions with CreateRectRgn(), CreateEllipticRgn(),
 and CreatePolygonRgn().


 143. When to Use LocalLock()

 Question:

 I am uncertain about Windows memory management. When do I lock and
 when should I not lock local handles?

 Response:

 The global and local allocators accept a flags word. It is the flags
 word that determines whether or not the handle returned by the
 allocator must be locked. A value of 0 for the flags to LocalAlloc()
 would specify that a fixed handle was desired. Fixed handles are
 equivalent to pointers returned by calloc() in C and new() in Pascal
 and thus may be dereferenced using the "*" operator.

 Passing a flag of value LMEM_MOVEABLE indicates that a handle to a
 movable structure is desired. In order to dereference a movable
 handle, a LocalLock() must be done on the handle (exceptions noted
 below). The LocalLock() generates a fixed handle from a movable
 handle.

 Local movable handles are designed such that if no intervening memory
 management calls occur, they may be dereferenced using the "**"
 notation of C. Alternatively, you may turn off local heap moving of
 local movable objects using the LocalFreeze() call. In this case,
 intervening memory management calls may be made and you may still use
 the ** notation to dereference a local movable handle.

 A movable handle is aliased as char **. Thus you can access the data
 pointed to by a movable handle using ** if it is known that the heap
 is frozen.

 In a virtual addresses space such as the VAX, the distinction between
 local and global is unnecessary.


 144. WIN87EM Handling Under Windows 2.x Vs. Windows 3.00

 Windows Version 3.00 applications no longer need to ship WIN87EM.EXE
 because the Retail Windows 3.00 package includes WIN87EM.DLL. Windows
 2.x applications, however, must continue to ship WIN87EM.EXE because
 it is needed in Windows 2.x and in Windows 3.00 real mode.

 Windows applications that use the Emulator math package need to
 dynamically link with the WIN87EM library. The following is
 information on how this support is provided in Windows 2.x and Windows
 3.00.

 Windows 2.x Applications Running in Windows 2.x
 -----------------------------------------------

 Applications that use the Emulator math package must ship WIN87EM.EXE
 along with their application. When the application loads, it
 implicitly requests WIN87EM.EXE, which must be pointed to somewhere by
 the PATH variable.

 Windows 3.00 Applications Running in Windows 3.00
 -------------------------------------------------

 Applications that use the Emulator math package no longer need to ship
 WIN87EM.EXE because Windows 3.00 Retail includes WIN87EM.DLL in its
 SYSTEM directory. Any Windows 3.00 application that uses Emulator math
 will implicitly link to WIN87EM.DLL.

 Windows 2.x Applications Running in Windows 3.00
 ------------------------------------------------

 In Windows 3.00 real mode, the situation is handled the same way as in
 Windows 2.x: the application requests a dynalink to WIN87EM.EXE, and
 the application must ship WIN87EM.EXE to satisfy this request. Windows
 3.00 real mode honors this request by loading WIN87EM.EXE.

 In Windows 3.00 standard mode and enhanced mode, however, we do not
 load the 2.x WIN87EM.EXE because it causes general protection
 violations in protected mode. Instead, we intercept the 2.x
 application's dynalink request for WIN87EM.EXE and replace it with the
 3.00 library WIN87EM.DLL. This happens transparently, so the
 application is not aware of it; therefore, no modifications to your
 application source file, MAKE file, .DEF file, or libraries are
 needed.

 This special-case handling of Windows 2.x applications in Windows 3.00
 occurs regardless of whether or not the application has been marked as
 "3.00-compatible" using the MARK.EXE utility.

 Microsoft has tested all of the above scenarios, and they work exactly
 as stated. If you are building a Version 2.x application with 2.x
 tools, or if you are building a 3.00 application with 3.00 tools, the
 above methods will work correctly. If you are building a 2.x
 application with 3.00 tools, the above methods are not guaranteed to
 work because this is not a valid way to build a 2.x application. For
 more information, query on the following words:

    create and windows and 3.00 and applications and compatible and 2.x


 145. Heap and Stack Usage within Windows

 Question:

 Could you please clarify heap and stack usage within a segment versus
 heap and stack usage within the application as a whole?

 Response:

 An application gets a single default data segment. From the data
 segment, the task descriptor and stack are allocated. The remainder is
 to be used for static data and local dynamic heap.

 The STACKSIZE keyword in the .DEF file for an application specifies a
 deductible quota from the default data segment for the application. It
 is fixed in size.

 The HEAPSIZE keyword in the .DEF file specifies a recommended default
 local heap size. The heap automatically grows when requests exceed the
 recommended size.

 The sum of static data, stack, and local heap cannot exceed 64K.

 Multiple local heaps can be managed using the LocalInit() call and
 swapping the DS register as needed.


 146. FREEMEM Sample Program from "Microsoft Systems Journal"

    The following is a summary of the FREEMEM program found in the
 "Microsoft Systems Journal" and in the Software Library.

    Issue: March 1987 Vol. 2 No. 1  Page 33
    Author: Charles Petzold
    Filename: FREEMEM

    This file can be found in the Software Library by searching for the
 filename FREEMEM.ARC, the Q number for this article, or S10005.
    The FREEMEM program displays the amount of free memory available
 for the currently active application in its icon area. If you have
 expanded memory enabled in your system, the amount of memory displayed
 by FREEMEM will be the amount of memory available when FREEMEM is
 running (since it is the active application when it is calculating the
 amount of free memory).
    Some of the Windows programming concepts that are used in FREEMEM
 are as follows:

    1. Causing an application to start as an icon
    2. Drawing in an icon dynamically
    3. Using timer messages
    4. Calculating the amount of free memory available for the current
       application


 147. Windows SDK: Using SYMDEB /f and /c1 Switches

 Question:

 We are having trouble debugging a Windows application. However, when
 we add the switches /f and /c1 to SYMDEB, everything works correctly.
 What do these switches do?

 Response:

 The switches /f and /c1 are not necessary for debugging Windows
 applications, but are a good idea to use if you have a dumb terminal
 hooked up to AUX.

 The /f switch means not to associate the first symbol table with the
 immediately following executable files on the command line when using
 SYMDEB (i.e., SYMDEB /f APP.SYM KERNEL.SYM WIN.COM).

 The /c1 switch is the same as the documented =COM1 command to redirect
 output to COM1:. The switch /c2 will redirect output to COM2:.


 148. GlobalNotify() Documentation Error in Windows SDK 2.00 Update

 The information on GlobalNotify() on Pages 12 and 13 of the "Microsoft
 Windows Software Development Kit Version 2.0 Update" manual is
 incomplete.

 The GlobalNotify() callback function will receive the segment address
 of the memory block, not the handle, as implied in the Version 2.00
 SDK Update manual. To retrieve the handle, callback routines should
 call GlobalHandle().

 Callback functions should not assume the data or stack segments of the
 application.

 Do not call any routine that has the potential of moving memory.

 To get your own DS, save it in the code segment.


 149. Windows SDK: Changing Scroll Bar Widths

 Question:

 The function call GetSystemMetrics(SM_CXHTHUMB) will return the width
 of the scroll-bar thumb box. Is there some way to change this width
 dynamically, i.e., at run time, so that one can have several scroll
 bars, each having different thumb-box widths that are independently
 adjustable in real time?

 Response:

 There is no way to dynamically set the width of scroll bars that were
 supplied when a window was created. However, if you create a window
 without the default scroll bars, you then can create two child windows
 of class scroll that you can control (vary the width).


 150. GDI Simulation Routines and Device Drivers for Windows

 Question:

 We have the following questions about GDI's simulation routines for
 device drivers:

 1. When a smart driver registers its capabilities with GDI, does GDI
    dump the simulation routines that it uses for less intelligent
    display drivers? For example, if we write a driver that can handle
    all the OUTPUT functions, will GDI avoid loading all the simulation
    routines?

 2. Are GDI's simulation routines optimized (like BitBlt), or should we
    duplicate this functionality in our driver code?

 Response:

 The following are answers to your questions:

 1. Yes, GDI dumps simulation routines when more intelligent drivers
    provide the required information. GDI is demand-loadable and
    discardable.

 2. Yes, GDI's simulation routines are optimized.


 151. Using Extra Fields in Windows Class Structure

 Question:

 Is it possible for static variables to be associated with a window
 handle (perhaps as part of the structure that is referenced by the
 handle)? I want to generate several child windows of the same class,
 each having its own set of static variables and independent of the
 sets of variables in the sibling windows.

 Response:

 You need to use the cbWndExtra field in WNDCLASS, the window-class
 data structure, when registering a window; then, use SetWindowWord()
 (or Long) and GetWindowWord() (or Long). These functions either will
 get or set additional information about the window identified by hWnd.

 Use positive offsets as indexes to access any additional bytes that
 were allocated when the window class structure was created, starting
 at zero for the first byte of the extra space. Correspondingly, if you
 want to refer to bytes already defined by Windows within the
 structure, use offsets defined with the GWW and GWL prefixes.


 152. Windows SDK: Changing the Name of a Second Instance

 Question:

 When running a Windows application with multiple instances, we want
 the new instance to realize that it is not the first instance. We also
 want to give the new instance a unique title name and possibly change
 the icon. Is there any way this procedure can be done?

 Response:

 Check hPrevInstance: if it is NULL, this is a new instance of static
 data; if it is not NULL, it is not a new instance of static data. You
 may give the instance a unique name by doing either of the following:

 1. Use CreateWindow() and give it a unique name.

 2. Use GetWindowLong() to access the CREATSTRUCT structure passed when
    processing a WM_CREATE message and specifically change the lpszName
    field (if you want to change the caption name on the existing,
    already named instance).

 You cannot change the icon name dynamically. We use the second method
 to update the icon name from untitled to a unique name.


 153. Windows and IBM Clock Interrupt

 Question:

 Will Windows interfere with the IBM clock interrupt? Are there any
 other hardware-specific parts to Windows?

 Response:

 Windows sits on the IBM clock interrupt. The code that performs the
 clock function is contained in an OEM-written module call SYSTEM.DRV.
 The IBM timer interrupt is used to implement the following:

 1. Get/SetTimer() routines

 2. Least Recently Used (LRU) global-memory segment discarding

 Windows chains to the interrupt. Therefore, as long as you use
 reentrant code, you will not interfere with Windows. You are expected
 to chain to the default timer procedure in the BIOS.

 Windows does not assume anywhere in its code that timer events will be
 accurate; it only assumes they will eventually happen. Thus, chaining
 is not harmful to Windows.

 GetTimer() and SetTimer() deliver their timer messages as close as
 possible to the correct time. The LRU code examines the global memory
 arena every 1/4 second to see if there are any segments it can
 discard.


 154. How Disk Drives Are Determined

 Note: The following discussion is MSINTERNAL because it discusses
 details of the MS-DOS Executive's source code.

 Question:

 How does the MS-DOS Executive determine what drives exist on the
 system?

 Response:

 The MS-DOS Executive does the following to find out what drives exist:

 1. We do a select disk function call (0EH) to get the number of
    logical drives.

 2. We then enumerate the drives sequentially, assuming that they are
    contiguous, using select disk (0EH) and current disk (19h) calls.
    If we do not successfully change drives, the drive does not exist.

 3. If the drive selection is successful, we do a SystemInquire() with
    command set to 1 (get drive information), as shown by the TSYSTEM
    routine. However, we do not use the information returned by the
    call. If the SystemInquire() call is successful, we know the drive
    exists. Additionally, we could use the fact that the disk is a hard
    drive since this is returned by SystemInquire(). However, the
    MS-DOS Executive simply does not use the information.

 4. MS-DOSD has all the code for the formatting. If you cannot use or
    do not want Drive A as the default, you need to fix the device
    driver code to match your needs.


 155. Misaligned Backgrounds after Scrolling and Repainting

 Problem:

 I get a misaligned background when I scroll my window and paint my
 background with the gray stock object brush (i.e., horizontal and
 vertical lines appear in my background).

 Response:

 A gray brush is an 8 x 8 bitmap. It has a particular dithered pattern.
 Imagine a gray brush as consisting of alternating black and white
 lines at a 45-degree angle. If the screen is painted with this
 background brush, it will be filled with 45-degree lines. If we scroll
 the screen up three pixels, we uncover three scan lines that are
 white, and the background must be painted. If we paint with our stock
 gray brush, the background lines will be off by three pixels because
 the brush is an 8 x 8 bit bitmap and we did not scroll a multiple of 8
 pixels. We will get a horizontal line across the bottom of the screen
 at the 3-pixel boundary.


 156. A Discussion on Windows Fonts

 Question:

 I have five questions on fonts, as follows:

 1. How do the default fonts get assigned when building windows? For
    example, the logical font descriptors in the FONTS.ASM file, the
    "fonts oembin PRELOAD fonts.bin" line in the LORES.RC file, and the
    "fonts = xxxx.exe" line in the WIN.INI file all seem to have
    something to do with this process. How are they related and what is
    their purpose?

 2. Exactly what fonts (type styles and point sizes) are provided with
    the FONTS.EXE font resource file? The FONTS.ASM file lists four
    fonts.

 3. When are each of these FONTS.EXE fonts used in Windows?

 4. What is the reason for having each of these fonts?

 5. Are all these fonts needed for each display driver developed?

 Response:

 The following are responses to each of the above questions:

 1. The OEM makes resource files HIRES.RES or LORES.RES which contain a
    resource named "fonts" of type "oembin." This is the file
    FONTS.BIN. The FONTS.BIN that HIRES and LORES contain is an array
    of four LOGICALFONT data structures. These logical font data
    structures describe the four fonts GDI wants to load for the system
    (OEM, ANSI FIXED, ANSI VARIABLE, SYSTEM). The HIRES and LORES files
    are renamed to be a resource for a particular driver (by typing
    "copy HIRES.RES EGAHIRES.RES"). The display driver is then made and
    renamed DISPLAY.EXE by SETUP (by typing "copy EGAHIRES.EXE
    DISPLAY.EXE").

    In its initialization procedure, GDI calls InitFonts(), which loads
    a file named FONTS.EXE. This FONTS.EXE is the one mentioned in
    WIN.INI; it is a library file that contains all of the physical
    font resources (i.e., raster and vector fonts). GDI enumerates all
    of the fonts in the library file FONTS.EXE.

    Later in its initialization, GDI gets the module handle of DISPLAY.
    Using the module handle, it does a FindResource() on "fonts oembin"
    (i.e., the LOGICALFONT structures that make up FONTS.BIN). It then
    loads the logical font resource array and steps though it, doing a
    CreateFontIndirect(). The CreateFontIndirect() will cause the
    physical font resources in FONTS.EXE to be loaded.

    Usually there is a one-to-one correspondence between the
    LOGICALFONT data structures in FONTS.BIN and the physical fonts
    stored in FONTS.EXE. The name of FONTS.EXE may be changed to
    FONTS.FON in the future.

 2. The fonts provided in FONTS.EXE are OEM, ANSI FIXED, ANSI VARIABLE,
    and SYSTEM.

 3. We cannot give you each instance of when each font is used. In
    general, however, the OEM font is used for captions (so they look
    nice) and ANSI FIXED is used for everything else.

 4. The reason for having different fonts is to provide an OEM font
    that looks nice at 10 pitch on the machine. This font is also used
    to determine the height of dialog boxes. The ANSI fonts are
    provided because they are based on proposed standards
    (device-independent) and match Microsoft's virtual-key definitions.

 5. Not all of these fonts are needed for display drivers. Only one font
    is needed; it can be mapped to the three or four logical fonts in
    FONT.BIN.


 157. Windows: Process Sequence of Program with "Export" Functions

 Question:

 We would like to know about the process sequence of a program that
 has "export" functions (such as the Cardfile application). How is the
 order to be called? When and how does Windows interface in its
 process?

 Response:

 The .DEF file exports all functions and procedures that Windows calls
 except for WinMain(). These procedures and functions must be declared
 FAR PASCAL: FAR for the 32-bit address Windows expects, and PASCAL for
 the PLM passing convention. The ordinal numbers you use for your
 defined procedures in your application's .DEF file can be any number
 and in any sequence. If you use a routine from one of the libraries
 (KERNEL, GDI, USER), you need to use its predefined ordinal number.


 158. Windows SDK: GlobalDiscard() Versus GlobalFree()

 Question:

 Please answer the following two questions:

 1. What is the difference between GlobalDiscard() and GlobalFree()?

 2. What is the meaning of GMEM_SWAPPED?

 Response:

 The answers to your questions are as follows:

 1. Discardable implies movable. Discarding an object frees only the
    object, not the associated handle. Locking the handle of a
    discarded object returns NULL; this is how the caller knows that
    the object has been discarded. Freeing an object frees both the
    memory for the object and the handle associated with the object (if
    the object was movable).

 2. GMEM_SWAPPED is an internal flag that tells GlobalLock() that the
    passed handle has been discarded and can be recovered from the swap
    module. There is an internal interface between the memory manager
    and the swap module.


 159. Network Drives as Swap Disk

 Question:
    Can a network drive be used as a swap disk?

 Response:
    Yes, but this procedure is risky. We do not check for duplicate
 filenames, so the use of that disk (as a temporary disk) by more than
 one person could result in problems.


 160. How to Make Outlined Font Characters in Windows

 Question:

 How can I create "outlined" characters of any font the way it is done
 in Windows Paint?

 Response:

 Paint creates an outlined font by drawing the font NINE times. Imagine
 a 3 x 3 square. The font is drawn in all of the positions around the
 outside of the square. Then you reverse the foreground and background
 colors to draw the middle position. This carves out the inside of the
 character. For best results, use the following:

    SetTextCharExtra(hDC, 2);


 161. Windows SDK: How to Define Multiple-Point Fonts

 Question:

 How do you define multiple-sized fonts and how do you use them? Does
 this process have anything to do with the VARIABLE selection in the
 FONT.SIZE pull-down menu?

 Response:

 Multiple-point fonts have nothing to do with the VARIABLE selection in
 the Font editor. This is for VARIABLE PITCH (proportional-spaced)
 fonts. The number of characters per inch that a font allows across the
 page is called its horizontal pitch, and each character has a width of
 so many points (144th-inch), or so many twips (1440th-inch).

 To get a set of different bitmapped fonts (like those handled by the
 Font editor), you have to create separate fonts bit by bit. Each font
 is scaled bit by bit to be a particular point size (horizontal pitch).
 You then may save the font you created as having a certain point
 (pitch) size. This is a .FNT file.


 162. Text Full Error Using ReadComm() in Windows

 Question:

 In $SNDCOM, the comerr word has the CE_TxFull bit set if the transmit
 queue is full. This process causes a problem in $RECCOM, which checks
 the comerr word for any errors before getting a character from the
 queue. If the CE_TxFull bit is set in comerr, no characters can be
 retrieved from the Rx queue. An error in the Tx queue should not
 inhibit receiving characters in the Rx queue. Is there a workaround
 for this problem?

 Response:

 The CE_TxFull bit is set only if an attempt is made to place a
 character and there is no room in the queue for the character. At this
 time, the CE_TxFull bit will be set and an error returned to the
 application. If the application then attempts to do a ReadComm(), it
 has ignored the error that was returned from WriteComm(). Windows
 considers the application to have failed to handle an error condition.
 If, on the other hand, the application responds correctly to the error
 by doing a GetCommError(), CE_TxFull is cleared and the call to
 ReadComm() will succeed.


 163. Making .FON Files without the RC Compiler

 Question:

 Is there any information on how to build a .FON file (i.e., a Windows
 Version 2.x font file) without using the RC compiler?

 Response:

 Yes. There is a document in the Software Library that gives you the
 information that you need. This file can be found in the Software
 Library by searching on the filename FONTRES.ARC, the Q number of this
 article, or S10039.


 164. EXEMOD Cannot Be Used With Windows

 Problem:
    I have encountered a possible problem with Windows and files with
 EXE headers modified by EXEMOD. When a program is loaded that does not
 have the original header created by link, unexpected results happen
 with the file. In my case, some of the code in a file modified in this
 fashion was corrupted.

 Response:
    EXEMOD is a utility to use with only DOS Versions 2.x and 3.x EXE
 header formats. Windows uses a modified DOS Version 4.0 header format
 for the EXE file. We do not recommend using EXEMOD on a Windows file
 (not supported and not shipped with Windows).


 165. Information about BS_USERBUTTON

 Question:

 Can you provide more information concerning how to create and use
 BS_USERBUTTON?

 Response:

 A user button, BS_USERBUTTON, is a user-definable button that notifies
 the parent when the button is pushed. The notification includes a
 message to paint the button, and to highlight or disable it.

 For BS_USERBUTTON styles, the low-order word of lParam parameter
 contains the button's window handle, and the high-order word
 contains one of the following notification codes:

 BN_CLICKED      The button has been clicked or the SPACEBAR is pressed
                 when the button is active
 BN_PAINT        Request to repaint the button
 BN_HILITE       Request to highlight the button
 BN_UNHILITE     Request to remove the highlight
 BN_DISABLE      Request to draw a disabled button


 166. Unframed Strings as Buttons in Dialog Boxes

 Question:

 It seems that a string without a frame is often used in the same way
 as BUTTON is used in a dialog box. In the MS-DOS Executive, how is the
 segment to display the pathname (e.g. /windows/test) written?

 Response:

 The procedure for displaying the pathname is as follows:

 1. Use DrawText() with lpRect clipped to the rectangle specified.

 2. Determine the width with modulo arithmetic to determine which
    character was hit.

 3. Look at the character string pointed to from the array and check
    for the "/" (forward slash) character, etc.

 4. Perform the desired action (i.e., change directories in the MS-DOS
    Executive application).


 167. Exchanging Window Handles

 Problem:

 I have two cooperating programs that have exchanged window handles.
 One of these programs is serving as a slave to the other, which I call
 the master. When an individual closes the master program, I wish to
 close the slave program's window and terminate it.

 If the window held by the slave is closed by the master using
 DestroyWindow(), the slave program's window is destroyed, but the slave
 never receives a WM_DESTROY message.

 Response:

 Messages are sent to the task that created the window (i.e., the
 master). Note that the Windows message dispatcher has no means of
 "knowing" that a window handle has been exchanged.

 You should send a WM_DESTROY message using PostAppMessage() to your
 slave program. Do not exchange window handles even though it is
 permissible to do so.


 168. How to Use the Control Classes in Windows

 Question:

 How do you implement radio buttons, check boxes, scroll bars, and edit
 text fields (seen in dialog boxes) in a window? I noticed that in
 PIFEDIT there are radio buttons, etc., in a window.

 Response:

 The procedure is specified in the "Microsoft Windows Software
 Development Kit Programmer's Reference" under CreateWindow(). There
 are parameters under this function for Classname and Style. The styles
 can be any of the control styles in addition to the standard window
 styles.

 In our example, we create a standard window with the following styles:

     if ( hWindow = CreateWindow((LPSTR) "shell",
                         (LPSTR) "Interactive Shell Example!",
                         WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
                         0,    /*  x - ignored for tiled windows */
                         0,    /*  y - ignored for tiled windows */
                         0,    /* cx - ignored for tiled windows */
                         0,    /* cy - ignored for tiled windows */
                         (HWND)NULL,      /* no parent */
                         (HMENU)NULL,  /* use class menu */
                         (HANDLE)hInstance, /* handle to window instance */
                         (LPSTR)NULL        /* no params to pass on */
                         ) ) {

 We then make another window with the following style within the
 overlapped window with a class name of LISTBOX. Valid class names for
 these windows are BUTTON, EDIT, STATIC, LISTBOX, and SCROLLBAR. For
 each class, the appropriate styles apply (whether the window is a
 dialog or overlapped). The following code illustrates making a window
 of class "LISTBOX" within the titled window:

     hInst = hInstance;
     ShowWindow( hWindow, cmdShow );
     UpdateWindow( hWindow );

     if ( hLBox = CreateWindow(
                 (LPSTR) "ListBox",
                 (LPSTR) "",
                 WS_CHILD | WS_VISIBLE | LBS_STANDARD,
                 10, 80, 120, 120,
                 hWindow,
                 1,
                 hInstance,
                 (LPSTR) NULL ) ) {


 169. Using Additional Pen Styles in Windows

 Question:

 In Windows, the CreatePen() and CreatePenIndirect() calls only allow
 you to create a pen with predefined styles (solid, dash, dot, etc.).
 Is it possible to create a pen with my own style?

 Response:

 It is not possible to create a pen with your own style. You have use
 only of the stock pens and styles provided with Windows.


 170. Changing Window Colors with Control Panel

 Question:

 In Windows, the screen background color and the test color can be
 changed under the Control Panel. If I go into Control Panel and choose
 a dark background instead of light and choose a light text instead of
 dark, the following programs will be affected: Notepad, Clock, Control
 Panel, Calendar, Clipboard, MS-DOS Executive, PIF Editor, Spooler,
 Terminal, and Write. The programs not affected are Calculator,
 Cardfile, Paint, Reversi, and COMMAND.COM. Why is this so?

 Response:

 It is up to the application to look in the WIN.INI file to see what
 you have selected as a [color] preference. Some applications do not
 change because either they do not look in the WIN.INI file for those
 colors, or the applications choose to ignore them.


 171. Windows and Multitasking (Nonpreemptive Scheduling)

 Question:

 Does Windows do preemptive scheduling? How does preemptive scheduling
 differ from nonpreemptive scheduling?

 Response:

 No, Windows does a type of scheduling called nonpreemptive scheduling.
 In this type of scheduling, an application is not forced out of
 context asynchronously (i.e., it is not preempted). Instead, the
 application runs until it explicitly gives up control. Windows-aware
 applications give up control through various system calls. Although
 they are not aware of it, "good" old applications give up control
 whenever they attempt various I/O functions.


 172. MAZE Sample Program from "Microsoft Systems Journal"

    The following is a summary of the MAZE program found in the
 "Microsoft Systems Journal" and in the Software Library:

    Issue: November 1987 Vol. 2 No. 5 Page 13
    Author: Kevin Welch
    Filename: MAZE

    This file can be found in the Software Library by searching for the
 filename MAZE.ARC, the Q number for this article, or S10007.
    The MAZE program demonstrates how to use dynamic data exchange
 (DDE) in your windows applications. It demonstrates this by setting up
 a DDE communication link between instances of the MAZE application and
 then bouncing a ball within the applications and, at random, passing the
 ball to other instances through the standard DDE protocol. It also
 sets up a DDE link with Excel and passes statistics concerning the
 ball to Excel as a demonstration of how to do DDE with Excel.
    Some of the Windows programming concepts used in MAZE are as
 follows:

    1. Using DDE between applications
    2. Using PeekMessage to allow other applications to run while the
       ball is bouncing


 173. Rules for Locking Data

 Question:

 Under what circumstances are LockData() and UnlockData() needed to
 prevent long pointers from being invalidated by segment movement?

 Response:

 The rules for data segment locking are as follows:

 1. An application's data segment is created with a lock count of one.

 2. When a call to GetMessage() or PeekMessage() is made, the lock
    count of SS (which is the same segment as DS, since SS==DS in an
    application), is decremented by one before relinquishing control to
    another application. The lock count is incremented again when it
    returns. Thus, while another application is running, the DS has a
    lock count of 0 and is free to move around, unless the application
    has incremented the lock count.

 3. LocalAlloc() and LocalRealloc() can decrement an application's data
    segment lock count by one to expand the local heap.

 These rules imply that only GetMessage(), PeekMessage(), LocalAlloc(),
 and LocalRealloc() can invalidate outstanding 32-bit pointers.
 LockData() and UnlockData() are necessary only if these calls are made
 and 32-bit pointers are outstanding.

 If the application's data segment is moveable, it must not keep any
 outstanding 32-bit pointers into DS past any call that can unlock and
 move DS, unless the application uses LockData() to increment the lock
 count and prevent movement. A typical outstanding 32-bit pointer would
 be of the form

    lpChar = (LPSTR)(address of a global or frame variable)

 or

    lpChar = (LPSTR)LocalLock(...)

 Calls of the following form create a 32-bit pointer, but are safe
 because DS is locked and TextOut() doesn't unlock it:

    TextOut(..., (LPSTR)"Hello", ...)

 Notes
 -----

 1. MessageBox() and DialogBox() both call GetMessage() implicitly.
    AddAtom() calls LocalAlloc() implicitly.

 2. In a DLL, SS!=DS so that GetMessage(), PeekMessage(), and
    WaitMessage() decrement the lock count on only SS so that the DS's
    lock count remains unchanged. LocalAlloc() and LocalRealloc()
    decrement the lock count of DS without affecting SS.

 3. WM_CREATE messages contain an lParam, which is a far pointer to a
    CREATESTRUCT structure. This structure resides in the application's
    stack, so this long pointer will be invalidated if you cause your
    DS to move while processing WM_CREATE.


 174. Windows SDK: Cannot Use WS_CHILD Style for Dialog Boxes

 Problem:

 If a dialog resource is created with the style WS_CHILD rather than
 WS_POPUP, both the parent window and the dialog box are disabled when
 the dialog is created through either CreateDialog() or DialogBox().

 Response:

 The use of WS_CHILD instead of WS_POPUP for dialog boxes is not
 allowed because there is no parent to activate the dialog.

 It is important to consider who the parent is. In this case, the
 Windows USER code is the parent; the current window is not the parent.
 Remember that messages flow through the parent to the child.
 Therefore, a dialog box with the style WS_CHILD will not work properly.


 175. Creating a List Box without a Scroll Bar

 Question:

 I would like to create a dialog box that contains a list box without
 the vertical scroll bar. I specified the following style

 LBS_NOTIFY | LBS_SORT | WS_BORDER | LBS_STANDARD

 but the dialog box that is created has the vertical scroll bar.

 I looked at the .RES file produced by RC, and found that it set the
 style to include WS_VSCROLL. Is there any way that a list box control
 in a dialog box can be created without a vertical scroll bar?

 Response:

 When you use LBS_STANDARD, you get the following:

 LBS_STANDARD = #00A00003; /* LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER *

 as defined in WINDOWS.H, which includes WS_VSCROLL.

 In this case, you can use NOT WS_VSCROLL as the style for creating a
 list box control without a vertical scroll bar.


 176. Windows SDK: Reference Counts for Dynamic Libraries

 Question:

 Windows frequently mentions a library/module reference count. How and
 when is the reference count updated?

 Response:

 Libraries are reference counted and remain resident in memory until
 they cease to be referenced.

 When a task module is started and is dynamically linked to the
 library, the reference count is incremented by one. When the task
 module is terminated, the reference count is decremented by one.

 Two functions, LoadLibrary() and FreeLibrary(), affect the reference
 count. LoadLibrary() increments the reference count by one while
 FreeLibrary() decrements the reference count by one.


 177. Using GlobalNotify and GMEM_NOTIFY to Set Notification Routine

 Question:
    Page 324 of the Version 2.00 "Microsoft Windows Software
 Development Kit Programmer's Reference" guide states that the
 GMEM_NOTIFY flag "calls the notification routine if memory is about to
 be discarded." How do I set the address of the notification routine?

 Response:
    There is a function called GlobalNotify that is used to set the
 notification routine. It is prototyped as follows:

    VOID   FAR PASCAL GlobalNotify( FARPROC );

    You should replace any definition of GlobalNotify in the WINDOWS.H
 file with the above.
    The notification function has the following form:

       BOOL FAR PASCAL NotifyProc(MemSegment)
       WORD MemSegment;
       {
       }

    The MemSegment is the segment about to be discarded. You should
 return FALSE if you do not want the memory item to be discarded.
    The following is an example of how this might be used:

       hMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_DISCARDABLE
                             |GMEM_NOTIFY,50000);
       lpnotify = MakeProcInstance(NotifyProc,hInst);
       GlobalNotify(lpnotify);

    You should note the following things about using GlobalNotify:

    1. It can only be called once per instance.
    2. It cannot be called from DLL.
    3. The notification function must be exported.


 178. Windows SDK: Problems Running Bad Applications

 Question:

 I tried to run an old application with a PIF file and with the Windows
 Clock running, but I could not get the application to run from
 Windows. Windows gave the message "Cannot run with other applications."
 What is the reason for this message?

 Response:

 This message is displayed when a Windows-aware application (tasking)
 is running and a bad application wants to run. Because Windows must
 swap out, it cannot save the current state to allow both to run at the
 same time.

 An example of this is trying to run a terminate-and-stay-resident
 (TSR) old application (with the memory bit checked in the PIF file)
 when other Windows applications are running (besides MS-DOS), or when
 other old applications are running. You cannot exit Windows to run
 this TSR application; if you do, when Windows returns there is no way
 to restart those other applications that are also running. The message
 "Cannot run with other applications" then results.


 179. Windows SDK: Using EM_SETHANDLE in an Edit Control

 Question:

 When setting the EM_SETHANDLE handle, what happens to the old edit
 control handle? If it is deleted, is the memory associated with it
 also freed?

 Response:

 After EM_SETHANDLE, control reallocates the buffer if the buffer must
 grow as user types, pastes, etc., and characters are inserted. Control
 also reallocates if the buffer must shrink.

 When EM_SETHANDLE is sent to an edit control, the old handle is not
 destroyed. This is the responsibility of the application. If you have
 done previous SETHANDLEs, you should do LocalFree() to free the space.


 180. Translating Client Coordinates to Screen Coordinates

 Question:

 No matter where I place my Window on the screen, the GetClientRect()
 call sets the ".left" and ".top" fields of the RECT structure to 0.
 How can I find out where my window is on the screen?

 Response:

 The GetClientRect() function will always return (0,0) as the
 coordinates for the origin of your window. This behavior is correct
 and is documented on Page 261 of the "Microsoft Windows Software
 Development Kit Programmer's Reference," as follows:

    "Since client coordinates are relative to the upper-left
     corners of a window's client area, the coordinates of the
     upper-left corner are (0,0)."

 If you want to find out the screen coordinates for the client area of
 your window, use the API call ClientToScreen() to perform the
 translation for you. The following code demonstrates how to use the
 two functions together:

    RECT rMyRect;
    GetClientRect ( hwnd, (LPSTR)&rMyRect);
    ClientToScreen( hwnd, (LPPOINT)&rMyRect.left);
    ClientToScreen( hwnd, (LPPOINT)&rMyRect.right);


 181. Windows SDK: Dialog-Box Style DS_ABSALIGN

 Question:

 In WINDOWS.H, what does the term DS_ABSALIGN mean?

 Response:

 The dialog-box style DS_ABSALIGN means "Dialog Style ABSolute ALIGN".
 Using this term will place the dialog box in relation to the physical
 screen's origin versus the window's origin.

 Use this term when you want the dialog always to start in a specific
 part of the display, no matter where the window may be on the screen.


 182. Buffers Required by Bitmaps in Windows

 Question:

 I have an application with a bitmap that is quite large. I read off
 parts of the bitmap from disk when needed. Is there a way that I can
 have a bitmap that uses the memory that I have allocated? If so, I
 would not need both my own GlobalHeap disk buffer and a bitmap buffer.
 Having these two buffers doubles my memory requirements. Is there a
 way to do bitblts from my own memory pool?

 Response:

 There is no way to do bitmaps directly from a buffer other than a
 memory display context created by CreateBitmap(), etc. So you will
 need two buffers. If this is a problem, you might want to do banding
 into a smaller buffer, rather than reading all of the bitmap
 information into a large buffer.


 183. Appending Text to the EditText

 Question:

 Windows allows an application to specify a local handle to use as a
 buffer for edit controls (and/or edit child windows). Does this imply
 that the application is allowed to directly access or modify the edit
 control buffer's contents? For example, if I want to append some text
 to the end of the control's existing text, is it permissible to simply
 access that memory and insert the desired text? If not, what is a
 quick way to simply add text onto the end of an edit control's buffer?

 Response:

 You can append text to the EditText Control's text buffer, but you
 must do it carefully to maintain consistency. We suggest that you set
 the selection to the end of the text buffer, then do a REPLACESEL to
 add text at that place. You probably will want to turn off redrawing
 while you do this; also, get the current selection before appending so
 you can restore the current selection after appending. The following
 sequence of instructions should accomplish your goal:

 LONG lSel, tend;

 /* disable redrawing */
 SendMessage(hwndEdit, WM_SETREDRAW, 0, 0L);

 /* get the current selection */
 lSel = SendMessage(hwndEdit, EM_GETSEL,...);

 /* find end of text. */
 tend = SendMessage(hwndEdit, WM_GETTEXTLENGTH, 0, 0L);

 /* Now reset the selection to an insertion point at the end of text */
 SendMessage(hwndEdit, EM_SETSEL, 0, MAKELONG((LOWORD)tend,(LOWORD)tend));

 /* This appends the text. */
 SendMessage(hwndEdit, EM_REPLACESEL, 0, lpText);

 /* Restore the selection to where it was */
 SendMessage(hwndEdit, EM_SETSEL, 0, lSel);

 /* enable redrawing */
 SendMessage(hwndEdit, WM_SETREDRAW, 1, 0L);


 184. Windows SDK: Definition of "Libraries"

 Question:

 The word "library" is used to mean three different things in Windows.
 Can you clarify the meaning of "library"?

 Response:

 The following is a list of definitions of the word "library":

 1. Object libraries: Produced by LIB.EXE, consist of INTEL object
    code, e.g. SWLIBC.LIB (the W implies the objects were compiled
    using Windows prolog conventions).

 2. Dynamic link libraries: Produced by IMPLIB.EXE and LIB.EXE.;
    contain dynamic link records derived from GDI.DEF, USER.DEF, and
    KERNEL.DEF; contain Windows start-up routine WINSTART.OBJ; contain
    any routines necessary to override C run-time functions (e.g.
    calloc and malloc), i.e., Windows-specific run-time functions.

 3. Executable libraries: Produced by LINK4.EXE; use the keyword
    LIBRARY in .DEF file; contain sharable Windows code, e.g. GDI.EXE,
    USER.EXE, KERNEL.EXE.


 185. Edit Control Size Limit in Windows

 Question:

 What is the maximum size limitation on the amount of text that can be
 associated with an edit control?

 Response:

 The absolute limit is 32K; however, keep in mind that anything more
 than 16K will not be practical because it takes too long to update or
 scroll.


 186. Use of Allocations with cbClsExtra and cbWndExtra in Windows

 Question:

 Would you explain the use of the allocations with cbClsExtra and
 cbWndExtra?

 Response:

 The use of the allocations with cbClsExtra and cbWndExtra is as
 follows:

 1. cbClsExtra -- extra bytes to allocate to CLASS data structure in
    USER.EXE local heap when RegisterClass() is called. Accessed by
    Get/Set CLASS Word/Long ();.

 2. cbWndExtra -- extra bytes to allocate to WND data structure in
    USER.EXE local heap when CreateWindow() is called. Accessed by
    Get/Set WND Word/Long ();.

 These structures may be used at your discretion and for any purpose
 you desire.


 187. Producing a Beep in Windows

 Question:
    What is the simplest way to produce a beep (a MessageBeep, but
 without a message box) in Windows?

 Response:
    Use the MessageBeep() function. The parameter listed as wType is
 currently ignored by Windows, so use a 0 for the parameter instead.
 The listing for wType on Page 373 of the "Microsoft Windows Software
 Development Kit Programmer's Reference Guide" is an error.


 188. Using Fonts and Freeing Memory

 Problem:

 I am using CreateFont, SelectObject, and DeleteObject in a small
 Windows application. Although I use DeleteObject after using the font,
 the memory used by the font seems to never be freed.

 Response:

 A font must always be deselected from a DC before deletion. The
 complete sequence for creating, selecting, using, and deleting fonts
 is as follows:

 HDC hDC = CreateDC( ... );  /* Or = GetDC( hWnd ) */
 HFONT hFont = CreateFontIndirect( ... );
 HFONT holdFont = SelectObject( hDC, hFont );

 TextOut( hDC, ... );
 SelectObject( hDC, holdFont );
 DeleteObject( hFont );


 189. Clipboard Memory Sharing in Windows

 Question:

 I have the following questions about Clipboard memory sharing:

 1. Does the Clipboard UNLOCK before freeing the handle when I tell it
    to SetClipboardData()?

 2. Does the Clipboard actually copy my global storage to another
    block, or does it just retain the value of my handle for
    referencing my block?

 3. Does GetClipboardData() remove the data from the Clipboard, or does
    it allow me to reference the data without removing it from the
    Clipboard?

 Response:

 The following are answers to your questions:

 1. Yes, the Clipboard UNLOCKs before freeing the handle when you
    SetClipboardData().

 2. The Clipboard is sharable; it retains the value of the handle.

 3. The data handle returned by GetClipboardData() is controlled by the
    Clipboard, not by the application. The application should copy the
    data immediately, instead of relying on the data handle for
    long-term use. The application should not free the data handle or
    leave it locked. To remove data from the Clipboard, call
    SetClipboardData().


 190. UnlockData and DLLs

 Question:

 When a dynamically linked library creates a local heap in its data
 segment by calling LocalInit(), the data segment is locked throughout
 the life of the library.

 Is there any way to have the data segment unlocked (except when the
 library is being called) in the same way that the data segment of an
 application is unlocked except when the application is running? Also,
 is it legitimate to unlock the data segment by calling UnlockData() in
 the library initialization routine immediately after calling
 LocalInit()?

 Response:

 A side effect of calling LocalInit() is that the data segment is left
 locked; therefore, you should call UnlockData() before returning from
 your DLL initialization routine. Listed below is a sample library
 initialization code that calls these two functions; it also places the
 instance handle of the DLL in a public variable that your C routines
 of your DLL can use if the DLL instance handle is ever needed.

 The following is a sample code:

 ; Library initialization code
 ; Assemble: MASM -i\include filename.asm;
 ; Link with other DLL object files.

 ?PLM = 1;
 ?WIN = 1;
 memS = 1

 include cmacros.inc

 externFP    <LocalInit>
 externFP    <UnlockSegment>

 ; PUBLIC definition of LIBINST, the library instance
 sBegin  DATA
         assumes DS,DATA
         PUBLIC  _LIBINST
 _LIBINST dw     ?
 sEnd    DATA

 sBegin CODE
 assumes CS,CODE

 ; DLLastart is the entry point for the Dynamic Link Library. This entry
 ;   point is called once, when the DLL is loaded.
 cProc DLLastart,<PUBLIC,FAR>
 cBegin
         ;;
         ;; DS = automatic data segment.
         ;; CX = size of heap.
         ;; DI = module handle.
         ;; ES:SI = address of command line (not used).
         ;;

         mov    _LIBINST,di
         xor    ax,ax
         cCall  LocalInit,<ds,ax,cx>
         mov    ax,0ffffh
         cCall  UnlockSegment,<ax>
 cEND    myastart

 sEnd   CODE

 end DLLastart


 191. Panning and Scrolling in Windows

 Question:

 For the task of panning and scrolling, would you recommend the
 following?

 1. Skew the viewport origin with respect to the logical window origin
    using the SetViewportOrigin() and SetWindowOrigin() functions.

 2. Use ScrollWindow() to move the picture vertically across the
    screen.

 3. Use BitBlt to fill in the uncovered area.

 Do you need to keep track of the shift in source bitmap origin, or
 does Windows do that for you? Can InvalidateRect(), ShowWindow(), and
 UpdateWindow() help in this regard? If so, how?

 Response:

 When you use bitmaps, the mapping mode is ignored and you use physical
 units (i.e., MM_TEXT pixels). There is little reason to use the
 extend/origin routines to keep track of the logical origin. However,
 there is nothing to keep you from doing so.

 If you want to scroll and there are no child windows in the client
 area, it is best to BitBlt the client area to scroll it and PatBlt the
 uncovered area with the default brush.


 192. Memory Swapping in Windows

 Question:

 Could you give me some information about swapping in Windows and
 swapping in WINOLDAP?

 Response:

 The swap interface has not been defined at this time. WINOLDAP allows
 swapping only because it has its own swapping code.

 The only swapping that Windows allows is discarding of "pure code."
 Windows can discard pure code because it "knows" it can safely get
 another copy of the code off the disk from which it was originally
 loaded. If your program consists of many small segments of pure code,
 there are no limits as to the size. For example, your program may have
 one megabyte of code as long as no segment exceeds 64K and the total
 size of all fixed segments does not exceed available memory. Note,
 however, that there is a 255-segment limit in the loader, so your
 program cannot have more than this number of segments in it. Windows
 will continue swapping discardable code segments as needed, using a
 Least Recently Used (LRU) algorithm to decide which segments to
 discard.

 Dealing with "old" (non-Windows) applications is a little different.
 Windows cannot discard pieces of an old application; all of it must
 fit into memory simultaneously. However, Windows can swap the entire
 old application out of memory to make room for other old applications
 to run. If an old application uses overlays, it will take up less
 space in memory, but Windows still won't discard any of it; the
 application's own overlay manager is responsible for discarding and
 re-reading overlayed code.

 To determine the correct .PIF settings for a non-Windows application,
 use the EXEMOD or EXEHDR utilities to find the minimum load size; you
 may have to adjust this value to get the application to run correctly.
 Windows uses the .PIF settings to determine how big to make the
 swapsize for old applications.

 The following additional information on the SWAPSIZE parameter set in
 the WIN.INI file may help clarify matters for you:

 1. SWAPSIZE is a method used to override the standard
    partition-size computation.

 2. If you are running two old applications, load the largest one
    first; otherwise, it might not run. SWAPSIZE provides a method to
    override the partition-size computation and force a particular
    partition size.

 The size of the old-application partition Windows tries to establish
 is the following:

    MAX (SWAPSIZE pifMaxMemory)


 193. Windows "Pictures" Bigger Than the Screen

 Problem:

 I have a toolkit programmer that is designing a program to allow you
 to edit graphics pictures that are larger than the current screen.
 However, when the programmer draws to a bitmap and then scrolls, the
 portion that went off the screen does not return.

 Response:

 In Windows, you must repaint any portion of the screen that is
 obscured for any reason.

 Windows is a repainting system. However, Windows does not store any
 graphics that scroll off the screen. It cannot repaint when the
 graphics are scrolled back on the screen; you must remember the
 graphics commands and redo the graphics. Alternatively, you can save
 the portion that is to be scrolled off the screen in a bitmap, then
 return this portion to the screen when you scroll back.

 There are logical problems associated with saving items that scroll
 off the screen. For example, if you logically write into the virtual
 bitmap that is off the screen, it may not be possible to see what was
 logically written when it scrolls back.


 194. Passing and Discarding Handles in Windows

 Question:

 If Program A passes the handle of its own icon to Program B and A then
 terminates, can B continue to safely use the icon handle?

 If this icon is discardable, will Windows reload the icon properly if
 B tries to use it after A terminates and the icon has been discarded?

 Response:

 The answer is no to both questions. Icons are resources, resources are
 segments, and segments are global memory blocks. Global memory blocks
 are not equivalent to task handles; if a segment is discarded, it
 invalidates the handle.

 You might use a sharable library or property lists for management of
 data handles; however, you must be careful. Only shared, global memory
 handles (e.g. GDI drawing objects) can be used by other applications.
 Global memory handles can be shared with other applications through
 the Windows Clipboard. Local memory handles cannot be shared.


 195. Windows' Use of Interrupts

 Question:

 Could you please summarize the use of interrupts made by Windows,
 specifically the following?:

 1. DOS interrupts trapped by Windows

 2. Specific Windows interrupts

 Response:

 Windows traps the following interrupts:

 1. Interrupt 21: any function codes dealing with character I/O to
    STDIN, STD OUT and STDERR, e.g. IOCTL, OPEN, READ, WRITE, etc.

 2. BIOS interrupt 10: Any function codes dealing with character I/O.
    Partial TOP/VIEW emulation.

 3. Interrupt 20, 21, 24: Any function codes dealing with EXEC, ALLOC,
    REALLOC.

 Windows uses only the following specific interrupt:

    Interrupt 3F - the dynamic link interrupt


 196. F10 Functions Like ALT

 Question:
    When I press F10, Windows sends my application a WM_SYSKEYDOWN
 instead of a WM_KEYDOWN message to my application. Is this a problem
 or a feature?

 Response:
    The F10 key under Windows Version 2.03 is reserved. Like the ALT
 key it will set the menu mode. This is documented in the Version 2.03
 SDK documentation on Page 44 of the Microsoft Windows application
 style guide.


 197. Format Of LocalInit

 Question:

 The LocalInit routine described in the advanced Windows programmer's
 class can be used to set up a local heap for a dynamic library.
 However, all of the examples show the call being made in the following
 way:

 LocalInit (0, 0, HeapSize)

 How should I call LocalInit if I have some static variables I want to
 keep in my data segment also? I have an old piece of documentation
 that says that the second parameter is a pointer to the start of the
 heap, and the third parameter is a pointer to the end of the heap.
 From that, I would guess that an appropriate call would be as follows:

 LocalInit (0, &Heap, &Heap + HeapSize)

 In this case, Heap is a character array that I guarantee to be
 allocated after any variables I want to keep. Is there a better way to
 determine the size of my static globals?

 Response:

 Your intuition is correct. The call is as follows:

 LocalInit (0, &Heap, &Heap + HeapSize)

 There is no better way to do it. However, if the start of the heap is
 0, the offset address of the end of the segment is calculated and the
 third parameter is subtracted from the end to yield the second
 parameter (start of heap). This means that although you may have
 static data in your segment, the heap is placed correctly. It is
 always placed at the end if the second parameter is 0. The following
 is always used because it places the heap at the end of the segment
 without any offset calculations that might be calculated incorrectly:

 LocalInit (0, 0, HeapSize );

 All of this implies that segments initialized for local allocation
 cannot be more than 64K in size.


 198. How to Obtain the Windows SAE License Agreement

 The SAE Licensing Agreement is an agreement between an ISV and
 Microsoft that allows the ISV to distribute the Windows run-time
 routines needed to support an application in a single application
 environment (SAE). There is no fee for this license.

 To obtain a copy of the Microsoft Windows SAE Licensing Agreement,
 call the Microsoft Information Center at (800) 426-9400. Identify
 yourself as a Windows ISV and ask for a copy of the agreement.


 199. Redraw Bug When Setting Empty Menu

 Windows does not draw the caption of the main tiled window when it is
 created with an empty menu, i.e., a menu created with CreateMenu() but
 without any items added to it.

 To correct this problem, do not have CreateMenu() inside the
 CreateWindow() function. Instead, use NULL.


 200. Windows SDK: Long INCLUDE Variable Causes RC EXEC Failure

 Setting the MS-DOS environment variable INCLUDE to a long string (more
 than 71 characters) causes Version 2.10 of RC.EXE to fail with the
 following error message:

    EXEC Failed - make sure RCPP.EXE is installed correctly.

 This error occurs even though RCPP is installed correctly.

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.



 201. Maximum Number of Fonts

 The maximum number of Font Table entries is 253.


 202. Discarding and Reloading Fonts

 Question:

 If Windows discards a font, will it automatically reload the font when
 it is used, or will it return a NULL handle to the caller?

 Response:

 If there is enough memory, Windows will reload the discarded font(s).
 If there is not enough memory, it will fail but will leave the
 currently selected font in the DC alone. However, you are guaranteed
 the success of selecting GetStockObject() on the System font.


 203. DC Storage in Memory under Windows

 Question:

 When a DC is created for a bitmap, are the bits stored in local or
 global memory and are they fixed or movable? If the bits are in local
 memory, how can we get a DC for a device with a resolution greater
 than 512K pixels (64K)?

 Response:

 Bitmaps are always in global memory and movable.


 204. ES_ Omissions from WINDOWS.H File

 Question:

 ES_NOTEFOCUS, ES_NOCHANGE, and ES_NOTECLICK are not defined in
 WINDOWS.H. What are their definitions?

 Response:

 The definitions are as follows:

    #define ES_LEFT            0L
    #define ES_CENTER          1L
    #define ES_RIGHT           2L
    #define ES_FRAMED          4L
    #define ES_NOTEFOCUS       8L
    #define ES_NOCHANGE        16L
    #define ES_NOTECLICK       32L


 205. Borders Around and Icons in Dialog Boxes

 Question:

 How do you adjust for the proper placement of an icon in a dialog box?
 How do you get attractive borders on dialog boxes?

 Response:

 To create a dialog box with attractive borders, use the STYLE keyword
 in your dialog template as follows:

 mydialog DIALOG 10, 10, 100, 50
 STYLE WS_DLGFRAME | WS_POPUP | WS_VISIBLE
 BEGIN
   ...
 END

 To include an icon in your dialog box, use something similar to the
 following in your dialog template:

 CONTROL "ResourceName", id, STATIC, SS_ICON, x, y, 0, 0

 where id is the control ID, and x and y are the horizontal and
 vertical positions of the top left corner of the icon. Note that the
 last two parameters are ignored since the icon automatically sizes
 itself to the appropriate size. "ResourceName" is the resource name of
 the icon resource, which must be in the same resource file as the
 dialog template.


 206. Windows SDK: List Boxes and Horizontal Scroll Bars

 Page 198 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" for Versions 2.x implies that horizontal
 scroll bars are allowed in the list box class. Only vertical scroll
 bars are supported in the list-box class.


 207. Scroll Bar Controls in Windows

 Problem:

 We have a vertical scroll bar in a dialog box but are not receiving a
 WM_COMMAND message when the scroll bar is hit with a mouse click.

 Response:

 The scroll bar controls do not submit notification messages as
 WM_COMMAND messages. Instead you should get a WM_VSCROLL or WM_HSCROLL
 message with a wParam value set to the window handle of the scroll
 control.


 208. How to Open More Than 20 Files

 To have more than 20 files open at a time you must do the following:

 1. Use DOS Version 3.30 or later.

 2. Include FILES=N in CONFIG.SYS, where N indicates the number of file
    handles you have available. (Under DOS Version 3.30, the maximum
    number of file handles you can request is 255). If you do not
    include FILES= in your CONFIG.SYS, you will have eight handles by
    default. The following five handles are already open by DOS:

    STDIN
    STDOUT
    STDERR
    STDAUX
    STDPRN

    So, you are left with three available file handles.

 3. Call DOS interrupt function 67h (Set Handle Count) to increase the
    number of file handles.

 The following code fragment demonstrates how to obtain 40 file
 handles. From here, you can use the OpenFile() function to obtain up
 to 35 file handles, since five are already open from DOS:

 #include <dos.h>

 union REGS inregs, outregs;

     inregs.h.ah=0x67;
     inregs.x.bx=40;
     intdos(&inregs, &outregs);

 Also, include the following line in your CONFIG.SYS file:

 FILES=40


 209. DrawText() Uses Current Font

 Question:

 Does DrawText() use the current font for drawing text within its
 control rectangle? If the text will not fit in the bounding rectangle,
 will a smaller size be used?

 Response:

 DrawText() uses the DC it is passed to do TextOut() calls. Thus, the
 font currently selected into the DC is used. Text is not resized to
 fit into the bounding rectangle.


 210. Windows SDK: Double Return Value in Medium Model

 Problem:

 Memory in the data segment that should be occupied by global variables
 is being corrupted when a function returning a double returns. I am
 programming under medium model.

 Response:

 A problem exists in the C compiler, which attempts near resolution of
 a far location if the -Gsw switch is used when compiling a routine
 returning a double under medium model.

 To work around this problem, pass the address of a double variable as
 an additional parameter to the routine, have the routine write the
 value to that address, and have the routine return a BOOL indicating
 success or failure in its calculations.

 A second workaround would be to declare the routine PASCAL. Since the
 calling convention is performed differently, the same error does not
 occur.


 211. CreateFont() Height Parameter

 Question:

 The reference manual describes the Height parameter of CreateFont() as
 being in "user units." It also uses the term "cell height" without
 defining it. What are the semantics of these terms?

 Response:

 The Height is the distance from the top of any internal leading to the
 bottom of any descenders. Cell height is the Height less internal
 leading. User units are the units set by SetMapModes(). These also are
 known as logical device coordinate units. MM_TEXT (pixels) is the
 default.


 212. When to Use LockData(), UnLockData()

 Problem:

 Please document which Windows routines make memory-management calls to
 the global memory manager. I would like to know when to use LockData()
 and UnlockData() calls, which prevent the global memory manager from
 moving my data segment.

 Response:

 When your application gets control through GetMessage(),
 TranslateMessage(), and DispatchMessage(), your DS is locked. It will
 be unlocked only if you explicitly do so, or make a call to
 PeekMessage() or GetMessage(). While your DS is locked, there is only
 one way it can move, that is, by a call to LocalAlloc(). If
 LocalAlloc() must expand the heap to succeed, it may also have to move
 it. Moving your DS invalidates any outstanding far pointers into your
 DS.


 213. Running Batch Files from inside Windows

 Question:

 Can I run a batch file from inside Windows?

 Response:

 There should be no problems associated with running a batch file from
 inside Windows. You will want to ensure that any program specified in
 the batch file has a PIF file set up with the appropriate parameters.
 This procedure will make the batch file more automatic and not require
 interaction from the standpoint of starting the program.

 If you do not have an appropriate PIF file, Windows will prompt you to
 use the defaults to continue loading the program.


 214. Modal Windows and Task Queues

 Question:

 I have a task that has a tiled window. That same task sometimes
 creates a modeless dialog with the tiled window as parent. But when
 the modeless dialog itself creates a modal dialog box (with itself as
 parent), the modal grandchild does not prevent the tiled window from
 getting input. If the tiled window creates the modal dialog box, that
 does not prevent the modeless dialog from getting input. I need to use
 a modeless dialog box when a certain option is invoked that causes a
 (true) application modal dialog box.

 Response:

 Your problem is a paradigm clash. The inheritance of messages, and
 queuing, by dialog boxes is not what you might expect; it tends to be
 very flat.

 In particular, while it is the case that both types of dialog box take
 a handle to a parent window, this should not be taken to imply
 inheritance in a hierarchical fashion. In fact, the window handle is
 simply used to find the instance, and hook into the instance/task
 queue. Both pop-up and tiled windows are "top-level" windows, and not
 proper children of one another.

 Effectively, you have two window trees rooted in the same
 instance/task queue. Your modal dialog box is modal to one of those
 two trees. It cannot be modal to both at once. You will not be able to
 achieve what you want directly. We recommend the following solution:

 1. It maybe an item in the modeless box that invokes the modal box.
    So, let the parent of the modal box be the modeless box.

 2. Whenever you create the modal box, create another modal box that
    has the TILED window as a parent (you will probably want to call
    into the tiled window's WindowProc to keep things clean). In the
    dialog box procedure for that dialog box, while processing the
    WM_INITDIALOG call, do a ShowWindow(hInvisDlg, HIDE_WINDOW) to keep
    the dialog invisible.

 3. Now, you have a visible modal dialog that disables the pop-up and
    another invisible one that disables the tiled window.

 4. When you are done with the REAL modal dialog, you will have to
    contrive to get the invisible one taken down.

 Note that both TILED and POP-UP windows are top-level windows. In no
 sense are they related as parent to child. The parameter called
 "hWndParent" is a misnomer; it could as easily be NULL in the case of
 either tiled or pop-up windows, with no change in semantics. The only
 tree in which precedence is secure is a series of modal windows, each
 of which is the parent to another window. At the top of the tree might
 be either a tile or pop-up window.


 215. LoadString Count and Return Values

 Question:
    Does the passed buffer length on LoadString include the terminating
 zero?  What about the the returned count?

 Response:
    The passed buffer length should include room for the terminating
 zero. If there is not sufficient room in the buffer for the string and
 the NULL terminator, LoadString will take the value you specified and
 will make the last value a zero, loading as many characters from the
 string as it can. Thus, if you specify four characters and the string
 is "Longer than 4," it will return the three characters "Lon" and will
 put NULL in the fourth character.
    In code within LoadString is similar to the following:

   Find string in Resource, load in temp buffer.
   if (nchar = strlen(temp) > nBufferMax) {
     nchar = nBufferMax;
     temp[nchar] = '\0';
     }
   strcpy(lpBuffer, temp);
   return(nchar);


 216. Moving the Stack While SS != DS

 Question:

 When SS != DS and the stack is MOVEABLE, will Windows move it if
 needed?

 Response:

 Yes, the stack is movable at all times unless you specify it as FIXED.
 When you have a stack size greater than 0, the stack is placed in a
 group of data segments called DGROUP. Both DS and SS registers point
 to the segment address of DGROUP.


 217. Discardable Bitmaps

 Question:

 How do you know when a discardable bitmap has been deleted and you
 should no longer access it?

 Response:

 The only way to determine if a discardable segment has been discarded
 is to attempt to use the handle to that segment. If an error value is
 returned, the segment has been discarded.

 One way to check the validity of a discardable bitmap's handle is to
 select it into a memory DC using SelectObject(). The SelectObject()
 function will return NULL if the bitmap has been discarded.

 SelectObject() will return NULL under the following two cases:

 1. The object selected has been discarded.

 2. There was no object previously selected.

 This results in a potential conflict of information when selecting a
 discardable bitmap as the first bitmap selected for a given DC.
 SelectObject() will return NULL. You do not know if this means the
 bitmap was discarded or that the previous handle was NULL. The only
 way to tell for certain is to try to select the discardable bitmap
 twice. If the second return value is NULL, the bitmap was discarded;
 otherwise, the bitmap has been selected.

 It is probably a better idea to attempt to lock the memory containing
 the bitmap you wish to select. This will tell you if it has already
 been discarded, and will prevent it from being discarded while
 selected. The application should make certain that it unlocks the
 memory when it selects another bitmap into the DC, or when it no
 longer needs the DC.


 218. Getting Mouse Movements Inside a Dialog

 Question:

 What mechanisms are available to enable reception of all WM_MOUSEMOVE
 messages that occur within the client area of a dialog box control?

 Response:

 There are two types of dialogs; the one created by DialogBox() has its
 own message loop (modal) and does not pass mouse movements. The other
 is created by CreateDialog() and uses the message loop of the parent
 (modeless). This is the one you must use to get mouse movements.


 219. CreateCompatibleDC() Does Not Copy DC Itself

 Problem:

 The CreateCompatibleDC() function does not copy the mapping mode, the
 windows extents and origins, or the viewport extents and origins from
 the old DC into the newly created compatible DC. I thought that all
 the DC properties from the old DC would have been copied into the new
 compatible one.

 Response:

 CreateCompatibleDC(hDC) creates a memory display context that is
 compatible with the device specified by hDC (and automatically selects
 a monochrome stock bitmap for it). The memory DC created is compatible
 with the device only, not with any other characteristics of the DC
 passed as an argument. Any attributes, such as those you mentioned,
 must be specifically set by the application.


 220. Documentation Missing for Cursor, Bitmap, and Icon File Format

 The following information about the format for the cursor, bitmap, and
 icon resources is missing from Page 637, Chapter 7 ("File Formats") in
 the "Microsoft Windows Software Development Kit Programmer's
 Reference" manual.

 The following is the format for the cursor, bitmap, and icon
 resources:

    byte  bFigure;
    byte  bIndependent;
    short xHotspot;
    short yHotspot;
    short cx;
    short cy;
    short widthbytes;
    short clr;

 The bits of the AND mask and the bits of the XOR mask are as follows:

 Bit              Meaning

 bFigure       Specifies whether the structure is a cursor, bitmap,
               or icon. The value 1 indicates a cursor, the value 2
               indicates a bitmap, and the value 3 indicates an icon.

 bIndependent  Specifies whether the object is device dependent
               or device independent. The value 0 indicates
               device dependent and the value 1 indicates device
               independent.

 xHotspot      Specifies the x-coordinate of the cursor's or
               icon's hotspot.

 yHotspot      Specifies the y-coordinate of the cursor's or
               icon's hotspot.

 cx            Specifies the x-extent of the cursor or icon.

 cy            Specifies the y-extent of the cursor or icon.

 widthbytes    Specifies the bytes per row in the cursor, or
               icon (note that the bytes must be aligned on a
               word-boundary).

 clr           Specifies the number of color planes (note that
               this value is always set to 0).

 If the resource is a bitmap, a BITMAP data structure follows the
 bIndependent field. The fields xHotspot, yHotspot, cx, cs, widthbytes,
 and clr are not part of the bitmap resource file.

 When the resource compiler creates this structure, it is put in part
 of the compiled resource file (.RES file). If you create a similar
 structure, there is no way to add it to the .RES file so that it can
 exist for the application. You must edit the executable header to
 include these resources.

 The Windows Version 2.03 SDK explains the new executable file format.


 221. Clearing COMM Events

 Question:

 Will SetCommEventMask(cid, NULL) clear the event mask? If not, how do
 I do this procedure?

 Response:

 SetCommEventMask() will not clear the event mask; GetCommEventMask()
 will. SetCommEventMask() is used to enable the communications driver
 to report status of various control lines and break and error status.

 GetCommEventMask() reports and clears event flags. In this way
 appropriate action can be taken on the reported event flags while the
 cleared event mask is ready to report subsequent communications port
 activity without dropping an event.


 222. System Bitmaps, Icons, and Cursors

 Question:
    Under Windows Version 2.x, how can I get the various system bitmaps
 for the minmax arrows, system menu, MDI system menu, etc.?

 Response:
    The system bitmaps are resources in the display driver. You can
 access these by using LoadBitmap with a NULL instance handle. For
 example:

 #define     OEMRESOURCE
 #include    "windows.h"

       hbmReduce  = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_REDUCE));
       hbmZoom    = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_ZOOM));
       hbmRestore = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_RESTORE));
       hbmSysMenu = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CLOSE));

    Note that hbmSysMenu is twice as long as needed: it has both the
 system menu bitmap and the MDI sysmenu bitmap (a minus sign).
    Search for OEMRESOURCE in WINDOWS.H, and you will see a number
 of related constants.
    OCR_* and OIC_* define the system cursors and icons, respectively,
 for use with LoadCursor() and LoadIcon(). Please see the ID?_*
 constants documented under LoadCursor() and LoadIcon() in the
 programmer's reference.


 223. Rubber Banding with a Dotted Rectangle

 Question:

 How do I display a dotted line that inverts the screen pixels so I can
 display and erase a dotted line without disturbing the underlying
 display? A dotted line now appears as a solid line when displayed and
 the mode is R2_NOT.

 Response:

 To get this effect, use SetBkMode() with transparent as the mode. When
 you first get a DC, its background mode is opaque.


 224. SetClipboardData() and CF_PRIVATEFIRST

 Problem:

 The documentation for SetClipboardData() states that CF_PRIVATEFIRST
 can be used to put private data formats on the clipboard. It also
 states that data of this format are not automatically deleted.
 However, that is apparently not true. That is, the data are removed
 automatically when the clipboard is emptied (sending a
 WM_DESTROYCLIPBOARD message) and the new item is set into the
 clipboard.  The old item is shown; however, the handle now is invalid
 because GlobalFree() is called on it.

 Response:

 GlobalFree() was not called on this handle. If you try to use the
 other handle to this memory, you will find that the one you initially
 received from GlobalAlloc() is still valid. Only the clipboard handle
 has been invalidated by the call to EmptyClipboard().

 The documentation states that "Data handles associated (with
 CF_PRIVATEFIRST) will not be freed automatically." This statement is
 referring to the memory associated with that data handle. When
 SetClipboardData() is called under standard data types, it frees the
 block of memory identified by hMem. This is not the case for
 CF_PRIVATEFIRST. Applications that post CF_PRIVATEFIRST items on the
 clipboard are responsible for the memory block containing those items.

 This is not intended to imply that items placed on the clipboard will
 remain on the clipboard if they are CF_PRIVATEFIRST. When a call is
 made to EmptyClipboard(), all objects will be removed.


 225. The Debug Kernel and AUX

 Question:
    Why do some programs that run properly under retail Windows crash
 trying to access the AUX port when run under the debug version?

 Response:
    Under the debug version of Windows, the Kernel, User, and GDI do
 much more extensive error checking. If an error is found, the kernel
 dumps the error and a stack trace to the AUX port. This causes an
 error if the port does not exist.
    To run the debugging kernel, you must have a terminal or other
 machine on COM1. You also need to do a "MODE COM1:9600,N,8,1" (or
 whatever settings are appropriate) before entering Windows. (In your
 AUTOEXEC.BAT file would be a good place.)  You can test if it is
 working by doing a "dir > com1".
    Alternatively, you can do everything on a monochrome display. To do
 this, put OX.SYS in your CONFIG.SYS, and use "symdeb /m ........".
 OX.SYS redirects all I/O for the AUX port to the main keyboard and to
 the monochrome display. OX.SYS is available in the Software Library by
 searching for OX.ARC, the Q number of this article, or S12005.


 226. The Escape(..GETSCALINGFACTOR..); in Windows

 Problem:

 I am confused by the values returned by
 Escape(..,GETSCALINGFACTOR..);. The values returned are being used to
 determine the resolution of the output device. After changing the
 arguments to StretchBlt(), the output I get is extremely small.

 Response:

 The scaling factor does not affect the resolution of the printer. This
 is simply a way for the driver to inform an application that the
 driver is sampling the bitmap in a different fashion. The scaling
 factor might be thought of as a sampling factor.

 An example is the best way to understand what is happening. Imagine an
 8 x 8 resolution device that has three scaling factors of 0, 1, and 2.
 In the scaling factor 0, all 8 bits of each row of the bitmap are
 sampled and sent to the printing device's internal bitmap array. In
 the scaling factor 1, every other bit of each row is sent to the
 device's internal bitmap array. In the scaling factor 2, every fourth
 bit of each row is sent to the printer.

 On the printer's side, an internal flag is set to either 0, 1, or 2
 (scaling factor). This flag informs the printer how to express the
 bitmap image from memory to the printer page. If the scaling factor is
 0, all 8 bits of each row are mapped one-to-one onto the printer page.
 For a scaling factor of 1, each bit of the internal memory bitmap is
 mapped to two bits on the output page; for the scaling factor 2, each
 bit maps to four bits on the output page.

 Although the scaling factor is used to represent the amount of memory
 needed to transfer the bitmap to the printing device, the target
 bitmap size remains the same. The actual resolution is not changed.

 Scaling factors do not represent the resolution of the output device,
 but instead represent the amount of memory used in the outputting
 device's internal bitmap array. The only purpose for the scaling
 factor is that the memory bitmap image you must create in the display
 context can be smaller than the actual printer resolution if a scaling
 factor other than 0 is used. The image can be half that of the actual
 printer resolution with a scaling factor of 1, and a quarter of the
 actual resolution with a scaling factor of 2. You still must
 StretchBlt() this smaller bitmap to fill the entire printer page. The
 smaller bitmap will be sampled and stretched (made larger) into the
 printer page. The driver then will resample and stretch (make smaller)
 the printer page back to an identical image found in the memory bitmap.


 227. Example of Adding Menus Dynamically

 Question:

 How can I add a new item, which is a pop-up menu, to the menu bar? For
 example, I have three items, File, Edit, and Options. Each has its own
 pop-up menu. I want to add a fourth item, Execute, with two subitems,
 Begin and Abort. When I attempt to add these new items, either I get
 only the top-level item, e.g. Execute, added without the pop-up, or
 nothing is added at all. Please specify the exact sequence of calls
 and the setup of the resources.

 Response:

 A pop-up menu can be added to a menu bar in two ways. In the first
 method, define a pop-up menu in the RC file like other pop-up menus.
 Then, use the following sequence of code:

 hPopup = LoadMenu (hInst, (LPSTR) "popup menu name as defined in rc file");
 SetMenu (hWnd, hPopup) ;
 DrawMenuBar (hWnd) ;

 In the second method, make a pop-up menu dynamically at run time. To
 use this method, you need to add the following lines of code to your
 application:

 hPopup = CreateMenu () ;
 ChangeMenu (hPopup, NULL, (LPSTR)"entry 1 in popup", id1, MF_APPEND) ;
 ChangeMenu (hPopup, NULL, (LPSTR)"entry 2 in popup", id2, MF_APPEND) ;
 hMenu = GetMenu(hWnd);
 ChangeMenu (hMenu, NULL, (LPSTR)"name of popup menu", (WORD)hPopup,
      MF_POPUP|MF_APPEND) ;
 DrawMenuBar (hWnd) ;

 There is a complete example of this dynamic-creation method in the
 Software Library. This file can be found in the Software Library by
 searching on the filename ADDMENU.ARC, the Q number of this article,
 or S12070.


 228. Windows SDK: Graying Dialog Box System Menu Items

 Question:

 How can I gray menu items in the system menu of a dialog box?

 Response:

 EnableMenuItem() can be used to gray menu items in the system menu of
 a dialog box. The following code is a modified version of the About
 routine from the HELLO sample application included with the Windows
 Software Development Kit (please note that HELLO.RC should be modified
 so that the About dialog box includes the window style options
 WS_CAPTION and WS_SYSMENU):

    BOOL FAR PASCAL About( hDlg, message, wParam, lParam )
    HWND hDlg;
    unsigned message;
    WORD wParam;
    LONG lParam;
    {
        switch( message ) {
        case WM_COMMAND:
            EndDialog( hDlg, TRUE );
            return TRUE;
        case WM_INITDIALOG:
            /* Get copy of the initial state of the dialog box sys menu */
            GetSystemMenu( hDlg, FALSE );
            return TRUE;
        case WM_INITMENU:
            /* Gray "Close" system menu item */
            EnableMenuItem( GetSystemMenu( hDlg, FALSE ), SC_CLOSE,
                         MF_BYCOMMAND | MF_GRAYED );
            return TRUE;
        default:
            return FALSE;
        }
    }


 229. Captions for Dialog Listboxes

 Question:
    How can a caption be displayed with a listbox?

 Response:
    Use the SetWindowText() function to place text into the caption bar
 specified for a listbox. First, use GetDlgItem() to get the handle of
 the listbox, then call SetWindowText() to set the listbox caption.
    The following code fragment from the Windows SDK TEMPLATE
 application illustrates the necessary steps (please note that
 TEMPLATE.RC was modified so the listbox would include the WS_CAPTION
 window style):

    ...
    BOOL FAR PASCAL TemplateDlg(hWndDlg, message, wParam, lParam)
    ...
        switch (message)
         {
        case WM_INITDIALOG:
            ...
            /* The following line sets the Listbox caption */
            SetWindowText( GetDlgItem(hWndDlg, IDDLISTBOX), (LPSTR)"Caption");
            for (i = 0; i < CSTR; i++) {
                LoadString(hInstTemplate, IDSSTR1+i, (LPSTR)szWaters, 12);
                SendDlgItemMessage(hWndDlg, IDDLISTBOX, LB_ADDSTRING, 0,
                    (LONG)(LPSTR)szWaters);
                }
            SendDlgItemMessage(hWndDlg, IDDLISTBOX, LB_SETCURSEL, iSel, 0L);
            ...
            return TRUE;

        case WM_COMMAND:
    ...


 230. Changing the Printer Resolution through the Control Panel

 Question:

 The EPSON driver has two resolutions. How do I get the higher
 resolution?

 Response:

 Because Windows is device independent, Windows does not provide a way
 for an application to hard code any instructions to talk directly to
 the printer (e.g. changing the print quality). Using the Control Panel
 is the best way to access the higher resolution option.

 Some applications, such as Draw and PageMaker, provide a menu item
 that will spawn the Control Panel to allow you to change the
 resolution or the output port. Once the printer is installed, select
 the resolution by executing the Control Panel. Select the Printer
 command from the Setup menu, then select the printer type from the
 list of printers. Several resolution options will appear, including
 the higher resolution option you seek.


 231. LINK4 Adds 16 NULL Bytes to _TEXT

 Question:
    LINK4 Version 5.01.17 (shipped with Version 2.03 SDK) places 16
 bytes of zeros at the beginning of the code segment. Is this a change
 from Version 5.00.12 (Version 1.04 SDK)?

 Response:
    The linker inserts 16 bytes of zeros at the beginning of segment
 _TEXT if the /DOSSEG option has been given or the .DOSSEG comment
 record has been seen (found in the runtime library startup module
 crt0). This is because, in small model, the constants SIG_DFL (0) and
 SIG_IGN (1) can be used for the signal() routine without conflicting
 with actual function addresses. The 16 bytes guarantee that no
 small-model function will have an address that conflicts with those
 constants. Sixteen was used instead of two to handle cases where
 people declare paragraph-aligned _TEXT segments.
    There is an option /NONULLSDOSSEG that disables the 16 nulls but
 provides all the other effects of the /DOSSEG option and comment
 record. It overrides the comment record in crt0 and can be used
 safely with small-model C programs as long as you do not call
 signal() on a routine with an address of 0.


 232. Memory for Communication Queues

 Question:

 Where do the communications routines (e.g. OpenComm) get their
 allocation for the receive and transmit queues? Do they come from the
 local or global heap?

 Response:

 The memory for the transmission queues in the communications routines
 comes from the global memory heap.


 233. Learning the Number of Function Keys

 Question:

 How can I determine the OEM-supported keys, e.g. function keys F11
 through F16, as well as other keys supported by keyboards from
 different manufacturers?

 Response:

 The following sample application shows how to determine this
 information, as well as how to determine the number of buttons in the
 mouse:

    ========================== info.mak
    info.obj: info.c info.h
        cl -d -c -AS -Gsw -Od -Zped info.c

    info.exe: info.obj  info.def
        link4 info, /align:16, /map/li, slibw, info.def
        mapsym info

    ========================== info.def
    NAME Info

    DESCRIPTION 'Get # of F keys and # of mouse buttons.'

    STUB    'WINSTUB.EXE'

    CODE    MOVEABLE
    DATA    MOVEABLE MULTIPLE

    HEAPSIZE   4096
    STACKSIZE  4096

    IMPORTS
        KeyboardInquire = Keyboard.1
        MouseInquire    = Mouse.1

    ========================== info.c
    #include "windows.h"

    extern WORD FAR PASCAL KeyboardInquire ( LPSTR );
    extern WORD FAR PASCAL MouseInquire ( FARPROC );

    typedef struct {
      char    Begin_First_range;
      char    End_First_range;
      char    Begin_Second_range;
      char    End_Second_range;
      WORD    kbStateSize;   /* #bytes of state info maintained by ToAscii. */
      WORD    kbNumFuncKeys; /* How many function keys are on the keyboard. */
      WORD    kbHasBreak;    /* true => keyboard supplies make and break. */
      WORD    kbRate;        /* Maximum rate of keyboard input events. */
      } KBINFO;

    KBINFO  keybdInfo;

    typedef struct {
        char  Exists;
        char  Relative;
        short NumButtons;
        short Rate;
        short XThresh;
        short YThresh;
        short XRes;
        short YRes;
    } MOUSEINFO;

    MOUSEINFO mouseInfo;

    int PASCAL WinMain( hInstance, hPrevInstance, lpszCommandLine, cmdShow)
    HANDLE  hInstance;
    HANDLE  hPrevInstance;
    LPSTR   lpszCommandLine;
    int     cmdShow;

    {
        register WORD  i,k;
        char buff[45];

      /* Get keyboard driver information. */
      if ( ( i = KeyboardInquire( ( LPSTR )&keybdInfo ) ) != sizeof( KBINFO ))
      {
          MessageBox ( NULL, (LPSTR)"Keyboard info", (LPSTR)"error", MB_OK);
          return (1);
      }
      sprintf( buff, "# of Function Keys: %d", keybdInfo.kbNumFuncKeys );
      MessageBox ( NULL, (LPSTR)buff, (LPSTR)"Keyboard", MB_OK);

      /* Get Mouse driver information. */
      if ((i=MouseInquire( (FARPROC)&mouseInfo )) != sizeof( MOUSEINFO ) ) {
          MessageBox ( NULL, (LPSTR)"Mouse info", (LPSTR)"error", MB_OK);
          return (1);
      }
      sprintf( buff, "# of mouse buttons: %d", mouseInfo.NumButtons );
      MessageBox ( NULL, (LPSTR)buff, (LPSTR)"Mouse", MB_OK);
   }


 234. Dialog Boxes and Device Independence

 Question:

 How do I get device-independent dialog boxes when their size depends
 on the system font?

 We are developing applications on the PS/2 Model 50 and Compaq 286
 with EGA cards (NEC MultiSync Monitors). When creating a dialog box
 through dialog on the PS/2 and moving it to the 286, the dialog box is
 much larger on the 286. This presents problems because the dialog box
 on the PS/2 was large initially. I know dialog coordinates are based
 on the system font (i.e., the PS/2 dialog boxes are smaller because
 the PS/2 font is smaller than the font on the 286).

 Response:

 Dialog-box appearance is determined by the system font. Therefore, a
 dialog box is not device independent. When designing a dialog box for
 a wide variety of displays, one of two approaches must be taken.
 Either use a dialog box designed for the minimum display resolution to
 be encountered, or create and display different dialog boxes,
 depending on the display quality. It may be possible to use the same
 dialog box message-handling routines, just different dialog-box
 resources.

 The standard approach is to design dialog boxes and icons in a CGA
 640 x 200 mode (the minimum resolution possible in Windows). This
 procedure will allow the dialog box to run on any display.

 It is not logical to take a large, complicated dialog box designed for
 a large-resolution screen and attempt to take it to a low-resolution
 display, i.e., taking a full-screen 1024 x 1024 pixel dialog box
 containing edit controls, and mapping it onto a 640 x 200 display.
 Device independence does not mean that the application designer must
 not consider the types of devices on which the software may run.


 235. Windows SDK: How to Implement Read-Only Edit Control

 Question:

 I want to process a password from a dialog box, but I do not want to
 show the password on the screen. The dialog box has several other edit
 controls that should echo. Is it possible to have only the password
 field not echo?

 Response:

 Use subclassing to implement read-only password input in a dialog-box
 edit control. An example demonstrating the subclassing technique can
 be found in the file TRACK in the Software Library. This file can be
 found in the Software/Data Library by searching on the keyword TRACK,
 the Q number of this article, or S12038.


 236. Dialog Box Controls

 Question:

 When a push button is selected, it is depicted momentarily in reverse
 video. How can I cause this state to be held for as long as I desire?
 Is there any way to set a control to go to reverse video and back?

 Response:

 There are two ways to accomplish this. One way is to send the button a
 BN_HILITE message as described on Page 151 of the "Microsoft Windows
 Software Development Kit Programmer's Reference." You then can send a
 BN_UNHILITE message to return it to normal.

 However, this procedure may not work properly because there are other
 messages sent to the button by the dialog routine itself. In this
 case, use the second approach, which is to intercept the messages
 being sent to the control. This technique is known as subclassing.
 There is a sample application in the Software library named TRACK.C
 that describes how to do this technique. This file can be found in the
 Software Library by searching on the filename TRACK.ARC, the Q number
 of this article, or S12038.


 237. Loading Resources from Libraries

 Problem:

 I am using the following code:

   for ( i = 0; i < 2; i++ ) /* do this twice */
     {
     hLib = LoadLibrary( (LPSTR)"FooLib.exe" );
     hRes = FindResource( hLib, ... );
     hResData = LoadResource( hLib, hRes );
     LockResource( hResData );

     /* LoadString(...) calls here */

     GlobalUnlock( hRes );
     FreeResource( hRes );
     FreeLibrary( hLib );
     }

 Every function call works properly the first time through the loop,
 but LockResource(...) always fails the second time.

 Response:

 Your code had difficulty attempting to loop through loading resources
 from a library several times because you called the LoadString()
 routine in addition to the FindResource() routine.

 The LoadString() routine (or LoadCursor(), LoadIcon(), LoadBitmap(),
 LoadMenu(), or LoadAccelerators()) does quite a few things you may not
 have anticipated. Essentially, LoadString() calls FindResource(),
 LoadResource(), LockResource(), and GlobalUnlock(), in addition to
 loading the string into your buffer. Therefore, you only need to call
 the LoadString() routine; none of the other resource-managing routines
 in the sample code you provided need to be called. Those routines
 should be used to load nonstandard resources (ones which you have
 designed).


 238. Implementing Linked Lists with Handles in Windows

 Question:

 What are some recommended methods to implement a linked list in the
 local heap?

 Response:

 The standard method when using C in a non-Windows environment is to
 use pointers to reference the next link in the chain. Using pointers
 in the Windows environment requires that the LMEM_FIXED flag be used
 with LocalAlloc() so heap compaction will not affect the internal
 pointer references. Problems will occur using this technique as
 members of the chain are added and deleted.

 Implementing a heap compaction to free space will be difficult (or
 impossible). Therefore, handles should be used instead of pointers to
 reference successive list members in the Windows environment. Handles
 will allow the allocated heap data to be moved; no LMEM_FIXED is
 needed. When data in the chain must be referenced, LocalLock() is
 called using the member's handle. To traverse the linked list, do
 the following:

 1. Given an initial handle to the chain link, call LocalLock().

 2. Use the address returned by LocalLock() to access the link's data
    and get the handle of the next link in the chain.

 3. Call LocalUnlock() for the current link.

 4. If not at the end of the chain, go to step 1 using a new handle.


 239. MENUITEMTEMPLATE Additional Information

 Question:

 The structure MENUITEMTEMPLATE is documented in the Windows Version
 2.00 manual, but is not defined in WINDOWS.H. Please clarify the usage
 of MENUITEMTEMPLATE entries.

 Response:

 Windows supplies all the functions necessary to dynamically build,
 change, and delete menus without the LoadMenuIndirect function. Here
 is the structure of the MENUITEMTEMPLATE:

 struct {
   BYTE  mtOption;
   WORD  mtID
   LPSTR mtString;
 } MENUITEM;

 This is the general structure. The only place this does not work as
 shown is when you have a pop-up. To start a pop-up item, if it is the
 first of many pop-ups, mtOption will have a value of 10. If it is the
 last item, mtOption has a value of 0x90. You get 0x90 from ORing the
 values POPUP|END, 0x10|0x80. If it is a pop-up item, the next byte is
 mtString, and mtID is omitted.

 Here are the #defines for the menu options that are listed on Page 625
 of the "Microsoft Windows 2.00 Software Development Kit Programmer's
 Reference" manual:

 /* menu option bits */
 #define CHECKED       0x08
 #define END           0x80
 #define GRAYED        0x01
 #define HELP          0x04
 #define INACTIVE      0x02
 #define MENUBREAK     0x40
 #define MENUBREAKWBAR 0x20
 #define POPUP         0x10


 240. Windows SDK: General Information on Dynamic Link Libraries

 Question:

 I have the following questions concerning Windows dynamic link
 libraries:

 1. Can small-, medium-, compact-, large-, and huge-memory models be
    used to create libraries?

 2. Is it correct to assume that because a library must be compiled
    with the -Aw option, you cannot use the -AS, -AM, -AC, or -AL
    options, but rather must specify -As or -Al, etc.?

 3. When must libraries be compiled with the -Gw option?

 4. Is it better to compile Windows libraries with FAR code addressing
    so that code segment swapping can be done to save memory,
    especially if the library is larger than 16K or 24K?

 5. Is it correct that libraries can only have the SINGLE, but not
    MULTIPLE, attribute on the DATA segment in the .DEF file?

 6. I understand that libraries can have only one data area for all
    instances of a library. Presumably a library can have multiple data
    segments (such as when compiled in compact or large), but all
    instances of the library use the same set of data segments. When
    there is more than one data segment, should the .DEF file have DATA
    FIXED SINGLE or can it have MOVEABLE SINGLE? For applications with
    FAR data segments, they have to be FIXED. Is it correct to assume
    that the same is true for libraries?

 7. Is it correct to assume that all callable library routines should
    be compiled with FAR PASCAL attributes and all pointers passed to
    library routines should be FAR pointers?

 8. Does an application need to import library functions? In an example
    of the DDE demo (server and bar graph), the DDE atom functions were
    not imported. When calling a library function, does anything
    special have to be done [such as MakeProcInstance() for dialog
    routines], or does the application call the routine like it would
    for any other function?

 Response:

 Listed below are answers to each of the questions you have raised
 concerning dynamic link libraries:

 1. Small and medium models are the models of choice for building both
    applications and dynamic link libraries (DLLs). Compact and large
    models should not be used due to the default 32-bit addresses used
    to reference data. Since memory is constantly moving, these
    addresses are quickly invalidated unless FIXED data segments are
    used. FIXED segments result in sand bars in memory, bogging down
    memory management and impacting every application.

 2. You are correct in your assumption. Use -Asnw for small-model DLLs
    and -Alnw for medium-model DLLs. By using -AS or -AM, the -Ad
    segment-setup switch is included, which instructs the compiler to
    generate code assuming DS == SS. DLLs can have their own data
    segment, but run on the caller's stack. Therefore, DS != SS and the
    -Ad switch would generate incorrect code for this environment. The
    -Aw switch sets up code generation assuming SS != DS and does not
    generate code to automatically load DS at each module entry point.

 3. The -Gw switch is normally used when compiling Windows applications
    and DLLs. This instructs the compiler to generate Windows prolog
    and epilog code that will fix up DS to point to the correct
    location of the data segment. This is required because segments,
    code, and data are moved by the memory manager. The only time -Gw
    is not required (although it is still a good idea to use it) is
    when compiling modules that will result in intra-segment calls. All
    inter-segment calls must include the prolog and epilog code.

 4. DLL entry points must be typed as FAR PASCAL: FAR is used because
    an inter-segment call must occur to get to the function; PASCAL is
    used for all call-back and DLL functions for proper stack handling
    in parameter passing. All intra-segment functions (functions used
    only within the current code segment) in a dynamic link library can
    be NEAR. All inter-segment functions (functions that may be called
    from a segment outside of the current segment) must be FAR.

    Using named segments within a DLL, specifying SEGMENT options such
    as PRELOAD, LOADONCALL, and DISCARDABLE, and keeping each segment
    to approximately 16K will ease memory requirements and help out the
    memory manager.

 5. You are correct in assuming that a DLL can only have at most one
    data segment. This is a direct implication of being a single
    instance resource. DLLs are a shared resource used by many
    applications.

 6. You are correct in assuming that libraries can obtain a large
    amount of data space that may include multiple segments. However,
    there is only one instance of the library, and hence, only one
    automatic data segment (DGROUP is the name we attach to the
    automatic data segment).

    As mentioned in the answer to question 1, do not use compact and
    large models. This may appear to be restrictive. However, global
    memory may be allocated through GlobalAlloc() to store large
    amounts of data in both small and medium model, thereby working
    around the limited 64K default data segment.

 7. You are correct in assuming that all callable library routines
    should be typed as FAR PASCAL and exported in the library's
    definitions file. Pointers passed from a calling application to a
    DLL that reference data belonging to the application should be FAR
    pointers. Try to pass handles to objects when possible.

 8. You must either explicitly define imports in the .DEF file of the
    application, or you must link to an import library generated by
    running the IMPLIB utility on the DLL's .DEF file (or be provided
    with the import library). The files SLIBW and MLIBW are the import
    libraries for the Windows KERNEL, USER, and GDI DLLs.

    Normally, no code is required to specially handle calls to DLL
    functions. However, there is another way to access DLL functions
    other than the two methods above. You can use the KERNEL functions
    LoadLibrary(), GetProcAddress(), etc. However, these functions
    require that the application have an in-depth knowledge of the
    library with which it is attempting to link. This method is very
    powerful, and we sometimes refer to it as dynamic-dynamic linking.


 241. Documentation on New Executable Format Is Incorrect

 The documentation on the new executable format listed on Page 648 of
 the "Microsoft Windows Software Development Kit Programmer's
 Reference" is incorrect.

 The documentation incorrectly lists the following:

     .
     .
    1CH  Number of entries in the segment table.
    1EH  Number of bytes in the nonresident-name table.
    20H  Offset of the segment table, relative to the beginning
         of the new .EXE header.
     .
     .

 Page 648 should read as follows:

     .
     .
    1CH  Number of entries in the segment table.
 -->1EH  Number of entries in module reference table. <---
    20H  Number of bytes in the nonresident-name table.
    22H  Offset of the segment table, relative to the beginning
         of the new .EXE header.
     .
     .

 The correct entry for 1EH has been omitted. The phrase "number of
 module reference table entries" must be inserted at 1EH and each entry
 following it must be shifted down one line, as shown above.


 242. CUBE.ARC: Rotating Cube Windows Program Source Code

 There is a file named CUBE.ARC in the Software Library that includes
 source code to a Windows application that displays a rotating
 hypercube.

 CUBE.ARC can be found in the Software Library by searching for the
 filename, the Q number of this article, or S10035. CUBE.ARC has been
 archived with PKARC so you will need to unarchive it with PKXARC. The
 PKXARC utility is located on your OnLine Utilities Disk 2.


 243. Kanji Support for Windows

 Problem:

 I would like some information on the use of the Kanji, Katakana, or
 Hiragana languages under Windows or other environments.

 Response:

 Kanji support for DOS and Windows is commonly referred to in the
 Japanese conversion shops as DBCS support (double-byte code support).
 Because there are so many characters in the language, two bytes were
 needed to cover them all.

 Basically, an application must be DBCS enabled to run with the Kanji
 character set. The Kanji character set is made up of a combination of
 the following three ranges:

 1. 0x20 - 0x7d (Roman characters just like ANSI)

 2. 0xa0 - 0xdf (one-byte Katakana characters)

 3. 0x80 - 0x9f plus 0xe0 - 0xfd (these are the allowed first byte
    range of two-byte streams)

 A DBCS-enabled application must be able to do the following:

 1. Recognize character streams that start in the valid first byte
    range

 2. Group the character streams with the subsequent byte and treat them
    as a single entity in searching or advancing and backing the
    cursor

 Note that a string can be a mixture of all three of the above ranges,
 so be careful not to let the application get out of sync and treat a
 second byte as a normal roman character.

 A valid second byte range is 0x40 - 0xfd. This makes parsing for
 commas, periods, and other odd symbols simple because they can never
 be part of a two-byte Kanji. However, the directory separator "\" is a
 valid second-byte Kanji, and you must be careful not to confuse the
 two when looking for "\".


 244. CallWindowProc() Documentation Is Incorrect

 The hWnd parameter to CallWindowProc() is incorrectly documented on
 Page 162 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" for Version 2.00.

 The hWnd description should be changed to the following:

 Identifies the window the message is for. It is also used to set up
 the DS that the called window function will run on.


 245. Overview of Windows Communications Routines

 Question:
    The section on communications events in the manual is not very
 clear. For example, why does SetCommEventMask return a far pointer to
 the event word, instead of the word itself? Is this a pointer to a
 fixed location that is to be polled for changes? Could you please
 provide an example of how and when to use the various functions for
 handling communications events?

 Response:
    The designers of the Windows communications routines intended to
 create independent communications software that handles hardware
 events from the communication device and transmission, reception, and
 queuing of information with minimal high-level interaction. Once
 initialized, the COM driver automates many communications tasks that
 developers normally had to handle.
    The COM driver may be initialized for hardware or software
 (XON/XOFF) handshaking, communication parameters (baud, bits, parity),
 queue sizes, and transmission delays and filtering. For communication
 using standard XON/XOFF and hardware handshaking, the functions to use
 are as follows:

 OpenComm()      ; Must be called first to select and initialize internal
                   tables.
 BuildComm()     ; Shortcut for setting baud and queue sizes and setting
                   tables to default values.
 GetCommState()  ; Retrieve data from com driver internal tables and
                   place in application's COMSTAT structure. Used to get
                   initial state.
 SetCommState()  ; Normally used instead of BuildComm() since full
                   initialization of com driver is controllable.
 WriteComm()     ; Output to communications port through queue.
 ReadComm()      ; Input from communications port through queue.
 GetCommError()  ; Check often. Not just for checking for errors, also
                   status. When errors occur, com device is locked down.
                   GetCommError() unlocks.
 CloseComm()     ; Close communications port before terminating
                   application.

    The following functions are not often used because the driver
 handles handshaking automatically:

 EscapeComm()    ; Can be used to set software and hardware handshaking.
                   Since this function would normally reside outside of
                   and interrupt routine, it cannot be relied upon for
                   controlling handshaking, only assisting handshaking.
 SetCommEventMask; Should be called before using GetCommEventMask.
 GetCommEventMask  SetCommEventMask enables the reporting of events
                   that can be checked with GetCommEventMask. GetComm
                   EventMask checks and clears an event so that future
                   events are not dropped. SetCommEventMask returns
                   a pointer to an integer that reports the current
                   state of the events; however, the events are not
                   cleared. This is not generally useful since events
                   should be cleared.

    The CommEventMask functions are useful for checking hardware events
 such as line breaks. They generally are not useful outside of an
 interrupt service subroutine. Your description of the use of the long
 pointer returned by SetCommEvenMask is correct. This long pointer
 remains valid as long as the communications port is open and can be
 polled for events, but the events are not cleared.


 246. Determining Number of Disk Drives

 Question:

 As evidenced by the disk-drive icons in the MS-DOS Executive, Windows
 obviously has some way of determining how many disk drives are
 attached to the system. How can I determine this in my own
 application?

 Response:

 Use the MS-DOS function Select Current Drive, INT 21H function 0EH.
 This function selects the current drive and reports the number of
 drives installed.

 Once you know how many drives there are, you can enumerate the drives
 and sequentially test their existence and attributes using function
 0EH (Select Current Drive) and function 19H (Report Current Drive) in
 conjunction with function 44H (IOCTL). For each potential drive, try
 selecting the next drive; if the current drive does not change, the
 potential drive does not exist.

 Function 44H allows you to determine if the drive has removable media;
 this is useful to programs because it lets them know if they need to
 check for a disk change or if they can rely on the same disk always
 being there.

 For more information on the MS-DOS functions 44H, 19H, and 0EH, please
 consult any of the following references:

 "Microsoft MS-DOS Programmer's Reference"

 "The Peter Norton Programmer's Guide to the IBM PC," Pages 277, 282,
 and 311, by Peter Norton, published by Microsoft Press

 "Advanced MS-DOS," Pages 295, 306, and 349, by Ray Duncan, published
 by Microsoft Press


 247. Windows SDK: How to Make Text Blink

 Question:

 Is there any way to make text blink?

 Response:

 What most of us would like to do is just set a bit on the character
 attribute and have it flash. This will not work in a graphical
 environment because it requires the hardware to do this. However,
 there is a fairly simple way to accomplish blinking text that requires
 you to use SetTimer() to set the rate of the flash.

 You can invert the rectangle by using PatBlt() with a dwRop of
 PATINVERT. This will give you a reverse video and the timer will
 determine when you do the next PatBlt() using the same dwRop. Be sure
 to turn it off with the desired background. There is a sample program
 in the Software Library that uses this technique to get blinking text.
 You can find this file in the Software Library by searching on the
 filename BLINK.ARC, the Q number of this article, or S12064.

 You can select a desired rate from the USER by getting the value from
 the WIN.INI file using GetProfileInt(). You can then modify the blink
 rate through the Control Panel. For example:

   nRate = GetProfileInt(
     (LPSTR)"windows",         /* heading in [] */
     (LPSTR)"CursorBlinkRate", /* string to match */
      550);                    /* default value */


 248. Printer Page Area in Windows

 Question:

 How do I determine the printable area for my printer?

 Response:

 Escape (GetPhysPageSize) gives the size of the physical sheet of
 paper, e.g. 8 1/2 by 11 for the Epson FX-80.

 Escape (GetPrintingOffset) says how far in and how far down the
 printable area starts on the sheet of paper, e.g. 1/4 inch in and 1/8
 inch down for the Epson.

 GetDeviceCaps (HorzSize) gives the width of the printable area on the
 page (8 inches for the Epson).

 GetDeviceCaps (VertSize) gives the length of the printable area on the
 page (10 inches for the Epson).

 GetDeviceCaps (HorzRes) gives the pixel width of the printable area on
 the page (960 for the Epson).

 GetDeviceCaps (VertRes) gives the pixel length of the printable area
 on the page (720 for the Epson).

 If pixels per inch are desired, GetDeviceCaps (LogPixelsX and Log
 Pixel sY) gives the number of pixels per inch in each direction (120
 per inch horizontal, 72 per inch vertical for Epson). Please note that
 these arguments were not documented.

 Device coordinates are returned, so conversion to logical coordinates
 is necessary before drawing with GDI.

 The origin of the printable area is 0,0. The answer to the question as
 to what the rectangle that defines the printable area is the
 following:

     left = 0
     top  = 0
     right = GetDeviceCaps (HorzSize)
     bottom = GetDeviceCaps (VertSize)

 Using the above example for the Epson, the paper is 8 1/2 by 11
 inches. Horizontally, the printable area goes from 1/4 inch to 8 1/4
 inches; vertically, the printable area goes from 1/8 inch from the top
 to 10 1/8 inches.


 249. Catch and Throw Functions

 Question:

 What do the Windows functions Catch and Throw do?

 Response:

 Catch and Throw are analogous to the C functions setjmp() and
 longjmp(). They are used to save or restore the current environment
 (including the state of all system registers and instruction counter)
 for Windows.


 250. Finding Where Windows Is Located

 Question:
    How do I determine the path of the Windows directory from within an
 application?

 Response:
    Use GetModuleFileName and GetModuleHandle. The following is sample
 code showing how to get the name of the location where the WINDOWS
 module was loaded:

     #define PATHMAX 80
     char szTemp[PATHMAX+1];
     GetModuleFileName( GetModuleHandle((LPSTR)"kernel"), szTemp, PATHMAX );
 /*  szTemp now contains the drive and path that you want.  */


 251. The Purpose of WINSTUB in Windows SDK

 Question:

 What is the purpose of WINSTUB?

 Response:

 WINSTUB is provided as a normal DOS program. All it contains is a
 printf() line. WINSTUB is used as a stub if you do not have a DOS
 version of your program. If you would like to have both your Windows
 version and DOS version of the program in one EXE file, replace
 WINSTUB in the DEF file and relink.


 252. SetScrollRange() and SB_CTL in Windows

 Problem:

 SetScrollRange() works differently for SB_CTL than for SB_VERT or
 SB_HORZ. If nMinPos = nMaxPos and SB_CTL, the scroll bar is disabled
 but not removed; however, for SB_VERT or SB_HORZ, the scroll bar is
 removed. If this is intentional, the documentation should be
 clarified. Why would you want them to work differently?

 Response:

 A scroll-bar control cannot be hidden by making the minimum and
 maximum scroll-range positions equal. Although this technique can be
 applied to a scroll-bar control, it disables the control but does not
 hide it. Only standard scroll bars, accessed using SB_HORZ or SB_VERT,
 can be hidden in this manner.


 253. Windows Color Printer Driver and Excel

 Question:

 When testing my driver with Excel, I don't get the right colors. My
 driver supports full 24-bit color (maybe this is the problem). When I
 print a chart, for some reason the output colors (including my device
 and screen preview) don't match the original chart. If I go into each
 object on the chart and actually set the color (instead of just using
 the default), I do get the proper color output. Should I enumerate my
 colors in any special way?

 Response:

 Due to the way that Excel expects the colors to be returned when it
 does an enumerate pen call, you have to enumerate the first eight
 colors to be the same as an EGA display. Therefore, the first eight
 colors should be enumerated as follows:

 Black    0, 0, 0
 White   ff,ff,ff
 Red     ff, 0, 0
 Green    0,ff, 0
 Blue     0, 0,ff
 Yellow  ff,ff, 0
 Magenta ff, 0,ff
 Cyan     0,ff,ff

 You can enumerate your own colors after that. Please note that Excel
 will also enumerate the next eight colors so that Excel is a 16-color
 application, so choose the next eight colors to be something
 desirable.


 254. FillRect() in the MM_LOENGLISH Mapping Mode

 Question:

 When I call FillRect() in MM_LOENGLISH mapping mode, it doesn't do
 anything. What's wrong?

 Response:

 FillRect() checks to see if the top of your rectangle is greater than
 the bottom of your rectangle. If so, the function returns immediately.
 This works properly for MM_TEXT mode, but it is not useful for other
 mapping modes that use positive y = up.

 The following are two workarounds for this problem:

 1. Reset the mapping mode to MM_TEXT before calling FillRect() (be
    sure to change the coordinates as well).

 2. Reverse the top and bottom of your rectangle to avoid the
    filtering.

 We recommend the first workaround when sending the WM_ERASEBACKGROUND
 message (which calls FillRect()); we recommend the latter when drawing
 to a device.


 255. How to Transparently Intercept Procedure Calls in Windows

 Question:
    Windows does not seem to check its arguments for validity. Is there
 any way to correct this?
    Is there any transparent way to enhance the Global memory manager
 to do data swapping?

 Response:
    The answer is yes to both questions.
    There is a technique for aliasing procedure names in Windows that
 allows you to intercept procedure calls transparently. It can be used
 for any type of monitoring activity.
    First, build a Windows executable code library (see the LIBRARY
 application note) that looks like the following:

         HANDLE MyGlobalAlloc( flags, size )
         WORD  flags;
         DWORD size;
         {
             /* Perform any type of monitoring you like here: e.g.

                if ( !size )
                    return 0;

             */
             return RealGlobalAlloc( flags, size );
         }

         HANDLE FAR PASCAL main( argc, argv )
         WORD argc;
         LPSTR argv;
         {
             return 1;
         }

    Then write the definitions file for the library above as follows:

    FOO.DEF:

    LIBRARY FOO
    DESCRIPTION "Procedure call interception library."
    DATA SINGLE MOVEABLE
    CODE MOVEABLE DISCARDABLE
    EXPORTS
           GlobalAlloc=MyGlobalAlloc
    IMPORTS
            RealGlobalAlloc=KERNEL.15

    The ordinal numbers for the KERNEL routines (or any other routine)
 can be obtained by using the LIB.EXE program to list the contents of
 SLIBW or MLIBW.
    You build a dynamic link library by running IMPLIB on FOO.DEF as
 follows:

    IMPLIB FOO.DEF FOO.LIB

    Build the library executable by using the following:

    link4 foo,,,mlibw mlibc,foo.def;

    Then dynamically link to FOO.EXE by linking to FOO.LIB as follows:

    link4 bar,,,foo mlibw mlibc,bar.def;

    Any calls to GlobalAlloc will be routed to MyGlobalAlloc first
 because of the name aliasing.
    The technique is very powerful and can be used to implement any
 type of monitoring function you need. In the case of GlobalAlloc, the
 monitoring is transparent in the sense that you do not need to
 recompile your application to remove error checking; you simply link
 to MLIBW.LIB instead of FOO.LIB MLIBW.LIB.


 256. Windows Resource Numbering Starts at 1

 Question:

 Numbered resources in Windows must start at 1, not zero. Why?

 Response:

 The new DOS Version 4.00 executable format numbers segments from 1 and
 resources are implemented as independent segments. Therefore,
 resources are numbered from 1.


 257. GetDCOrg Not Defined in WINDOWS.H

 Although GetDCOrg(hDC) is discussed in the programmer's reference, it
 is not defined in WINDOWS.H. You can add the definition to the
 GetDCOrg(), as follows:

    DWORD FAR PASCAL GetDCOrg(HDC);

 Note: This definition was added to the WINDOWS.H file that was
 released with the Version 2.10 SDK.


 258. DlgDirSelect() Has Undocumented Behavior

 If you use DlgDirSelect() to get the selection from a list box and
 there is no selection, DlgDirSelect() returns zero and leaves the
 output string unchanged.

 Clicking in the white space after the last item in a list box is one
 reason there may not be a selection.

 Since DlgDirSelect() returns zero when the selection is a file and
 zero when there is no selection at all, using the return value alone
 is not enough to determine if there is a current selection. One way to
 determine if there is a selection is to send the LB_GETCURSEL message
 to the list box. If the return value is not LB_ERR from this message,
 there is a currently selected item in the list box.


 259. Windows SDK: Getting Around Capacity Limits of List Boxes

 Question:

 List boxes are used in my application to show items, such as in a
 database. What is the practical upper limit to the number of items
 that can be contained in a list box?

 Response:

 List box controls are limited to a total capacity of 32K. However,
 there is another problem with list boxes: the control does not clean
 up previously deleted strings (compaction is not performed). The
 application developer can simply keep a count of the bytes put into
 the list box (again, deleted string space is not reclaimed).

 When the amount of strings placed into the list box approaches 32K, do
 the following:

 1. Read the strings to another location.

 2. Delete the list box.

 3. Create a new list box.

 4. Restore the saved strings.


 260. NetBIOS Interrupts under Windows

 Question:

 We utilize NetBIOS in our product. What is the recommended way of
 installing code to handle the NetBIOS interrupt? Could
 MakeProcInstance() be used to create a fixed address location to be
 handed to the NetBIOS software for setting the location of the NetBIOS
 interrupt handling code so that it can be movable? Does the code that
 resets the jump table when moving code turn off interrupts?

 Response:

 You should be making synchronous INT 5C calls only under Windows. Be
 aware that some INT 2A network handlers may generate some asynchronous
 INT 5C calls. When you issue a synchronous INT 5C call, Windows will
 stop processing until your interrupt returns. Therefore, you should
 not have to FIX your data down or worry about segments moving.


 261. Interrupting Timer Messages

 Question:

 Can a WM_CHAR message interrupt a timer procedure or can a timer
 procedure interrupt a WM_CHAR procedure? If yes, how can I hold off
 the interruption?

 Response:

 No, a WM_CHAR message cannot interrupt the procedure that is
 processing a WM_TIMER message. Likewise, a WM_TIMER message cannot
 interrupt a procedure that is processing a WM_CHAR message.

 You can use PeekMessage() to check your input queue for either of
 these messages without being required to remove the message from the
 queue.


 262. Coordinates of Rectangle Returned by BeginPaint()

 Problem:

 When I process a WM_PAINT message in response to a ScrollWindow() or
 other screen disturbance, I cannot determine what is in rcPaint() from
 BeginPaint().

 Response:

 BeginPaint() returns the update rectangle in rcPaint() in screen
 coordinates.


 263. Length of STRINGTABLE Resources

 Question:
    Is there any way to find out how long a string is in the
 STRINGTABLE?

 Response:
    You need to do a FindResource and then a SizeofResource to find the
 total size in bytes of the current block of 16 strings.
    Remember that STRINGTABLEs are stored specially; to FindResource
 you will ask for RT_STRING as the Type, and the (string number mod 16)
 + 1 as the name.


 264. Sending Output to a Different Device in Windows

 Question:

 Our application currently draws to the screen using standard GDI calls
 to LineTo() and Polyline(), etc. If we want to output to a plotter,
 can we use the existing routines and use the DC from CreateDC(), where
 the lpDriverName is HP7475A or a similar plotter device?

 Response:

 Yes. You would use CreateDC() to create a DC for the printing/plotting
 device (the default printer name can be obtained from the WIN.INI
 file, [windows] section, device= key section). Once the DC has been
 created, you can use the same GDI calls and routines.


 265. GetCurrentTime() Rolls Over Every Hour

 Question:

 Does the GetCurrentTime() function roll over to zero every hour?

 Response:

 Yes, this function does wrap after every hour. In fact, it rolls after
 every 3,604,480 ticks.


 266. Hiding the Caret

 Question:

 Mixing certain Windows operations, e.g. StretchBlt and SetCaretPos,
 can leave copies of the caret bitmap scattered on the screen. What
 Windows operations result in these unsightly blotches? Also, during
 which operations should the caret temporarily be hidden (during
 bit-map operations, all GDI operations, or operations such as
 SetCaretPos)?

 Response:

 You should hide the caret when you handle WM_PAINT messages and any
 other time you access the display context that contains the caret.


 267. Windows SDK: Problem with DrawText()

 Problem:

 I am having a problem with the DrawText() function when I try to
 output a string using a stroke (vector) font with the escapement value
 set between 3151 and 3599, inclusive. When I execute the code below,
 my application is terminated, and my PC eventually locks up. I tried
 the code with all three of the stroke fonts provided with the
 Microsoft Windows run-time environment: Roman, Modern, and Script. All
 three of the fonts caused my application to terminate.

 Also, if I increase the value to 3600 or above, I do not have any
 problems. It also appears that the escapement and orientation
 parameters to a logical font only affect stroke or vector fonts and do
 not work for raster fonts.

 Response:

 DrawText() does indeed hang if you meet a specific set of conditions.
 Those conditions are as follows:

    (Escapement - Orientation) % 900  >  450

 To avoid the problem, test for the condition and increment or
 decrement either variable to be outside the above conditions.

 As for the escapement and orientation not working on raster fonts,
 this is not correct. Windows does the best it can for the given
 device. This generally means there is little effect if you are
 attempting small values for either variable, but the effect often will
 be noticeable if the values are increments of 900 (90 degrees).


 268. Changing the Cursor for an Application

 Problem:

 I am unable to change the mouse cursor until the mouse moves. Now,
 when the mouse is first moved (albeit a few pixels), the cursor
 changes, but not until then.

 I have tried first hiding, setting, and then showing, with no change
 in the behavior.

 The requirement is to change the cursor on Mouse Down, not Mouse Down
 and then move.

 Response:

 You need to do the following:

    LoadCursor()
    SetClassWord() /* using GCW_CURSOR */
    SetCursor()

 The SetCursor() call will change the immediate cursor, and the mouse
 will not need to move in order to observe the change.


 269. Windows SDK: Window COLORS and BRUSHES

 Problem:

 My problem concerns window-class background color versus DC background
 color (on an EGA). I defined the window-class background color using
 the following:

    #define ELANGREEN  0x003FFF00
    pTemplateClass->hbrBackground = CreateSolidBrush ( (DWORD)ELANGREEN );

 I then created a background color in my DC, as follows:

    #define SZ -1
    SetBkColor ( hDC, (DWORD)ELANGREEN );
    DrawText ( hDC, (LPSTR)"text", SZ, (LPRECT)&Rect, DT_BOTTOM ) ;

 The text is drawn correctly, but the background color is slightly
 different than the window background color; it has a yellow cast.

 Response:

 You have a clash between BRUSH realization, which may use "dithered"
 colors on some output devices, and "background" colors, which are
 solid.

 Below are two ways to proceed. These choices are based on the fact
 that BRUSHES may have a wider color range than COLORS because BRUSHES
 cover areas, whereas COLORS may be used to paint nominal-width lines
 (i.e., one device-unit wide lines) that must be the same color at all
 locations and angles. The device-driver writer therefore has the
 option of providing dithered COLORS for BRUSHES, but has no such
 freedom when it comes to line-drawing COLORS. Your choices are as
 follows:

 1. Use a solid color for the window background, and use the same
    color for the text background.

 2. Use TRANSPARENT mode for your text; this will not paint background
    behind the text. The window background color will show through
    instead, and everything will look the same.


 270. Turning Off Mouse Interrupts Not Possible

 Question:

 Is there any way to temporarily disable the mouse interrupts?

 Response:

 No, there is no way to turn off the hardware interrupts generated by
 the mouse once the driver has been installed.


 271. Drawtext() DT_* Flags and Rotated Text

 Problem:

 The DT_* flags do not seem to work correctly for rotated text.

 Response:

 The DT_TOP, DT_VCENTER, and DT_BOTTOM format options for use in the
 final argument of DrawText() are used to calculate the position from
 which the first character in the string will be drawn. They are not
 used to justify the entire text to the top, center, or bottom.


 272. Trapping the INS and DEL Keys

 Problem:

 I added a subclass procedure for the list box, as outlined in TRACK.C.
 (TRACK.C can be found in the Software Library by searching on the
 filename TRACK.ARC, the Q number of this article, or S12038.) When
 running the application, a message box appears verifying that a key
 was pressed. This works for most keys except INS and DEL. I thought
 the DLGC_WANTALLKEYS code would trap the INS and DEL keys because that
 method works in an edit-control subclass procedure, but it did not
 work.

 Response:

 A list box does not use the INS or DEL keys. If you want to trap the
 VK_DELETE and VK_INSERT keys, you must add a switch statement to the
 List Box window procedure. The following is the list box procedure
 from TRACK.C with a switch statement added for VK_DELETE and
 VK_INSERT:

 /********************************************************************
  * This is the new window procedure for the ListBox control.        *
  ********************************************************************/
 long FAR PASCAL NewListWndProc(hwnd, message, wParam, lParam)
 HWND hwnd;
 unsigned message;
 WORD wParam;
 LONG lParam;
 {
    char  temp[80];   /* added */

    switch (message) {
      case WM_GETDLGCODE:
           /***********************************************************
            *  When the parent needs to know what keys the control    *
            *  needs to process, it sends this message and it is      *
            *  up to the control to let it know.  In this case        *
            *  here, the control would like all the keys.             *
            ***********************************************************/
           return (DLGC_WANTALLKEYS);
 	  break;
      case WM_KEYDOWN:
           switch(wParam)
 	     {
 	     case VK_DELETE:
 	         MessageBox(hwnd,(LPSTR)"Del",(LPSTR)"hit",MB_OK);
 	         break;

 	     case VK_INSERT:
 	         MessageBox(hwnd,(LPSTR)"Ins",(LPSTR)"hit",MB_OK);
 	         break;
 	     }
 	  break;
      case WM_CHAR:
           /***********************************************************
            *  Warning: Make sure that when you intercept messages    *
            *  and then pass them on, make sure they are going to the *
            *  correct Window, or in some cases to the PARENT.        *
            *  A case of this may be where you want to convert a ENTER*
            *  into a TAB; you will want to pass the converted TAB    *
            *  to the PARENT or the HANDLE to the DIALOG in this case.*
            ***********************************************************/
           sprintf(temp,"%x was pressed",wParam);
           MessageBox(hwnd, (LPSTR)temp, (LPSTR)"WM_CHAR Hit", MB_OK);
           if (wParam == VK_TAB ||wParam == VK_RETURN) {
             /* Let's send a message to the parent to let it know
                what's going on  */
             SendMessage (hParent, WM_USER,0,0L);
             SetActiveWindow (hPushButton); /* put focus on next control */
             return TRUE;
           }
           else
             return CallWindowProc(lpfnListOld, hwnd, message, wParam, lParam)
 	  break;
      default:
           /**********************************************************
            *  This returns all the unprocessed messages back to     *
            *  the original procedure.                               *
            **********************************************************/
           return CallWindowProc(lpfnListOld, hwnd, message, wParam, lParam);
     } /* end switch */
 }


 273. Checking Radiobuttons within a Dialog Box

 Question:

 I have a dialog box with a set of five radio buttons in it, along with
 an OK and a Cancel push button. When the box comes up, the correct
 button has the dark circle in the middle. However, the blinking
 underline is under the left-most button rather than at the button that
 has the dark circle. Why aren't they both at the same button?

 Response:

 In the template code we provide as a sample medium-model application,
 we use the radiobutton control to notify the parent window from a
 dialog box. It is up to the application to check the clicked
 radiobutton and to uncheck all the rest. This is something that you
 need to add to your code. Please look at the template again.

 Also, in our template .RC file, we use a WS_GROUPBOX style to group
 our controls together. This is necessary if you want to have
 CheckRadioButton() automatically check and uncheck buttons for you.
 Otherwise, you need to add code to check and uncheck them yourself, as
 described above.

 Note that when defining your .RC file, all controls following a
 WS_GROUP style belong to the same group. The next control with a
 WS_GROUP style ends the first group and starts the next group.


 274. The MDI Bitmap for a Maximized Child

 Question:
    When using the MDI interface, how do I place the bitmap as the
 first item of my top-level menu when my child gets maximized?

 Response:
    The following code sample calls ChildSysMenu to generate the bitmap
 and obtain its handle, and then addS it to the top-level menu:

   hMenu = GetMenu(hWnd);
   hPopupMenu = CreateMenu();
   hBitmap = ChildSysMenu(hWnd);

   if (hBitmap == NULL)
        MessageBox (hWnd, (LPSTR)"not found", (LPSTR)"bitmap",MB_OK);
      else {
     ChangeMenu(hMenu, 0,(LPSTR)(DWORD)hBitmap,hPopupMenu,
             MF_INSERT | MF_BYPOSITION | MF_POPUP | MF_BITMAP);
     ChangeMenu(hPopupMenu,1,(LPSTR)"more",201, MF_APPEND | MF_STRING );
    }
   DrawMenuBar(hWnd);

 --------------------------------
 #define OBM_CLOSE  32767

 HBITMAP ChildSysMenu( hWnd )
 HWND hWnd;
 {
   BOOL      bResult;
   HDC       hDC, hdc1, hdc2;
   BITMAP    bmp;
   HBITMAP   hbmpClose, hbmpSystem;
   int       dxMenu, dyMenu;

   hbmpClose = LoadBitmap(NULL, OBM_CLOSE);
   GetObject(hbmpClose, sizeof(BITMAP), (BYTE far *)&bmp);
   hDC    = GetDC(hWnd);
   hdc1   = CreateCompatibleDC(hDC);
   SelectObject(hdc1, hbmpClose);
   hdc2   = CreateCompatibleDC(hDC);
   dyMenu = GetSystemMetrics(SM_CYMENU);
   dxMenu = bmp.bmWidth / 2;
   hbmpSystem = CreateCompatibleBitmap(hdc2, dxMenu, dyMenu);
   SelectObject(hdc2, hbmpSystem);
   PatBlt(hdc2, 0, 0, dxMenu, dyMenu, WHITENESS);
   bResult = BitBlt(hdc2, 0, 0, dxMenu, dyMenu,
                    hdc1, dxMenu, 0,
                    SRCCOPY);
   DeleteDC(hdc1);
   DeleteDC(hdc2);
   ReleaseDC(hWnd, hDC);
   return (bResult == NULL ? NULL : hbmpSystem);
 }


 275. Changes to DeviceModes() Function

 Question:

 I have heard that the DeviceModes() function can be used to set up
 printer characteristics. DeviceModes() is no longer documented but can
 be found in WINDOWS.H. What are the new parameters for DeviceModes()?

 Response:

 DeviceModes() is no longer a GDI function. The functions capabilities
 have been made part of the OEM driver layer. Part of the OEM layer is
 the capability of setting a device's characteristics using the
 SetEnvironment() and GetEnvironment() calls.

 Each device has a data structure called DEVMODE. Within the DEVMODE
 data structure are fields for such device characteristics as
 PORTRAIT/LANDSCAPE, PAPER SIZE, etc. You may scan the WIN.INI file for
 the [devices] entry and then bind a copy of a DEVMODE data structure
 to a port to which the device is attached, using SetEnvironment(). It
 then is up to the driver to use GetEnvironment() to get its device
 characteristics from the port.

 It is up to the OEM to publish its DEVMODE data structure.

 For our sample Epson driver, DEVMODE is as follows:

   typedef struct
    {
           short orient;   /* orientation - portrait or landscape */
           short feed;     /* cut sheet or continuous */
           short size;     /* paper size - legal or regular */
    }   DEVMODE;

    Our sample EPSON device driver has a DeviceMode() entry point that
    posts a dialog box using the data obtained from the DEVMODE
    structure. It is entirely up to the device driver writer to decide
    what to do with the data in DEVMODE. For example, you could write
    an EPSON driver that had a field in DEVMODE that was a boolean
    telling the driver whether to take the DEVMODE data and use it
    directly or whether to post the dialog box.

    DECLARATION:

    LPSTR FAR PASCAL DeviceMode(HWND, HANDLE, LPSTR, LPSTR);

    DRIVER EXPORT ENTRY:

    EXPORT
          DeviceMode @13

    EXAMPLE OF USE:

    /* Find driver's DeviceMode() entry.  This is always device driver entry
       13. */
    /* You could also load using string "DEVICEMODE". */

    HANDLE  hDriver = GetModuleHandle( (LPSTR)"Epson" );
    FARPROC lpfnModes = GetProcAddress(hDriver,(LPSTR)(LONG) 13 );
    BOOL    success = (*lpfnModes) ((HWND)hDlg,        /* parent window */
                                   (HANDLE)hDriver, /* module handle */
                                   (LPSTR)szLBPrint,/* printer name */
                                   (LPSTR)pszPort   /* port name */
                                   );


 276. F6 Key in Application Style Guide

 Problem:
    One of my applications has five modeless dialogue boxes opened and
 stacked from oldest to newest. The newest is fully visible on top of
 the pile, with the input focus.
    Using only the keyboard, I cannot access the other four modeless
 dialogue boxes. While holding down the ALT key, if I repeatedly press
 TAB, I move from the top modeless dialogue box to each of the ICONed
 windows and back to the modeless dialogue box.

 Response:
    ALT-TAB is used to switch between the last two applications with the
 focus. ALT-ESC is used to switch between applications round robin.
    According to the applications style guide, you will want to use F6.
    The suggested uses are as follows:

 F6             Selects the next child window within a child.
 SHIFT-F6       Selects the previous child window within a child.
 CTRL-F6        Selects the next child window within main window.
 SHIFT-CTRL-F6  Selects the previous child window within main window.
 ALT-F6         Selects the next non-child window.
 SHIFT-ALT-F6   Selects the previous non-child window.

    However, the final two lines require some clarification. Under
 "cascading" (1st is parent of 2nd is parent of 3rd...) modeless dialog
 boxes, the description given is accurate, as in the following:

 ALT-F6         Selects the next non-child window.
 SHIFT-ALT-F6   Selects the previous non-child window.

    However, if the modeless windows all have the same parent, the uses
 will change, as in the following:

 ALT-F6         Alternates between two non-child window.
 SHIFT-ALT-F6   Circles non-child windows, going to parent
                between each step.

    Finally, if all the modeless windows have NULL as the parent
 window, the effect is again circling among the windows, this time
 including the parent in the circle.


 277. Disabling Expanded Memory in Slow Boot Windows

 Question:
    In the installed version of Windows, "/n" is the switch to use to
 disable expanded memory. What is the correct syntax for disabling EMS
 memory when using the slow boot version of Windows?

 Response:
    You cannot disable expanded memory with the slow boot version of
 Windows unless you are using Win386. If you are using Win386 you can
 set the EMMSIZE to zero in the WIN.INI file. In the installed version
 of Windows, WIN.COM handles the /n switch. Since WIN.COM is not
 present in the slow boot version of Windows, disabling expanded memory
 is not possible.


 278. Free Code to Draw Circles and Ellipses in MM_TEXT Mode

 Microsoft does not support or advise, for or against, the use of the
 following routine to plot an ellipse:

 ;****************************************************************
 ;  ROUTINE TO PLOT AN ELLIPSE                                   *
 ;  ellipse(Xcenter, Ycenter, Radius, Xaspect, Yaspect)          *
 ;                                                               *
 ;        NOTE: The Xaspect and the Yaspect equate roughly to    *
 ;              the major and minor axis of an ellipse. When     *
 ;              drawing an ellipse the Radius and the Xaspect    *
 ;              should be the same. When plotting a circle the   *
 ;              Xaspect and Yaspect should equate to the aspect  *
 ;              ratio of the screen. For example, Xaspect = 89,  *
 ;              Yaspect = 40, Radius = 100. This is done as a    *
 ;              matter of convenience.                           *
 ;                                                               *
 ;              On a 640x200 IBM display, 89 should be used for  *
 ;              the Xaspect and 40 for the Yaspect. This produces *
 ;              THE BEST VISUAL CIRCLE. (5:12 produces a flattened *
 ;              circle).                                         *
 ;                                                               *
 ;      QUALIFICATION:                                           *
 ;              This routine produces a well formed curve that   *
 ;              is almost closest to the true curve. There are   *
 ;              other routines that will produce a better curve, *
 ;              but with more computation and the resultant curve *
 ;              is not that much better than the one produced    *
 ;              here.                                            *
 ;                                                               *
 ;  ellipse(Xcenter, Ycenter, Radius, Xaspect, Yaspect)          *
 ;  int Xcenter, Ycenter, Radius, Xaspect, Yaspect;              *
 ;  {                                                            *
 ;      long Xasq, Yasq; /* need longs for squared terms    */   *
 ;      long s;          /*                decision variable */   *
 ;      long yinc, xinc, ysum, xsum; /* and accumulators    */   *
 ;      int x, y;                                                *
 ;                                                               *
 ;      Xasq = Xaspect * Xaspect;                                *
 ;      Yasq = Yaspect * Yaspect;                                *
 ;      s = -Yasq * Radius;                                      *
 ;      yinc = 2 * Xasq; xinc = 2 * Yasq;                        *
 ;      ysum = 0; xsum = 2 * Yasq * Radius;                      *
 ;      y = 0; x = Radius;                                       *
 ;      while (x >= 0) {                                         *
 ;           plot4(Xcenter, Ycenter, x, y);                      *
 ;           if (s < 0) {                                        *
 ;               y = y + 1;                                      *
 ;               ysum = ysum + yinc;                             *
 ;               s = s + ysum;                                   *
 ;           }                                                   *
 ;           if (s >= 0) {                                       *
 ;               x = x - 1;                                      *
 ;               xsum = xsum - xinc;                             *
 ;               s = s - xsum;                                   *
 ;           }                                                   *
 ;      }                                                *
 ;  }                                                            *
 ;                                                               *
 ;  plot4(Xc, Yc, x, y)  /* plot all 4 quadrants */              *
 ;  int Xc, Yc, x, y);                                           *
 ;  {                                                            *
 ;      plot(Xc + x, Yc + y);                                    *
 ;      plot(Xc - x, Yc + y);                                    *
 ;      plot(Xc + x, Yc - y);                                    *
 ;      plot(Xc - x, Yc - y);                                    *
 ;  }                                                            *
 ;                                                               *
 ;****************************************************************
 ;

                                 dseg
 YINCH:          dw      0
 YINCL:          dw      0
 XINCH:          dw      0
 XINCL:          dw      0
 XSUML:          dw      0
 XSUMH:          dw      0
 YSUML:          dw      0
 YSUMH:          dw      0

                                 cseg
                                 public  ellipse_
 ELLIPSE_:       push    bp
                                 mov     bp,sp
                                 call    ell_setup ; initialize all parameters

 ELLIPSELP:      call    plot4                   ; plot 4 quadrants
 PTLOOP:  or     di,di                   ; is s < 0 ?
                                 jns     check_x        ; jump if no
                                 call    update_y       ; update y
 CHECK_X: or     di,di                   ; is s >= 0 ?
                                 js      ellipselp       ; jump if no
                                 call    update_x        ; update x
                                 jns     ellipselp     ; keep going if x >= 0
 ELLRET:  pop    bp
                                 ret

 ELL_SETUP:      mov     ax,[bp+10]              ; get Xaspect
                                 mul     ax                ; square it
                                 shl     ax,1              ; times 2
                                 rcl     dx,1
                                 mov     yinch,dx          ; save it
                                 mov     yincl,ax
                                 mov     ax,[bp+12]        ; get Yaspect
                                 mul     ax                 ; square it
                                 shl     ax,1               ; times 2
                                 rcl     dx,1
                                 mov     xinch,dx           ; save it
                                 mov     xincl,ax
                                 mov     cx,dx
                                 mul     word [bp+8]        ; get Radius
                                 mov     si,ax             ; compute xsum
                                 mov     di,dx
                                 mov     ax,cx
                                 mul     word [bp+8]
                                 add     di,ax
                                 mov     xsuml,si
                                 mov     xsumh,di
                                 shr     di,1               ; compute s
                                 rcr     si,1
                                 not     si
                                 not     di
                                 add     si,1
                                 jnc     inity
                                 inc     di
 INITY:          xor     bx,bx                   ; set y to 0
                                 mov     ysuml,bx       ; set ysum to 0
                                 mov     ysumh,bx
                                 mov     ax,[bp+8]          ; get x
                                 ret

 UPDATE_Y:       mov     dx,yincl
                                 add     ysuml,dx
                                 mov     dx,yinch
                                 adc     ysumh,dx
                                 add     si,ysuml
                                 adc     di,ysumh
                                 inc     bx
                                 ret
 UPDATE_X:       mov     dx,xincl
                                 sub     xsuml,dx
                                 mov     dx,xinch
                                 sbb     xsumh,dx
                                 sub     si,xsuml
                                 sbb     di,xsumh
                                 dec     ax
                                 ret

 PLOT4:          push    bx                      ; save y
                                 push    ax                ; save x
                                 mov     cx,[bp+4]         ; get Xcenter
                                 mov     dx,[bp+6]         ; get Ycenter
                                 add     ax,cx             ; add Xc offset
                                 shl     cx,1           ; adjust Xc for later
                                 add     bx,dx             ; add Yc offset
                                 call    point            ; plot all 4 points
                                 neg     ax
                                 add     ax,cx
                                 call    point
                                 neg     bx
                                 add     bx,dx
                                 add     bx,dx
                                 call    point
                                 neg     ax
                                 add     ax,cx
                                 call    point
                                 pop     ax                 ; restore x
                                 pop     bx                 ; restore y
                                 ret

 ; routine to plot a point on the screen
 ; given x = ax and y = bx

 POINT:   ; your code here


 279. Reserved Application Names in Windows

 Question:

 We have a Windows application with the filename DISPLAY.EXE. Windows
 will not invoke it. If we rename the file to (almost) anything else,
 it works correctly. Have we violated any rules or used a reserved
 word? If so, please document all such rules.

 Response:

 You have found a name that you are not allowed to use for an
 application. DISPLAY.EXE is reserved for DISPLAY BOARD manufacturers
 that are building display drivers so they are able to test their
 drivers.

 The following are some other names that you are not allowed to use:

       SPOOLER.EXE
       CLIPBRD.EXE
       CONTROL.EXE
       GDI.EXE
       USER.EXE
       KERNEL.EXE
       MSDOS.EXE
       MSDOSD.EXE
       MOUSE.EXE
       SYSTEM.EXE
       WINOLDAP.EXE
       KEYBOARD.EXE
       COMM.EXE
       WIN.EXE
       WIN200.EXE


 280. Registering Unique Names

 Question:

 When you register a window, you give the system a name of the window
 class. How global is the name space? If a different application
 happens to use the same name for one of its windows, will there be a
 conflict, or does Windows use the application as well as the window
 name to distinguish registered windows?

 Response:

 The name space is global to everyone. For example, some people make
 sample test programs called EDIT, compile the program, and then try
 running their application. All goes well until they bring up a dialog
 box that has an edit control in it. Then things do not work like they
 used to. You must make your registered names unique.


 281. Keeping an Application in the Zoomed State

 Question:

 Is there any way to make an overlapped window always be in the zoomed
 state?

 Response:

 Yes, there is a way to zoom an application and keep it zoomed until
 the application is closed. To do this, you must trap the following
 system command, non-client, and move/size messages

     case WM_NCLBUTTONDBLCLK:
     case WM_NCRBUTTONDBLCLK:
     case WM_NCMBUTTONDBLCLK:
     case WM_SHOWWINDOW:
     case WM_SIZE:
     case WM_MOVE:
     case WM_SYSCOMMAND:

 where wParam is as follows:

         case SC_SIZE:
         case SC_MOVE:
         case SC_ICON:
         case SC_ZOOM:

 The ShowWindow(hWnd,SHOW_FULLSCREEN) function must be called after
 each of the above messages. The non-client area messages will need
 some further filtering to allow a double click on the system menu box
 to close the application.

 This assumes that the first call to ShowWindow() is passed the
 SHOW_FULLSCREEN argument.


 282. Documentation Change between Versions 1.0x and 2.03

 Question:
    Is there any documentation on the differences between Windows
 Versions 1.03 and 2.03?

 Response:
    There is a file called CHANGE.ARC in the Software Library that has
 a list of all of the new and revised functions. The new functions and
 new window messages also have descriptions following them. Windows
 Version 2.03 will be a complete replacement of Version 1.03.
    This file can be found in the Software Library by searching for
 CHANGE.ARC, the Q number of this article, or S10060.


 283. FillRect(): Border Not Drawn

 Problem:

 I have a program that first puts up a rectangle. A call is then made
 to FillRect() to fill part of the interior with a BLACK_BRUSH. The
 part to be filled is the interior of the drawn rectangle, but leaving
 a blank set of pixels between the border and the filled portion. This
 results in a black rectangle surrounded by a set of white pixels (one
 pixel thick) surrounded by a set of black pixels (one pixel thick).

 However, the white pixels on the right side of the black-filled
 rectangle are two pixels thick, rather than one. The same is true for
 the bottom. The top and left sides are correct.

 Response:

 This is not a problem in FillRect(), but an omission in documentation
 in the "Microsoft Windows Software Development Kit Programmer's
 Reference." On the right and bottom sides of the rectangle, you must
 allow for n-1 extents for the rectangle to be drawn correctly.


 284. Window Capture and Mouse Tracking

 Question:

 Is there a simple mechanism to cause a message to be sent to a window
 whenever the mouse moves outside of the window? I need my application
 to plot its own cursor for various reasons and to know when the mouse
 leaves the window.

 Response:

 No, there is no simple mechanism for doing this. In the meantime, you
 may try the following:

 1. Use SetCapture() on a handle to a window to direct all subsequent
    mouse input to be sent to the currently selected window, regardless
    of the position of the mouse cursor.

 2. Next, use the lParm of WM_MOUSEMOVE, which contains the x and y
    coordinates of the mouse cursor. The x coordinate is in the
    low-order word; the y coordinate is in the high-order word (these
    coordinates are always relative to the top-left corner of the
    window). To extract this information into a POINT structure, use
    the MAKEPOINT macro.

 3. Now, use PtVisible( hDc, X, Y) to track whether or not the given
    point is within the clipping region of the specified display
    context.

 Remember that mouse coordinates are in screen coordinates, so you need
 to perform ScreenToClient(). The new client coordinates replace the
 existing values in lpPoint.


 285. Define Your Own Format Resources and Bind Them to EXE

 The types of resources bound to a Windows application are not limited
 to those types defined in the Windows SDK; you can define your own
 type of resource. One way this can be done is described on Page 32 of
 the "Microsoft Windows Software Development Kit Programming Tools"
 manual Version 2.10. Using this technique, you can include
 information stored in another file in the executable.

 There is an example in the Software Library of how to implement
 user-defined resources. This file can be found in the Software Library
 by searching on the filename MYRES.ARC, the Q number of this article,
 or S12223.


 286. Windows SDK: How to Get Bitmaps to Work Properly

 Problem:

 I cannot get bitmaps to work. Please give me examples.

 Response:

 The ISV toolkit contains sources to CARDFILE. CARDFILE saves and
 restores bitmaps. This code should be consulted if you wish to see
 bitmaps in use.

 GDI defines a strict format for monochrome bitmaps. It does not define
 a format for color bitmaps. In GDI, a bitmap is a file that contains
 the following header:

 typedef struct {
     short      bmType;
     short      bmWidth;
     short      bmHeight;
     short      bmWidthBytes;
     BYTE       bmPlanes;
     BYTE       bmBitsPixel;
     char FAR * bmBits;
 } BITMAP;

 For monochrome bitmaps, the field bmBits is null and followed by the
 bits that make up the bitmap.

 Bitmaps are stored as a series of scan lines that are bmWidthBytes
 wide, bmWidth pixels wide, and bmHeight pixels high. Pixels are
 ordered zero to seven within a byte with zero being the left-most
 pixel. Within a scan line, the first byte is the left-most byte.

 Selecting a bitmap into a DC (display context) using SelectObject()
 means that raster operations on the DC should use the selected bitmap.
 For bitmaps, the origin is always (0,0).

 A logical bitmap is a description of a bitmap. A physical bitmap is
 the bitmap itself.


 287. Windows SDK: Creating a Window without a Title Bar

 Question:

 How do you create a window without a title or caption bar?

 Response:

 To create a window without a caption or title bar, create a pop-up
 Window. Since this is a pop-up, you will have to give it a starting
 dimension, as follows:

   hWnd = CreateWindow((LPSTR)szAppName,
                      (LPSTR)szMessage,
                       WS_POPUP | WS_DLGFRAME,
                       0,0,200,200,
                      (HWND)NULL,
                      (HMENU)NULL,
                      (HANDLE)hInstance,
                      (LPSTR)NULL
                      );

 You can also use WS_BORDER instead of WS_DLGFRAME.


 288. SetTimer/KillTimer Document

 Problem:
    When I call SetTimer as follows:

       btimer = SetTimer(NULL,NULL,260,(FARPROC)myroutine);

 I get a return of 142, which I presume to be good, since the
 documentation says it returns a zero if the timer is not set.
    My routine, which I have declared as the following:

       MyRoutine(hWnd, Message, ID, Time)
       HWND hWnd;
       unsigned Message;
       WORD ID;
       DWORD Time;

 is apparently called when the timer has expired. When I try to kill
 the timer as follows:

       KillTimer(hWnd, ID);

 the return is FALSE (timer not found).
    Since the ID with which MyRoutine is called is 33123, not 142, I
 have also tried calling KillTimer with the ID returned by the SetTimer
 call, that is, 142; this also returns FALSE. In both cases, MyRoutine
 is then called many times and never gets to finish.
    I also tried calling the SetTimer routine with the window handle
 set and an ID of 1; the returned value was 142, and I still could not
 kill the timer.

 Response:
    The documentation on Page 454 of the "Microsoft Windows Software
 Development Kit Programmer's Reference" is unclear on this issue. It
 should read something like the following:

       SetTimer( hWnd, nIDEvent, wElapse, lpTimerFunc ): nIDNewEvent
       ...

    hWnd is a handle to a window.
    If hWnd is NULL, nIDEvent is ignored and SetTimer returns a unique
 nIDNewEvent that can be use to kill the timer (you do not want to kill
 someone else's timer by mistake).
    If hWnd is non-NULL, nIDEvent is not ignored and must be used to
 kill the timer. In this case, nIDNewEvent should be interpreted as a
 boolean indicating whether SetTimer succeeded.
    nIDEvent is an integer value identifying the timer event.
     Now, you also must make sure that if you use the following:

       SetTimer( NULL, NULL, 260, (FARPROC)MyRoutine );

 and if MyRoutine is not a return value from MakeProcInstance, then you
 should do the following:

    1. Export MyRoutine in the .DEF file,
    2. set lpfnMyRoutine = MakeProcInstance( (FARPROC)MyRoutine,
       hInstance );
    3. idTimer = SetTimer( NULL, NULL, 260, (FARPROC)lpfnMyRoutine);

    Calling SetTimer without a window handle or an ID value associates
 the timer with the application queue of that window, which happens to
 be the current queue running at the time of the call to SetTimer. To
 kill a timer created in this manner, the format of the call is as
 follows:

       KillTimer( NULL, idTimer).


 289. Ill-Behaved Applications and Memory in Windows

 Problem:

 I start COMMAND.COM as a "bad" application. I then run a batch file
 that runs Lotus 1-2-3. I then switch back to Windows using ALT+TAB and
 double click on Lotus 1-2-3 and run it as a good PIF application.
 However, Windows says "not enough memory."

 Should Windows partition swap with Lotus 123? There should be enough
 room.

 Response:

 This is a subtle problem. When a bad application is run, a video
 buffer is created. In our case, COMMAND.COM allocates a video buffer
 of 4K. The partition size of COMMAND.COM is, for example, 32K + 4K.

 When Lotus 1-2-3 runs from the batch file, it inherits the video
 buffer of its parent COMMAND.COM. The partition size for Lotus 1-2-3
 in this instance is 192K for this example.

 When you ALT+TAB and run Lotus 1-2-3 by double clicking, the needed
 partition size is 192K plus 4K for the "parent" video buffer.

 Since the 196K needed by the PIF Lotus will not fit in the bad 192K
 partition, you get the "out-of-memory" message.

 The strategy used in partition swapping is part of the OEM adaptation.
 It is not guaranteed to be uniform across machines.


 290. Definition of Pen Width

 Problem:
    A problem occurs with pens with thick line widths and 1 or 2 pixel
 high PolyLine, Polygon, or LineTo-MoveTo figures. When drawn, the
 lines of the figure extend beyond the X bounds given for the figure.
 This problem appears on both a CGA and an EGA system. Other heights
 (more than 2 pixels) for the figure presents no problem. The same code
 works properly in Version 1.04.
    To see the problem do the following:

    1. Remake TRACK using the following line to create the pen:

       hpen = CreatePen( PS_SOLID, 5, GetSysColor(COLOR_WINDOWTEXT) );

    2. Run TRACK.EXE
    3. Draw out a Star that is 1 or 2 pixels high (3 pixels or more
       works properly).

    The resulting figure extends significantly beyond the original X
 bounds of the drawn-out rectangle.

 Response:
    Your question brings forward an important issue relative to the
 definition of pen width. A pen's width is drawn with respect to the
 LOGICAL coordinate system's horizontal axis. This definition implies
 that a pen created using the following

    hpen = CreatePen( PS_SOLID, 5, GetSysColor(COLOR_WINDOWTEXT) );

 will be exactly 5 pixels wide only if the mapping mode is MM_TEXT (or
 MM_ANISOTROPIC with the horizontal resolution set to pixel width).
    Regarding your version of TRACK, whether the pen overflowed the
 rectangle defined using the mouse is much more dependent upon the
 width of the rectangle than the height. Try making a star in a
 rectangle that is the entire width of the screen and 10 pixels high.
 The star still overflows because the width is large. Drawing a star
 that is only 10 pixels wide and 10 pixels tall will work quite well.
    If you actually wish to restrict the drawing to the area defined
 with the mouse, use SelectObject() to select the clipping region (you
 could use SelectClipRgn(), but then you do not have the handle to
 reinstate the region once you are done). Use something similar to the
 following code:

     hcliprgn = CreateRectRgnIndirect((LPRECT) &rcPaintOutline);
     hRgnOld = (HRGN) SelectObject( hDC, hcliprgn);
     switch (cCurrentShape) {
       case IDDCLEAR:
         break;
       case IDDRECT:
         DrawRect( hDC );
         break;
       case IDDELLIPSE:
         DrawEllipse( hDC );
         break;
       case IDDTRIANGLE:
         DrawTriangle( hDC );
         break;
       case IDDSTAR:
         DrawStar( hDC );
         break;
       }
     SelectObject( hDC, hRgnOld);
     DeleteObject( (HRGN)hcliprgn );


 291. Loading Dynamic Link Libraries

 Question:

 How are dynamic link libraries (DLLs) loaded?

 Response:

 Dynamic link libraries can be loaded in the following three ways:

 1. You can preload the library, which causes the system to load the
    library immediately after loading an application that will make
    calls to that library (the application imports some of the
    library's functions in the application's .DEF file).

 2. The library can be LOADONCALL, which will cause the system to load
    the library after a call has been made to one of the library's
    exported functions. Again, the application has imported this
    function in the application's .DEF file.

 3. The library can be loaded by an application using the LoadLibrary()
    function.


 292. Detecting Downloadable Fonts for Windows

 Problem:

 If a font must be downloaded to the printer, there is no way for the
 application to determine this.

 Response:

 You are correct. Note that when a device driver is loaded by the
 Graphics Device Interface (GDI) it is the device driver that registers
 the fonts it is capable of realizing. It is the device driver that is
 responsible for "loading" fonts. You may query the system to find out
 the fonts that the system is capable of delivering (Enumerate Fonts).

 There is nothing that prevents an OEM from realizing an ESCAPE
 function for the device driver that allows a font to be downloaded.
 The ESCAPE function is provided by every device driver through a
 "control" procedure.


 293. PostMessage Caveat

 Problem:
    I am writing a Windows application that transfers files between an
 IBM host computer and a PC. The application displays a small pop-up
 window so I can monitor the state of the transfer while interacting
 with other applications. However, a problem arises when I try to
 interact with other applications. The structure of the program is as
 follows:

    1. Initialize window.
    2. Lower task priority and send message to myself to start
       transfer.
    3. While not EOF, do the following:
       a. Send a record to host and update count.
       b. Post message to myself to continue transfer.
    4. Send message to myself to clean up and PostQuitMessage.

    In Step 3, the application must wait for the host to acknowledge
 the receipt of every block of data sent. This wait takes time, so
 while I monitor the host for the acknowledgment, I repeatedly call
 PeekMessage(). According to the documentation, PeekMessage should
 allow other applications to run.
    However, from the time I send the first message to myself to start
 the transfer, no other applications can run.

 Response:
    The problem lies in posting a message to yourself. GetMessage()
 will continue to empty your message queue (without yielding) as long
 as messages remain within the queue, regardless of your priority. As
 long as you continue to post yourself a message from within your
 WinProc routine, your queue always has a message to process and thus
 never yields.
    Furthermore, if you put specific calls to Yield() within your loop,
 the focus cannot be transferred to other applications until your
 message queue is empty. Specific calls to Yield() would allow other
 applications to repaint themselves, but they cannot receive the focus
 from your application until its message queue is cleared.
    Focus is not given up because there may be mouse messages or
 keyboard messages in the queue. The queue serializes these events.
 Keyboard events and mouse events must be processed serially. Yield()
 is not smart enough to know which messages are in the queue. In fact,
 since Yield() is a kernel function, it would not be a desirable for it
 to have knowledge of the queue. Thus, it is not advisable to post a
 message to yourself; this should only be done under either of the
 following two circumstances:

    1. You know that the message will not repeat itself.
    2. You place the following code immediately before the call to
       PostMessage():

    while (PeekMessage((LPMSG)&msg, NULL, 0, 0, FALSE)) {
      /* Make sure the message on the queue is not the message */
      /* that is about to be posted.  If it is, remove it and */
      /* take appropriate action.  If not, process it.        */
        if (msg.message == wCountMsg)
          PeekMessage((LPMSG)&msg, NULL, 0, 0, TRUE);
          ++nCount;
        else {
          GetMessage((LPMSG)&msg, NULL, 0, 0);
          TranslateMessage((LPMSG)&msg);
          DispatchMessage((LPMSG)&msg);
          }
        }

    This code will empty your queue and Yield() in order to give other
 applications a chance. The call to PeekMessage() returns nonzero if a
 message is in your queue, which is then processed by the
 Get/Translate/Dispatch sequence. If there is no message in the queue,
 PeekMessage() yields before returning.
    Were it not for the line "if (msg.message == wCountMsg)", inserting
 the GetMessage() function within a routine inside your WndProc routine
 could be precarious, especially if it is possible to receive this
 message from anywhere other than the location that you are preceding.
 If the message can come from other applications communicating with
 your application, the possibility of deep recursion develops. Be
 careful.
    Finally, we suggest the use of SetTimer() as the preferred method
 to perform polling. It takes fewer messages to perform the same task,
 allows you to poll at regular intervals, and avoids the difficulties
 that you experienced in posting messages to yourself.


 294. Details of Windows Memory Management

 Question:

 How does Windows' swapping work?

 Response:

 The GlobalAlloc(), GlobalReAlloc(), etc., routines allocate and
 manipulate segments. In the Windows memory model, a segment can be up
 to one megabyte in size. When the GlobalAlloc() routine returns a
 handle, it is returning an index that describes a fixed or relocatable
 segment (i.e., fixed or movable). A locking routine must be applied
 to the index to generate an address (GlobalLock()).

 The LocalAlloc(), LocalRealloc(), etc., routines allocate and
 manipulate blocks of memory within a segment. A block of memory
 allocated by GlobalAlloc() may be initialized for local allocation
 using LocalInit(). When you specify HEAPSIZE in a definitions file,
 you are telling the Windows loader to do a GlobalAlloc() and
 initialize the resulting segment for local allocation. As with
 GlobalAlloc(), the Local routines return an index that may be locked
 to produce an address (LocalLock()).

 The characteristics of a memory block (LocalAlloc()) or segment
 (GlobalAlloc()) may be changed using the ReAlloc routines and the
 appropriate flag word.

 Memory blocks or segments that are MOVEABLE also may be marked as
 DISCARDABLE. This means that if the memory manager needs space, it may
 throw away any blocks marked as DISCARDABLE. The next time you do a
 Lock on the handle that points to a discarded block, the Lock returns
 NULL.

 Some optimizations are available for LocalAlloc(). A LocalAlloc() of a
 fixed item is equivalent to a "char *" (pointer to char). A
 LocalAlloc() of a movable item is equivalent to a "char **" (pointer
 to a pointer to char). Thus, Local handles do not necessarily need to
 be locked to be used. This saves a lot of programming overhead.

 Global handles must always be locked to be used. This is because
 Global handles have different internal representations on different
 DOS's.

 The Windows Swapsize refers to the amount of memory to be set aside
 for partition swapping old applications, regardless of their PIF
 requirements. It has nothing to do with regular memory management.


 295. DUMP.ARC: Windows Screen Dump Utility

 There are two Windows application programs named DUMP.EXE and LJ.EXE
 in the Software Library that enable you to create a bitmap file and
 then print it out.

 DUMP.EXE creates bitmap files (.DMP) for CGA and EGA high-resolution
 Windows screens.

 LJ.EXE takes a .DMP file and turns it into a (.OUT) file that an HP
 LaserJet can print out. The file can then be copied to the printer
 port that is attached to the LaserJet. The LaserJet must be in
 portrait mode for this to work.

 DUMP.ARC contains the files DUMP.EXE and LJ.EXE, and can be found in
 the Software Library by searching for the filename DUMP.ARC, the Q
 number of this article, or S10037. DUMP.ARC has been archived with
 PKARC so you will need to unarchive it with PKXARC. The PKXARC utility
 is located on your OnLine Utilities Disk 2.


 296. How SetSwapAreaSize Works

 Question:
    How does SetSwapAreaSize work, and when should it be called? How
 does one determine a good size?

 Response:
    The swap area is the amount of memory needed to load the largest
 DISCARDABLE segment that is not currently loaded. Windows
 appropriately adjusts this size as applications are started and
 terminated.
    SetSwapAreaSize was provided to allow the programmer the ability to
 change the CODE swap area size. To see this in action, use HEAPWALK.
 There you will see a line called "code fence", which you are able to
 move using HEAPWALK.
    You should never call SetSwapAreaSize. It was provided for people
 making standalone run-time versions of their applications who were
 familiar with how Windows was using memory.


 297. Documentation Error: SW_OTHERZOOM/UNZOOM Not Sent

 Problem:

 Pages 592 and 593 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual state that the WM_SHOWWINDOW message
 with lParam equal to SW_OTHERZOOM will be sent to the window I am
 currently working on if another window is being maximized. This
 message worked in Windows Version 1.x, but it does not work in Windows
 Version 2.03.

 Response:

 This is a documentation error. The SW_OTHERZOOM and SW_OTHERUNZOOM
 messages are not sent if another window is being maximized. In Windows
 Version 2.03 there are no messages sent to a window when it is covered
 or uncovered.


 298. Using Expanded Memory with Windows, -l on RC Line

 Problem:
    When I run my application that uses LIM Version 4.0 and the -l
 switch for the RC compiler, it seems that Windows does not use
 expanded memory any more.

 Response:
    Windows will not use expanded memory if your application makes any
 LIM 4.0 calls. If you want to use expanded memory directly with your
 application, you must use only LIM 3.2 calls.


 299. Focus on Different Controls

    To keep the focus in a dialog control window procedure after
 receiving a WM_KILLFOCUS message, subclass both controls involved in
 the focus change. The following model will have the desired effect:

   case WM_KILLFOCUS:
        if (input == correct)
           global.ok = TRUE
        else
           global.ok = FALSE
        break;

    When you hit a TAB to go to the next control, you get a
 WM_KILLFOCUS message. At this time you need to determine if the input
 is correct.
    Next Windows will send the WM_SETFOCUS message to the NEXT control
 in line; wParam is the handle to the window losing the focus.

   case WM_SETFOCUS:
        if (global.ok == TRUE)
           ;
        else
           SetFocus (wParam);  /* return control to the previous window */
        break;


 300. Help on Right Side of Menu Bar

    To get the help menu on the right side of the menu bar, add "\a"
 before the beginning of the menu text. The following is sample code
 fragment for a menu definition in the resource file:

    MENUITEM "\aHelp", ID_HELP, HELP


 301. Trapping the SHIFT-TAB Sequence

    To detect the SHIFT-TAB sequence, add the following code to your C
 file:

 /* Put the following in your WndProc part of the program */

     case WM_KEYDOWN:
 	if((wParam == VK_TAB) && (GetKeyState(VK_SHIFT) & 0x8000))
 	    MessageBox(hWnd, (LPSTR)"shift down", (LPSTR)"Tab Hit!",MB_OK);
 	    break;


 302. Escape(NEXTBAND) and Escape(NEWFRAME) Reset DC Attributes

 When the end of a page is reached during printing, the contents of the
 device context are reset to the default attributes for the device.
 This is true whether you are banding [using Escape(NEXTBAND)] or not
 [using Escape(NEWFRAME)].


 303. Undo, the ALT-BACKSPACE Accelerator

 Question:
    The application-style documentation for Windows Version 2.00
 specifies that the ALT-BACKSPACE key combination should be used for
 the "Undo" function. How can I specify this key combination as an
 accelerator in the .RC file? I do not see any way to do it. What have
 I missed?

 Response:
    The following is an example of an accelerator table incorporating
 the ALT-BACKSPACE keystroke for an undo operation, as well as examples
 of other types of accelerator key combinations:

 TestApp ACCELERATORS
 BEGIN
         VK_BACK,    IDM_UNDO,  VIRTKEY,  ALT    /* This is the one... */
         VK_DELETE,  IDM_CUT,   VIRTKEY,  SHIFT
         VK_INSERT,  IDM_COPY,  VIRTKEY,  CONTROL
         VK_DELETE,  IDM_CLEAR, VIRTKEY
 END

    The above table is on Page 127 of the Windows learning guide. The
 learning guide will be shipped with the Version 2.00 SDK.


 304. DDE Request to Excel

 Question:
    The REQUEST function does not work with EXCEL. I receive the
 request message but EXCEL will not process the WM_DDE_DATA I send it
 back.

 Response:
    You also need to set the fResponse bit in the WM_DDE_DATA message
 (bit 12). This bit tells Excel that the data message is in reply to a
 REQUEST and not an ADVISE. So if you add lpddeup->fResponse=1 the
 request function should work correctly.


 305. MENUITEMTEMPLATE, LoadMenuIndirect Defined

 Question:
    The structure MENUITEMTEMPLATE is documented in the Windows Version
 2.00 manuals, but is not defined in WINDOWS.H. Please clarify the
 usage of MENUITEMTEMPLATE entries.

 Response:
    Windows supplies all the functions necessary to dynamically build,
 change, and delete menus without the LoadMenuIndirect function.
 Following is the structure of the MENUITEMTEMPLATE:

 struct {
   BYTE  mtOption;
   WORD  mtID
   LPSTR mtString;
 } MENUITEM;

    This is the general structure. The only place this does not work in
 this manner is when you have a popup. To start a pop-up item, if it is
 the first of many popups, mtOption will have a value of 10. If it is
 the last item, mtOption has a value of 90. You get 90 from ORing the
 values POPUP|END, 10|80. If it is a pop-up item, the next byte is
 mtString; mtID is omitted.
    Here are the #defines for the menu options that are listed on Page
 625 of the Version 2.00 "Microsoft Windows Software Development Kit
 Programmer's Reference" manual:

 /* menu option bits */
 #define CHECKED       0x08
 #define END           0x80
 #define GRAYED        0x01
 #define HELP          0x03
 #define INACTIVE      0x02
 #define MENUBREAK     0x40
 #define MENUBREAKWBAR 0x20
 #define POPUP         0x10


 306. Windows 2.03 README File

    Below is the README file for Windows Version 2.03.

 ===========================================================================
                    MICROSOFT WINDOWS VERSION 2.0
 ===========================================================================

 DOCUMENTATION CORRECTION

 PIF Editor
 ----------
 Page 182, "Microsoft Windows User's Guide"
 The manual says the PIF Editor, PIFEDIT.EXE, may be located in
 the PIF directory, a subdirectory of the main Windows directory.
 Actually, it is in the main Windows directory.

 Page 228, "Microsoft Windows User's Guide"
 The manual says that if "you don't specify an amount of memory,
 SMARTDrive will receive 256K (the default size)." This is true if
 you are using extended memory.  However, if you are using expanded
 memory for SMARTDrive, then the default is all of available expanded
 memory.

 ADDITIONAL FEATURES

 Minimize MS-DOS Executive
 -------------------------
 You can automatically reduce the MS-DOS Executive to an icon each time
 you run an application. This frees up memory and reduces screen clutter.

 1. Select the MS-DOS Executive File Run command. A dialog box is
    displayed.

 2. Turn on the check box option called Minimize MS-DOS Executive; then
    choose the OK button or press the ENTER key.

 Once you turn on this option, it remains in effect until you turn it off.

 Spooler will Terminate After Printing is Completed
 --------------------------------------------------
 A feature has been added to the Spooler. If the Spooler is running as
 an icon, Spooler will terminate (and free up the memory it was using)
 when printing is finished. If Spooler is running as a window, it will
 not terminate automatically.

 NOTES ON EARLIER VERSIONS OF WINDOWS

 Following are notes of interest if you have used earlier versions of
 Windows.

 Using Applications Designed for Earlier Windows Versions
 --------------------------------------------------------
 Windows applications, such as Aldus Pagemaker 1.0A, often include versions
 of the Windows files SPOOLER.EXE, CONTROL.EXE, and CLIPBOARD.EXE as well
 as instructions to use these file versions rather than the ones supplied
 with Windows. You should NOT use these file versions with Windows 2.0.

 Instead use the Windows 2.0 versions of SPOOLER.EXE, CONTROL.EXE and
 CLIPBOARD.EXE. This contradicts the instructions included with your
 application; however, these instructions apply to using the application
 with earlier versions of Windows, not to using the application with
 Windows 2.0.

 New Font File Format
 --------------------
 Some applications designed for use with Windows include font files (files
 with a .FON extension). For example, Aldus Pagemaker 1.0A includes several
 font files. Font files designed for use with earlier versions of Windows
 will not work correctly with Windows 2.0. Included with Windows 2.0 is a
 program, NEWFON.EXE, that converts your old font files to the new Windows
 2.0 format. Here's how to create the new font files:

 1. Change directories to the disk drive and directory in which the old
    font file(s) is located.

 2. Create a backup copy of the old font file(s). You do this by typing
    the following command:

         COPY *.FON *.BAK

 3. Place the "Utilities Disk" in drive A:

 4. Start the Newfon program from the A: disk drive. The form of the
    Newfon command is the following:

         A:NEWFON [old-font-filename]

    For example, to create a new PageMaker font file, PMFONTE.FON, you
    would type:

         A:NEWFON PMFONTE.FON

 NOTE:   You may also specify a new font file name. The form of the Newfon
         command for doing this is the following:

         A:NEWFON [old-font-filename] [new-font-filename]

 WARNING: You will lose the old font file if you do not make a backup copy.
          Be certain to backup your old font files.

 Using Windows 2.0 Paint Files with Applications Designed for Windows 1.0
 ------------------------------------------------------------------------
 The Windows 2.0 Paint file format differs from that of earlier versions
 of Paint. Thus applications designed to read Windows 1.0 Paint files
 can no longer do so. To work around this problem, either use Copy and
 Paste to move images from Windows 2.0 Paint into those applications
 designed to read Windows 1.0 Paint files, or use CVTPAINT.EXE to convert
 2.0 Paint files into 1.0 Paint files.

 Paint version 2.0 can read Paint files having the version 1.0 format.

 CHANGING SETTINGS IN THE CONFIG.SYS FILE

 The following notes relate to changing command lines in the CONFIG.SYS
 file.

 Using SMARTDrive
 ----------------
 Appendix C, "Speeding Up Windows with SMARTDrive" in the Windows User's
 Guide contains detailed instructions on installing the disk-caching
 program SMARTDrive provided with Windows 2.0. The following is updated
 information on adding SMARTDRV.SYS to your CONFIG.SYS file:

 - Set the buffers= command line to 10. Setting buffers= to higher values
   uses memory without additional performance benefits; setting it to a
   lower value reduces performance benefits.

 - Put the "device=smartdrv.sys" command line after the "device=" command
   line for any expanded memory manager you may be using (i.e. emm.sys,
   remm.sys, ps2emm.sys, etc.).

 - If you have a Compaq Deskpro and ENHDISK.SYS is listed in your
   CONFIG.SYS file, and you wish to use SMARTDrive, you must list the
   SMARTDRV.SYS command line after ENHDISK.SYS.

 - If you intend to use expanded memory for SMARTDrive, be sure to
   use the /A flag.

 Using RAMDrive
 --------------
 In general, SMARTDrive gives better overall performance than RAMDrive.
 One exception is output performance for frequently written files.  We
 recommend that you use SMARTDrive; however, a new version of RAMDrive
 is included with Windows 2.0.  This version performs significantly
 better than older versions and is the ONLY version that works with
 Windows 2.0.

 For detailed documentation on RAMDrive, see RAMDRIVE.TXT located on
 your Utilities Disk.

 Using EGA.SYS with an EGA Display Adapter
 -----------------------------------------
 If your display adapter is an EGA (Enhanced Graphics Adapter), you can
 improve the quality of standard applications by using the device driver
 file EGA.SYS included on the Utilities Disk. Here's what to do:

 1. Copy EGA.SYS from the Utilities Disk to your hard disk.

 2. Add a line of the following form to your CONFIG.SYS file:

         device=[drive:][path]EGA.SYS

 If you have a mouse installed on your computer, be sure to add the
 EGA.SYS line before the MOUSE.SYS line.

 Adding the EGA.SYS device driver is optional; it will enhance the
 performance of standard applications, but it is not required.

 NOTES ON USING EXPANDED MEMORY

 Windows 2.0 can utilize any expanded memory board which implements the
 LIM 4.0 expanded memory specification in its memory management software.
 To determine if your expanded memory board implements LIM 4.0, see your
 board's documentation or contact the manufacturer.

 Windows 2.0 contains software supporting the AST RAMpage, the IBM
 Personal System/2 Expanded Memory Option, and the Intel Above Board.
 This software is located in three files, REMM.SYS, PS2EMM.SYS, and
 EMM.SYS located on the Utilities Disk.

 The software for the AST RAMpage is named REMM.SYS.  For detailed
 instructions on how to install REMM.SYS, see the file REMM.TXT on
 the Utilities Disk.

 The software for the IBM Personal System/2 Expanded Memory Option
 is named  PS2EMM.SYS.  For detailed instructions on how to install
 PS2EMM.SYS, see the file PS2EMM.TXT on the Utilities Disk.

 The software for the INTEL Above Board is named EMM.SYS. For detailed
 instructions on how to install EMM.SYS, see the file EMM.TXT on the
 Utilities Disk.

 The performance of expanded memory boards is enhanced by disabling some
 of your computer's main memory, and replacing that memory with some of
 your expanded memory board's memory. If your computer supports the
 disabling of main memory, and your expanded memory board supports the
 use of its memory as main memory, then you should disable your computers
 main memory down to 256K, and use your expanded memory board's memory
 to fill main memory back up to 640K.

 ADDITIONAL INFORMATION

 Slow Response When Running DOS Apps on the IBM PS/2 Model 50, 60, & 80
 ----------------------------------------------------------------------
 If you have a Microsoft Mouse installed on your IBM PS/2 Model 50,
 60, or 80, keyboard and mouse response may seem slow when switching
 from Windows to a standard DOS application. When you switch to a DOS
 application, Windows re-enables your DOS mouse driver. On the IBM PS/2
 Model 50, 60, & 80, this process causes some delay. Thus, the keyboard
 and mouse may seem unresponsive during the switch.

 Disk Space Needed to Run DOS Applications
 -----------------------------------------
 Windows will not allow you to execute a DOS application if ample disk
 space is not available. To calculate the needed disk space, add 128K
 to twice the amount of memory requested by the application's PIF file.

 Running Windows 2.0 on Floppy Disk Drives
 -----------------------------------------
 Windows 2.0 will run on three different floppy drive configurations:
 1 high density (1.2 Megabyte or greater) floppy drive; two 720K,
 3 1/2" diskette drives; or two 360K, 5 1/4" floppy drives.

 On the two 360K floppy configuration, some font files and the Spooler
 are not set up so that disk space will be available for a printer
 driver. You can use a different font by using the Control Panel to
 delete the present font, and install a new one. Using Spooler with
 a floppy drive setup is not recommended. Likewise, running DOS
 applications from Windows on a floppy drive system is not recommended.

 When using a two floppy system, be careful to start windows with the
 Startup Disk in drive A: and the System Disk in drive B:. Swapping
 these disks will cause Windows to malfunction. Be particularly careful
 on machines having both a 5 1/4" drive and a 3 1/2" drive.

 THINGS TO AVOID

 Deleting Fonts When More Than 40 Fonts Are Installed
 ----------------------------------------------------
 If you want to delete a font when more than 40 fonts are installed, you
 should use Notepad to modify the WIN.INI file rather than Control Panel.
 Control Panel can only handle 40 fonts. In WIN.INI, locate the [fonts]
 header and delete the entire line for the fonts you wish to delete.

 Using the DOS JOIN Command with Windows 2.0
 -------------------------------------------
 >From Windows, using the DOS command, JOIN, to join floppy drives on
 a single drive system may cause your system to halt.

 Using APPEND During Setup
 -------------------------
 Do not use the DOS command, APPEND, before running the Windows Setup
 process.  Doing so may confuse Setup and cause it to fail.

 Using TSR (Terminate and Stay Resident) Applications with Windows
 -----------------------------------------------------------------
 Most TSR applications, such as Borland's SideKick, do not function
 properly under Windows, especially if they modify the screen. We do
 not recommend using TSR applications with Windows. If you choose to
 risk using a TSR, be sure to start it BEFORE starting Windows.

 Running with Low Memory
 -----------------------
 As you run out of memory, Windows' graphics will deteriorate, e.g.
 icons become black, menu bars do not display.  Also, the clipboard
 may malfunction, and Windows may give "Not enough memory" messages.
 In this situation, close an unused application to enhance performance.

 Using Display Drivers Designed for Earlier Windows Versions
 -----------------------------------------------------------
 Most display drivers designed for earlier Windows versions will not
 work with Windows 2.0. If a driver for your display is not included,
 then contact your display's manufacturer for an updated driver.

 If no driver is available, try installing your old one.  Install your
 old driver by choosing "Other" when Setup asks you to choose a display
 adapter. After Setup, replace files in your Windows directory having a
 .FON extension with the corresponding files from your Windows 1.0
 Fonts Disk. For example, replace HELVA.FON with the HELVA.FON file on
 your Windows 1.0 Font Disk.

 Setting Up Windows 2.0 Over Earlier Versions of Windows
 -------------------------------------------------------
 We do not recommend setting up over an earlier version of Windows.
 Instead, copy files you wish to keep to a temporary directory (be
 sure to copy modified PIF files), delete the old version of Windows,
 Setup Windows 2.0, and then copy the temporary files into the new
 Windows set up.

 If you do set up over an earlier version of Windows, the WIN.INI file
 will be renamed WIN.OLD. You may wish to rename this file to WIN.INI
 if you had important information in your old WIN.INI file. Optionally,
 you may want to use Notepad to copy information from WIN.OLD into your
 new WIN.INI file.

 Having Multiple Versions of Windows on the Same Fixed Disk
 ----------------------------------------------------------
 Windows 2.0 and other versions of Windows have many filenames in
 common. If you have multiple versions of Windows on your computer,
 avoid problems by including the path to only one version in your
 PATH environment variable.

 Problems Using Special Format
 -----------------------------
 On some DOS configurations, you cannot use the Special Format command
 from the MS-DOS Executive.  If you experience this problem, you can
 still format a diskette by running DOS FORMAT with the File Run command
 from the MS-DOS Executive.

 Using EGA.SYS with Compaq EGA Cards Running Under DOS 3.0
 ---------------------------------------------------------
 EGA.SYS does not function properly with a Compaq EGA card running
 under IBM DOS 3.0. If you experience this problem, do not use EGA.SYS
 or upgrade your DOS.

 Running Certain MS-DOS 3.1 Commands
 -----------------------------------
 Due to the way some MS-DOS version 3.1 commands are implemented, they
 will not work when run from Windows 2.0. If you must run these commands
 from Windows, upgrade your MS-DOS.

 Running BASICA Version 3.2 in a Window
 --------------------------------------
 BASICA version 3.2 will not run in a window because it directly modifies
 the keyboard.  It will run full screen if you change the PIF file such
 that "Directly Modifies Keyboard" is checked.

 ADDITIONAL PRINTER INFORMATION

 For more information on printers, see these files located on the
 Utilities Disk:

         Epson LQ                        READMEEP.TXT
         Hewlett Packard Plotters        READMEPL.TXT
         IBM Proprinters                 READMEPR.TXT
         PCL / HP LaserJet               READMEHP.TXT
         Postscript / LaserWriter        READMEPS.TXT

 ===========================================================================
                                END OF README.TXT
 ===========================================================================


 307. New -e Switch for the RC Compiler and EMS

 Question:

 What does -e on the RC line do specifically?

 Response:

 The -e switch was designed as a way for smart printer drivers to get
 around an efficiency routine that we put in. By default, all libraries
 that call GlobalAlloc() get memory from above the EMS line EXCEPT
 libraries that are loaded through LoadLibrary() (the majority of
 libraries loaded this way are printer drivers). Libraries that are
 loaded by LoadLibrary() get non-banked memory by default. The -e
 switch changes the default situation for these LoadLibrary()-loaded
 modules so that by default they get memory from above the EMS line.

 Please note the following examples:

 1. EPSON.DRV does a simple GlobalAlloc(). It was compiled without the
    -e switch. It is loaded via LoadLibrary(). Therefore GlobalAlloc()
    returns memory from below the line.

 2. HPPCL.DRV was compiled with the -e switch. When it does a
    GlobalAlloc(), it gets memory from above the line even though it
    was loaded through LoadLibrary(). It can get below-the-line memory
    with the GMEM_NOT_BANKED switch.

 3. WIN87EM.EXE is not loaded through LoadLibrary(). Instead, it is
    loaded whenever a task requests it. Therefore if it does a normal
    GlobalAlloc() it gets above-the-line memory.



 308. Determining the Visibility of a Menu

 Question:
    My application receives messages (DDE variety) that modify the menu
 shown on my application's menu bar. The message might change a
 checkmark, delete several options, append several options, etc. The
 problem is that this message might arrive while the user is viewing
 the menu and has a pop-up menu shown. When this occurs and I then
 modify that menu, strange things occur. I would like to have a way of
 knowing that the user is viewing a pop-up menu on my application and a
 way of knowing when he is finished. Is there an easy clean way of
 knowing this?

 Response:
    Unfortunately, no message is sent to the parent window to indicate
 that a pop-up menu has disappeared.  However, using subclassing and
 screening for the WM_SHOWWINDOW message will provide all the needed
 information.


 309. Fatal Exit 0x00FF

 Question:
    What can cause a fatal exit 0x00FF?

 Response:
    This fatal exit code can mean one of several things, and is usually
 associated with a bad handle.
    Some possibilities are as follows:

    1. GetProcAddress() is not valid for a task.
    2. MakeProcInstance() is not valid for a task.
    3. There is a segment load error.
    4. There is a module load error.
    5. An automatic data segment is greater than 64K.


 310. Determining the Available RGB Values of an Output Device

 Problem:
    Is there any way to determine what colors (RGB values) are
 supported for a device? Using the function GetDeviceCaps with the
 NUMCOLORS parameter, I can determine how many colors are supported,
 but the actual RGB values are not given. The function GetNearestColor
 will tell me whether a certain RGB value is available from the device,
 but I need something to enumerate through the supported colors.

 Response:
    There is an Escape function documented in Appendix C of the
 programmer's reference guide which can be used for both printer and
 display devices. GETCOLORTABLE will return an RGB color, given an
 index into the device driver's color table. The number of colors
 should be determined using the NUMCOLORS parameter to GetDeviceCaps().
 The following code fragment illustrates how to enumerate all of the
 RGB color values in the device driver table:

     int index, numcolors;
     long color;

     numcolors = GetDeviceCaps(ps.hdc, NUMCOLORS);
     hDC = GetDC( hWnd );    /* Change if using printer DC */
     for (index=0; index<numcolors; index++) {
        Escape( hDC, GETCOLORTABLE, NULL, (LPSTR)&index, LPSTR)&color );
        ...
        /* The value in the variable, color, holds an RGB value */
     }
     ReleaseDC( hWnd, hDC ); /* Change if using printer DC */


 311. WHATSIZE Sample Program from "Microsoft Systems Journal"

    The following is a summary of the WSZ program found in the
 "Microsoft Systems Journal" and in the Software Library:

    Issue: December 1986 Vol. 1 No. 2 Page 13
    Author: Charles Petzold
    Filename: WSZ

    This file can be found in the Software Library by searching for the
 filename WHATSIZE.ARC, the Q number for this article, or S10013.
    The Whatsize (WSZ) program demonstrates how to write and build a
 simple Windows application. The program itself is designed to show you
 the basics of writing a simple Windows application. The program
 displays only the size of the window's client area in various
 different units (pixels, inches and millimeters).
    Some of the Windows programming concepts used in WSZ are as
 follows:

    1. Setting up your environment to build a Windows application
    2. Drawing an icon with the icon editor
    3. Building a resource .RC file
    4. Initializing an application
    5. Creating a window procedure
    6. Adding an "About" box to the "System" menu
    7. Creating menus and dialog boxes
    8. Listing of the steps to build a Windows application



 312. Windows SDK: Horizontal and Vertical Sizing of Child Windows

 Question:

 When using a child window with the WS_THICKFRAME style, is there any
 way to only allow vertical sizing and not horizontal sizing, or vice
 versa?

 Response:

 Your application can do this by processing the WM_GETMINMAXINFO
 message in the child window procedure, which changes the minimum and
 maximum tracking information before passing the message on to the
 default window procedure. The following is an example of how this can
 be accomplished:

    LPPOINT lpArray;
    RECT  rRect;
    POINT LogP;
    HDC   hDC;

    ...

    case WM_GETMINMAXINFO:

             hDC= GetDC(hWndParent);
             GetClientRect(hWndParent, (LPRECT)&rRect);
             LogP.x=rRect.right/2;
             LogP.y=rRect.bottom;
             lpArray=(LPOINT) lParam;
             LPtoDP(hDC,&LogP,1);
             lpArray[4].x=LogP.x;
             lpArray[3].x=LogP.x;
             ReleaseDC(hWndParent,hDC);
        }
     default:
          return(DefWindowProc(hWnd, identifier, wParam, lParam));
          break;

 The example above will stop the child from ever being sized
 horizontally smaller or larger than half of the parent's client area;
 it will still allow normal vertical sizing.

 This example could have also limited vertical sizing by creating a
 different element of lpArray.

 Please note that there is no break statement before the default
 processing. This allows the WM_GETMINMAXINFO to pass to the default
 window procedure when your window procedure is done modifying the
 array.

 For more information on the array of POINTs pointed to by lParam,
 please see WM_GETMINMAXINFO in the "Microsoft Windows Software
 Development Kit Programmer's Reference" for Version 2.03 or 2.1.


 313. GetWindow() Function Returns a NULL Value

    The GetWindow function returns a NULL value when it reaches the end
 of the Window Manager's list. However, the "Microsoft Windows Software
 Development Kit Programmer's Reference" guide incorrectly states on
 Pages 316-317 that the return value indentifies a window.
    The following piece of code can be used to determine the number of
 siblings of the window represented by the hWnd variable:

      HWND hWnd, hWndNext;
      short nCount;

      hWndNext = GetWindow(hWnd,GW_HWNDFIRST);

      nCount = 0;
      while (hWndNext != NULL)
       {
        nCount++;
        hWndNext = GetWindow(hWndNext,GW_HWNDNEXT);
       }


 314. What Is the Return Value from ChangeClipboardChain?

 Question:
    When I check the return value for ChangeClipboardChain, sometimes
 it is TRUE and other times it is FALSE. However, the links are still
 made correctly. What is happening?

 Response:
    The return value for ChangeClipboardChain is TRUE if hWnd (the one
 to be removed) is the last one on the chain. Otherwise, the value is
 the return value (by way of Sendmessage) from the final item in the
 Clipboard chain, which should be TRUE. The change can successfully be
 made, however, even if the final window in the chain returns FALSE.
 This merely indicates that the application with the final window in
 the chain is handling the message incorrectly.


 315. PlayMetafile Slower Than EnumMetaFile/PlayMetaFileRecord

 Question:
    Printing through PlayMetaFile is slower than playing it using
 EnumMetaFile/PlayMetaFileRecord. One would intuitively expect things
 to be the other way around, yet the improvement in performance is
 dramatic. On a test metafile that I have been using, it takes 13
 58/100 seconds versus 8 78/100 seconds, respectively. This was run
 without the spooler.

 Response:
    PlayMetaFile is slower because it performs a great deal more than
 simply playing the metafile. The Enum/PlayRecord alternative will
 always be faster since it is effectively a stripped down version of
 PlayMetaFile.  Below is a list of some of the things that PlayMetaFile
 must do in addition to simply playing the records of the metafile.

    1. Verify that the handle passed is to a valid metafile
    2. Lock the metafile
    3. Save all DC objects (pen, brush, region, etc.)
    4. Play the metafile records
    5. Restore all DC objects, restoring default objects if restoration
 fails
    6. Unlock the metafile; delete all objects created while playing


 316. PlayMetafile Return Value Incorrect

 Question:
    The PlayMetaFile intrinsic does not report failures. If a metafile
 that contains a number of GDI calls is played and one of the calls
 fails, PlayMetaFile still returns TRUE.

 Response:
    This bug is produced any time you are not playing the metafile to a
 printer, or any time you pass in an invalid metafile handle. What the
 return value currently indicates is whether or not the playing of the
 metafile was halted by a call to an abort procedure. This will be
 corrected in a future release of Windows.


 317. Scope of Window Classes

 Question:

 Please explain the scope of classes with regard to RegisterClass() and
 CreateWindow().

 Response:

 When you register a class, USER checks to see if your module (i.e.,
 ___.exe) has registered that class before. If so, it fails. When you
 do a CreateWindow(), USER first looks in its list of classes for
 (lpClass, hModule). If the class is there, USER uses it. If not, it
 looks for that class by any module. If the class is there, USER uses
 it. If not, it fails.

 This implies that a module can see any other module's class if and
 only if it hasn't registered the class itself. If it has registered a
 class that others have also registered, it will see its own version.
 If it hasn't registered a given class, but more than one other module
 has, the result is unclear.

 An application can reference a class defined by a DLL, but should not
 reference classes registered by other applications. The reason for
 this restriction is that under Version 2.00 EMM, the WindowProc
 registered with that class may not be in available memory while your
 application is running.


 318. Be Sure to Reset Handle to Modeless Dialog Boxes

 Some customers are generating a "Local Heap Busy" Fatal Exit after
 opening and closing a modeless dialog box a number of times.

 When you close a modeless dialog box, you need to set the global
 handle back to NULL (zero). Otherwise, other parts of your application
 may attempt to send messages to the dialog box. In addition, the
 IsDialogMessage() call, which is usually in the GetMessage() loop,
 will be called if the handle is non-NULL. The need to set the global
 handle to NULL upon destroying the dialog box is clearly stated on
 Page 481 of Charles Petzold's book, "Programming Windows."



 319. Determining Escape Support for a Device

 Question:
    How can I find out if a particular device supports a particular
 escape?

 Response:
    To find out if a device supports a particular escape, use Escape()
 with QUERYESCSUPPORT. The following is an example:

    Escape(hDC, QUERYESCSUPPORT, 2, (LPSTR)&nEscape, (LPSTR)NULL);

    The nEscape is an integer that references the escape in question.


 320. Slow Boot Version of Windows

 Question:

 What are the files that are necessary to create a slow boot version of
 Windows?

 Response:

 The following is a list of the files necessary for you to do a "slow
 boot." The files without comments need no changes.

 WIN200   OVL    /* this is just a dummy file */
 DISPLAY  DRV    /* this is a rename of the driver you need, i.e.,
                 /* EGAHIRES.DRV */
 SYSTEM   DRV
 COMM     DRV
 SOUND    DRV
 MOUSE    DRV    /* rename NOMOUSE.DRV if you do not have a mouse. */
 KEYBOARD DRV    /* this is a rename of KBDUS.DRV */
 WINOLDAP MOD
 WINOLDAP GRB    /* this is a rename of the driver you need */
 WIN      COM    /* this is a rename of the DEBUG version of KERNEL.EXE */
 DISPLAY  LGO    /* this is a rename of the driver you need EGA.LGO */
 GDI      EXE    /* you need the DEBUG version of this */
 KERNEL   EXE    /* you need the DEBUG version of this */
 USER     EXE    /* you need the DEBUG version of this */
 MSDOS    EXE
 MSDOSD   EXE
 WIN      INI
 GDI      SYM    /* this is for debugging purposes */
 KERNEL   SYM    /* this is for debugging purposes */
 USER     SYM    /* this is for debugging purposes */
 FONTS    FON    /* this is a rename of HIFONTS.FON for EGA */

 --------------- /* this is the only change from 1.0x to 2.0x */

 OEMFONTS FON    /* this is a rename of FONTSHIUS.FON for EGA */

 ---------------/* to create 386 version change the following */

 Rename WIN.COM to WIN86.COM

 If you have any problems, start up the debugging version of KERNEL.EXE
 under SYMDEB, as follows:

    c:symdeb kernel.exe

 Then type "g" at the minus sign prompt. If one of the above files is
 missing, the KERNEL will tell you which one.


 321. When Can a Call to GlobalLock Fail?

    The following can cause a call to GlobalLock to fail:

    1. Attempting to Lock an invalid handle.
    2. Attempting to Lock an object that has been discarded. (This
 will occur if the object was allocated as discardable and the
 system has discarded it due to low memory.)
    3. When an application attempts to lock a DDE object that is in
 expanded memory under low memory conditions. (This act will cause
 GlobalLock to fail if there is not enough memory below 1 megabyte
 to allow Windows to make a copy of the object that the application
 wishes to lock. If this problem occurs, the program should free
 some of its Global memory and then try the GlobalLock again.)


 322. Documentation Error: Use Rcinclude to Include Resources

 The #includes statement appears to include #defines but not any RC
 commands. Page 27 of the "Microsoft Windows Software Development Kit
 Programming Tools" manual incorrectly states the following:

    If the #include directive is placed after the first definition
    statement, rc processes all statements in the include file.

 To include resources, you must use the rcinclude statement, as in the
 following example:

    rcinclude dlg.rc


 323. Maximum Baud Rate of Windows Communications Driver

    The Windows Version 2.03 communications driver can be set to 19200
 baud. The Windows device driver can support 19200 baud and will
 transfer the data at that rate. The driver will support requests for
 baud rates in the range of 2-19200.  However, the serial port hardware
 will only support a small subset of baud rates in this range.


 324. Determining the First Tab Stop Window

 Question:
    I am trying to determine the window handle of the first child
 window. This child window is a tab stop within a given parent window.
 Windows supplies the GetNextDlgTabItem() command, but this function
 will not tell the FIRST control it is a tab stop. I've tried putting a
 NULL in for the hCtl parameter, but it causes Windows to lock up.

 Response:
    For a dialog box, GetTopWindow() will return the first window in
 the TAB group. If you created the controls in your dialog box with a
 dialog box template in your rc file, this generally will be the first
 control listed in the dialog box template.


 325. Locking the Bitmaps Handle Directly

 Problem:
    Is there a way to lock down the bitmaps handle similar to the
 following:

    lpBitmapBits = LockBitmap(hBitmap);

    There should be such a function. If you are writing an application
 that displays many large bitmaps, having to create a separate
 intermediate buffer to hold bitmaps read from a disk file doubles the
 amount of memory needed for the application.

 Response:
    There is no such function, because the bitmap is the property of
 the display driver. Because Windows is device independent, it has no
 way of knowing if the bitmap is in conventional memory or in the host
 memory of the display card in question.


 326. Converting Colors between RGB and HLS (HBS)

    The code fragment below converts colors between RGB and HLS (HBS).
 It is based on Foley and Van Dam, "Fundamentals of Interactive
 Computer Graphics," Pages 618-19.

 /* Color Conversion Routines --

    RGBtoHLS() takes a DWORD RGB value, translates it to HLS, and
 stores the results in the global vars H, L, and S. HLStoRGB takes the
 current values of H, L, and S and returns the equivalent value in an
 RGB DWORD. The vars H, L, and S are only written to by:

    1. RGBtoHLS (initialization)
    2. The scroll bar handlers

    A point of reference for the algorithms is Foley and Van Dam,
 "Fundamentals of Interactive Computer Graphics," Pages 618-19. Their
 algorithm is in floating point. CHART implements a less general
 (hardwired ranges) integral algorithm.

    There are potential round-off errors throughout this sample.
 ((0.5 + x)/y) without floating point is phrased ((x + (y/2))/y),
 yielding a very small round-off error. This makes many of the
 following divisions look strange.
 */

 #define  HLSMAX   RANGE /* H,L, and S vary over 0-HLSMAX */
 #define  RGBMAX   255   /* R,G, and B vary over 0-RGBMAX */
                         /* HLSMAX BEST IF DIVISIBLE BY 6 */
                         /* RGBMAX, HLSMAX must each fit in a byte. */

 /* Hue is undefined if Saturation is 0 (grey-scale) */
 /* This value determines where the Hue scrollbar is */
 /* initially set for achromatic colors */
 #define UNDEFINED (HLSMAX*2/3)

 void  RGBtoHLS(lRGBColor)
 DWORD lRGBColor;
 {
    WORD R,G,B;          /* input RGB values */
    BYTE cMax,cMin;      /* max and min RGB values */
    WORD  Rdelta,Gdelta,Bdelta; /* intermediate value: % of spread from max */

    /* get R, G, and B out of DWORD */
    R = GetRValue(lRGBColor);
    G = GetGValue(lRGBColor);
    B = GetBValue(lRGBColor);

    /* calculate lightness */
    cMax = max( max(R,G), B);
    cMin = min( min(R,G), B);
    L = ( ((cMax+cMin)*HLSMAX) + RGBMAX )/(2*RGBMAX);

    if (cMax == cMin) {           /* r=g=b --> achromatic case */
       S = 0;                     /* saturation */
       H = UNDEFINED;             /* hue */
    }
    else {                        /* chromatic case */
       /* saturation */
       if (L <= (HLSMAX/2))
          S = ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin);
       else
          S = ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) )
             / (2*RGBMAX-cMax-cMin);

       /* hue */
       Rdelta = ( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
       Gdelta = ( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
       Bdelta = ( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);

       if (R == cMax)
          H = Bdelta - Gdelta;
       else if (G == cMax)
          H = (HLSMAX/3) + Rdelta - Bdelta;
       else /* B == cMax */
          H = ((2*HLSMAX)/3) + Gdelta - Rdelta;

       if (H < 0)
          H += HLSMAX;
       if (H > HLSMAX)
          H -= HLSMAX;
    }
 }

 /* utility routine for HLStoRGB */
 WORD HueToRGB(n1,n2,hue)
 WORD n1;
 WORD n2;
 WORD hue;
 {

    /* range check: note values passed add/subtract thirds of range */
    if (hue < 0)
       hue += HLSMAX;

    if (hue > HLSMAX)
       hue -= HLSMAX;

    /* return r,g, or b value from this tridrant */
    if (hue < (HLSMAX/6))
       return ( n1 + (((n2-n1)*hue+(HLSMAX/12))/(HLSMAX/6)) );
    if (hue < (HLSMAX/2))
       return ( n2 );
    if (hue < ((HLSMAX*2)/3))
       return ( n1 + (((n2-n1)*(((HLSMAX*2)/3)-hue)+(HLSMAX/12))/(HLSMAX/6)) )
    else
       return ( n1 );
 }

 DWORD HLStoRGB(hue,lum,sat)
 WORD hue;
 WORD lum;
 WORD sat;
 {
    WORD R,G,B;                /* RGB component values */
    WORD  Magic1,Magic2;       /* calculated magic numbers (really!) */

    if (sat == 0) {            /* achromatic case */
       R=G=B=(lum*RGBMAX)/HLSMAX;
       if (hue != UNDEFINED) {
          /* ERROR */
       }
    }
    else  {                    /* chromatic case */
       /* set up magic numbers */
       if (lum <= (HLSMAX/2))
          Magic2 = (lum*(HLSMAX + sat) + (HLSMAX/2))/HLSMAX;
       else
          Magic2 = lum + sat - ((lum*sat) + (HLSMAX/2))/HLSMAX;
       Magic1 = 2*lum-Magic2;

       /* get RGB, change units from HLSMAX to RGBMAX */
       R = (HueToRGB(Magic1,Magic2,hue+(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX
       G = (HueToRGB(Magic1,Magic2,hue)*RGBMAX + (HLSMAX/2)) / HLSMAX;
       B = (HueToRGB(Magic1,Magic2,hue-(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX
    }

    return(RGB(R,G,B));
 }


 327. Format for Strings and Longs in RCDATA in RC

 Question:
    What is the format for strings and longs in RCDATA in RC?

 Response:
    Numbers in the RCDATA section are formatted as documented, with the
 additional ability to specify long numbers with an "L" suffix.
    Strings are enclosed in double quotation marks ("), and are not
 null terminated.
    The following special mappings are performed:

     "" gives one double quote.
           """Hello "" there"""  ==> "Hello " there"
     "'" ==> '
     "\\" ==> \
     "\t" ==> \011 ==> ^I ==> TAB
     "\a" ==> \010 ==> ^H ==> BS
     "\x12" ==> HEX byte 0x12
     "\123" ==> OCTAL byte 0123 ==> HEX byte 0x53

     \" is not allowed.
     Single \'s before any other character are ignored.
     Nulls are not allowed anywhere in strings. Instead of using

         "A string.\0"

     use

         "A string.",
         0

    Longs and ints cannot be directly represented inside a string. They
 must be built up one byte at a time, or outside any strings.


 328. Bitmaps ICONEDIT RC LoadBitmap

 Question:
    What is the format of a bitmap file written by ICONEDIT?

 Response:
    ICONEDIT writes bitmap files for RC to read as follows:

 Byte 0:     2   /* OLDCURSOR=0, ICON=1, BMAP=2, CURSOR=3 */
                 /* Note that the printed update, and an older board
                    entry have these reversed. */
 Byte 1:     Device independent flag
                 /* BMR_DEVDEP=0, BMR_DEVINDEP=1. Some older bitmaps
                 may also have DISCARD_BIT=0x80 set in Byte 1. */
 Bytes 2-15: BITMAP structure
                 /* With bmType=0, bmPlanes=1, bmBitsPixel=1,
                 bmBits=0L. See the Programmer's Reference for
                 the other bm fields. */
 Bytes 16-??: BITMAP body
                 /* This will be WidthBytes * Height bytes of data.
                    Only the XOR bitmap is written. This image is as
                    edited, without any stretching. */

    If Byte 1 == BMR_DEVDEP, the BITMAP structure and the BITMAP body
 are repeated for the device-dependent version of the bitmap. In the
 current version, the device-dependent version is the same image
 StretchBlted according to the resolution set in the ICONEDIT NEW
 dialog. HiRes does no stretching.
    RC checks the size of the .BMP file to see if it contains one or
 two bitmaps. If there are two, it writes out only the second one.
    As RC writes the bitmap into the executable, it pads the end of the
 data out to the next 16-byte boundary. The padding always starts with
 "MT" and consists of some portion of the following string:

       imtswslnkmcjklsdlsbdmMICROSOFT

    As LoadBitmap read the resource, it checks for BMR_DEVDEP versus
 BMR_DEVINDEP. If it is BMR_DEVINDDEP, Microsoft will StretchBlt it to
 the device's resolution. However, Microsoft will not StretchBlt it if
 it is BMR_DEVDEP.
    To create and use a bitmap without having the system do any
 stretching, use ICONEDIT to write a device-dependent bitmap in HiRes.
    This information is based on source code as of April 12, 1988.


 329. Text Displays Faster When Aligned to 8-bit Screen Boundaries

 Problem:

 Text displays much faster in a maximized window than in a nonmaximized
 window.

 I try to improve the speed in a nonmaximized window by entering the
 code to align my TextOut x position on an 8-bit screen boundary. My
 code is successfully accepted; I can see the text being offset by
 various amounts from its window's left edge. However, I am not
 receiving any speed improvement when in a nonmaximized window.

 Response:

 Writing your text on 8-bit screen boundaries should make it display
 the text faster. If you are in transparent-background mode, the speed
 increase will be less noticeable because transparent mode requires the
 system to perform more work as it is displaying the characters.

 To switch from transparent to opaque-background mode, see the
 SetBkMode() call.


 330. Changing the Default Push Buttons

 Problem:
    I am having trouble changing the style of my default buttons. I
 have a dialog box with three buttons: OK, Save As, and Cancel. The
 default is Save As. What I want to do is make OK the default if I have
 already done a Save As. The following code does not seem to do this:

 SendDlgItemMessage(hDlg, ID_SAVEAS, BM_SETSTYLE, BS_PUSHBUTTON, 0L);
 SendDlgItemMessage(hDlg, IDOK,      BM_SETSTYLE, BS_DEFPUSHBUTTON,0L);

 Response:
    You must set the lParam to True in order for the BM_SETSTYLE
 message to cause the button to be redrawn. However, sending just
 BM_SETSTYLE messages with wParams of BS_PUSHBUTTON and
 BS_DEFPUSHBUTTON will only change the looks of the buttons. (Default
 push buttons have thicker borders.)
    To change the default push button, you must send BM_SETSTYLE
 messages to the buttons and also send a DM_SETDEFID message to the
 dialog box with wParam equal to the identifier of the new default push
 button.
    The following code will change the default push button from the
 control with identifier IDOK to the control with identifier NEW:

 SendMessage(hDlg,DM_SETDEFID,NEW,(long)NULL);
 SendDlgItemMessage(hDlg,IDOK,BM_SETSTYLE,BS_PUSHBUTTON,(long)TRUE);
 SendDlgItemMessage(hDlg,NEW,BM_SETSTYLE,BS_DEFPUSHBUTTON,(long)TRUE);


 331. Changing and Updating Icons

 Question:
    In the Windows Software Development Kit, the application icon can
 be either of two icons, depending on certain conditions. We would like
 the icon to change on the screen when the application is represented
 as an icon because the condition has changed.
    I change the application icon by using SetClassWord() and then try
 to display the new icon. I have tried using the following commands:

    1. FlashWindow()
    2. InvalidateRect() with bErase set to 0
    3. InvalidateRect() with bErase set to 1

    In all three cases, the new icon was drawn over the old one, but
 did not erase it. What windows call or series of calls do I need to
 make to erase the old icon so that the new one can be cleanly
 displayed?
    At the same time I am changing the icon, the application title also
 needs to change. If I click the painted-over icon, it cleanly changes
 to the new icon. If I can simulate a mouse click on the icon, this may
 cause it to repaint cleanly.

 Response:
    The reason that your commands are not currently working as you
 expect them to is that icon redrawing is done inside USER code, and is
 set to BitBlts with no concern for what is already there.
    What you are doing when you click the icon is drawing it as a
 cursor instead of an icon. In this case, you are redrawing it with
 concern for the background, so that you see it as you intend to.
    Instead of simulating a mouse click (which could cause focusing
 problems), you should do the following:

    1. Set the icon to Null.
    2. Call InvalidateRect. (You will now be sent messages to draw the
 icon yourself, at which point you can fill the rectangle with the
 background color.)
    3. Set the icon to the one you wish to have drawn.
    4. Call InvalidateRect again.


 332. Paint File Header Check Sum Calculation

 Question:
    The Microsoft Windows Paint file format header structure contains a
 wCheck field that contains a check sum. How should this be calculated?

 Response:
    The first 12 words in the header are xor'ed to calculate the check
 sum. The following is one way to calculate the wCheck field:

    WORD *pfilehdr;
    FILEHDR filehdr;

  /* Initialize to first word*/
   filehdr.wCheck = filehdr.key1;

   pfilehdr = &filehdr.key2;
   for (i=1; i < 12; i++)
       filehdr.wCheck ^= *pfilehdr++;


 333. Fatal Error Exit 409 "Segment Trashed"

 Question:
    I get a fatal exit 409 "Segment Trashed" on a segment of my DLL.
 When I look at the segment with the debugger, nothing has changed
 except the first instruction "MOV AX,####". Why is this an error?

 Response:
    There is a bug in the kernel. It occurs with segments declared in a
 DLL with "DATA SINGLE MOVEABLE" when there are no far calls from the
 segment and it is running with the DLLs DS unlocked while not in the
 DLL.
    The workaround is to call LockData(0) and UnlockData(0) on entry and
 exit from the routine.


 334. LB_GETTEXT Bug When wParam Is Negative

 Problem:
    Gibberish is written to pBuf, and NumBytes is set to the number of
 bytes of gibberish written to pBuf, when the following call is
 executed:

    NumBytes = SendMessage( hLB, LB_GETTEXT, (unsigned int) -1,
                                             (long) (LPSTR) pBuf );

    The documentation says NumBytes should be set to LB_ERR since
 there were no items in the listbox at the time this call was made.

 Response:
    A bug performs a signed comparison instead of an unsigned
 comparison. Using any negative number will cause the same problem.
    Microsoft is researching this problem and will post new information
 as it becomes available.


 335. Predefined Controls for DialogBoxIndirect

 Question:

 What are the codes for the control classes used with the
 DialogBoxIndirect and CreateDialogIndirect functions?

 Response:

 The following is a list of the predefined control-class codes:

 #define BUTTONCLASS    0x80 /* 128 */
 #define EDITCLASS      0x81 /* 129 */
 #define STATICCLASS    0x82 /* 130 */
 #define LISTBOXCLASS   0x83 /* 131 */
 #define SCROLLBARCLASS 0x84 /* 132 */

 For more information and sample code, please refer to the DIALOG.ARC
 file in the Software Library. This file can be found in the Software
 Library by searching for the filename, the Q number of this article,
 or S12026.


 336. GetParent Returns Unexpected Value for Pop-Up Windows

 Question:
    I have an application with a main window containing a child window
 named Child. I created a Dialog box using the handle to the window
 Child as the third parameter to DialogBox().
    This should make the window Child the parent of the Dialog box.
 However, when I call GetParent() using the handle to the Dialog box,
 the function returns the handle to the main window and not the handle
 to its true parent, the window Child.

 Response:
    GetParent returns the handle to the top level window when the
 window it is retrieving the parent of is a pop-up window (such as a
 Dialog box).
    This occurs so the system, when creating the Dialog box, may
 disable all the other windows above itself when it makes the Dialog
 box the active window.
    If you need to access the window that created the Dialog box, you
 will need to pass that handle to the Dialog box in an external
 variable.


 337. Servicing Hardware Interrupts

 Question:

 I need to manage custom communications hardware through an I/O
 interrupt. What is the recommended method for implementing a device
 driver for Windows?

 Response:

 You can implement a device driver that needs to service an I/O
 interrupt by doing the following:

 Create the function that will service your interrupt in a FIXED code
 segment of a dynamic link library (DLL), which by default will be
 labeled as NOTBANKED code, so that it is not affected by EMS.

 When this is done and your program is first loaded, your Windows
 program should use the SetTimer() function. The SetTimer() function
 will use the Elapsed parameter to set the amount of time you want to
 elapse before you call your routine again.

 The last parameter to SetTimer() points to your dynamic link library
 function that will be called when the timer goes off. The function
 should look at the interrupt, collect the data if there is any, and
 use PostMessage() to notify the application that the interrupt has
 occurred.


 338. Initiating DDE Conversation with Instance of Windows Excel

 Question:

 I am planning to use Excel to EXEC() another application. I want that
 application to be able to initiate a DDE conversation with that
 particular instance of Excel.

 The documentation for the EXEC() macro on Page 275 of the "Microsoft
 Excel Functions and Macros" manual refers to its return value as "the
 Microsoft Windows task ID number of the started program." However, it
 is not the value returned by the Windows KERNEL routine
 GetCurrentTask.

 How can I get the the correct value to start a DDE conversation with
 the instance of Excel that I need?

 Response:

 You are correct in stating that the documentation is incorrect. What
 EXEC does return is the instance handle to the EXEC'd application.

 Windows and Excel do not provide an easy way to find out which
 instance of Excel called your application. However, you can get the
 correct value.

 The following is a sample macro that uses the Register Call functions
 to obtain the Instance handle to the version of Excel that is running.
 You can then pass that information to your application inside the EXEC
 call. However, you will have to append the value in hInstance as a
 string for your EXEC call.

 The following is the text version of the macro sheet:

 _____________________________________________________________
              A                          |        B
 This is an example of how you can obtain|
 the Instance and then pass that value to|
 your EXEC'd application:
                                         |
 =STEP()                                 |
 Get instance handle of current window.  |
 =REGISTER("USER","GetFocus","H")"       |
 =CALL(A7)                               | <== hWnd
        -6                               | <==GWW_HINSTANCE
 =REGISTER("user","GetWindowWord","HHI")"| <==GetInstance
 =CALL(GetInstance,hWnd,GWW_HINSTANCE)"  | <== hInstance
 =RETURN()                               |
 Exec("YourApp.exe  hInstance",1)"       |
 _____________________________________________________________


 339. Drawing Different Icons for Each Instance of a Program

 Question:
    How can I have a program display a different icon for each instance
 of the program that is created?

 Response:
    You can use the DrawIcon function within your Paint procedure to
 draw different icons for different instances of your application.
    You need to do the following three things to your program to be
 able to draw your own icon for each instance of your application:

    1. Set the hIcon field of WNDCLASS to Null when registering the
 class. This will prevent Windows from drawing the icon for you when
 your application becomes an icon.
    The following is an example of the first instance initialization
 routine:

 int index;   /* global counter to keep track of which instance this is */

 void InitFirst (hInstance)
 HANDLE hInstance;
 {
     WNDCLASS   rClass;                     /* Window class structure.  */

     rClass.lpszClassName = "HELLO";
     rClass.hInstance     = hInstance;
     rClass.lpfnWndProc   = WindowProc;
     rClass.hCursor       = LoadCursor (NULL, IDC_ARROW);

     rClass.hIcon         = NULL;           /* NULL so that we can handle
                                               drawing the icon */

     rClass.lpszMenuName  = NULL;
     rClass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
     rClass.style         = CS_HREDRAW | CS_VREDRAW;
     rClass.cbClsExtra    = 0;
     rClass.cbWndExtra    = 0;

     RegisterClass( &rClass);    /*  Register the class. */

     index=1;  /* set global instance counter to 1 for first instance */

 }

    2. Load the proper icon for the current instance from the resource
 file during the initialization routine.
    In the initialization routine for additional instances, you will
 need to use GetInstanceData() to get the instance counter from the
 previous instance and then increment it by one so that it reflects the
 number of this instance. The following is an example of this:

 void InitAdded (hInstance, hPrevInstance)

 HANDLE hInstance, hPrevInstance;
 {
        GetInstanceData (hPrevInstance,
                         (PSTR) &index,
                         sizeof(int));
        index++;
 }

    In the initialization section for all instances, you should load the
 proper icon for the instance you are initializing.
    In this case, icon1, icon2, icon3, and icon4 are individual icons
 declared in the RC file for this application. This is shown in the
 following code:

 HICON hIcon;       /* Declare handle that will be used to hold the icon for
                       this instance */

 void InitEvery (hInstance, cmdShow)

 HANDLE hInstance;
 int    cmdShow;
 {
     HWND  hWnd;

     hInst = hInstance;              /*  So window procs can use it later   */

     switch (index)  /* which instance is it */
       {
         case 1:  hIcon=LoadIcon(hInstance, "icon1");
             break;

         case 2:  hIcon=LoadIcon(hInstance, "icon2");
             break;

         case 3:  hIcon=LoadIcon(hInstance, "icon3");
             break;

         default:
             hIcon=LoadIcon(hInstance, "icon4");
        }

      CreateWindow ( ... );
 }

    3. Draw the icon on the client area in response to a WM_PAINT
 message if the window has been represented by an icon.
    When you get a paint message, use the IsIconic() function to check
 if your application is being represented by an icon. If it is, draw
 the icon in the client area.
    The following is the paint procedure that will draw the icon:

 void HelloPaint( hDC )
 HDC hDC;
 {
   RECT ClientRect;
   short iconx, icony;

   GetClientRect( hWnd, (LPRECT)&ClientRect );
   if (IsIconic(hWnd)) {

     /* figure out where to draw icon within client area so that the icon
        is drawn in the center of the window's client area */

     iconx = (ClientRect.right - GetSystemMetrics(SM_CXICON)) / 2;
     icony = (ClientRect.bottom - GetSystemMetrics(SM_CYICON)) / 2;
     DrawIcon(hDC, iconx, icony, hIcon);
     return;
     }

 /* Normal paint procedure stuff goes here */

 }


 340. LB_SELECTSTRING Scrolls Listbox Regardless of REDRAW Flag

 Problem:
    I have a Windows listbox that contains the names of all existing
 items in a database. I do not want to have duplicate names when I
 create a new database, so I tried to compare the new item names with
 the current listbox entries by doing the following:

    1. Setting WM_SETREDRAW to FALSE to stop the listbox from updating
       as I do the checks.
    2. Using LB_GETCURSEL to remember what was selected at the start.
    3. Using LB_SELECTSTRING to find the name.
    4. Using LB_GETTEXT to get the matched string to do a full
       comparison.
    5. Using LB_SETCURSEL to restore the old selection.
    6. Setting WM_SETREDRAW to TRUE to allow the listbox to repaint.

    Everything works well until the LB_SELECTSTRING causes an item that
 is not currently visible in the window to be selected. WM_PAINT and
 WM_ERASEBKGND arrive, implying that the listbox is not taking complete
 notice of the WM_SETREDRAW message sent earlier.
    I thought listboxes would not do any painting while WM_SETREDRAW is
 set to FALSE.

 Response:
    It may seem strange that scrolling occurs while REDRAW is set to
 FALSE. This occurs because although REDRAW determines whether paint
 messages will be sent for invalidated regions, it does not limit
 Windows from moving a bitmap from one location to another.
    If your application had set the REDRAW flag to FALSE and you moved
 your application's window sideways 1 pixel, Windows would shift the
 bitmap over for you without requiring a repaint.
    To find a string in a listbox, you need to perform the following
 algorithm:

 short FindLBoxString(hDlg, lbidnum, string)
 HDLG hDlg;         /* Handle to the dialog box containing the listbox   */
 short lbidnum;     /* ID number of the listbox (used in SendDlgItemMsg) */
 LPSTR string;      /* string to be found                                */
 {
   short i, totalentries;
   char  buf[MAXCHARS];

   totalentries = (short)SendDlgItemMessage(hDlg, lbidnum,
                                               LB_GETCOUNT, NULL, (long)NULL);
   for (i = 0; i < totalentries; i++) {
     if (LB_ERR == SendDlgItemMessage(hDlg, lbidnum, LB_GETTEXT,
                                      i, (long)(LPSTR) buf)) {
         i = totalentries;
         break;
         }
     if (!AStrCmp((LPSTR) buf, (LPSTR) string))
       break;
     }
   if (i >= totalentries)
     i = -1;
   return(i);
 }

    Please note the following things about the code above:

    1. It includes a call to AStrCmp(), a function that performs string
        comparison, which you will need to supply. As the code is
        currently written, returning 0 indicates success (a match).
    2. The return value from LB_GETTEXT is not checked to see if there
       has been a buffer overflow. This should be done, unless MAXCHARS
       is known to exceed the text length of any possible listbox items
       (hence, its name). If this is of concern, a routine that
       performs a LocalAlloc of the longest size could be employed.


 341. LINK4 Uses .DLL Extension for Dynamic Link Libraries

 Problem:
    When I link a dynamic link library using the LINK4 program supplied
 with the Windows Version 2.03 Software Development Kit, the file it
 produces is named with an extension of .DLL. I have to rename it to
 .EXE before I can use the library.

 Response:
    The linker uses the .DLL extension for files created from .DEF
 files that contain the LIBRARY keyword.
    The Windows linker and the OS/2 linker are very close to being the
 same. In OS/2, dynamic link libraries use the extension of .DLL to
 avoid confusion with .EXE files.
    This change in the naming of dynamic link libraries has not been
 carried over to Windows, so you need to do one of the two following
 things:

    1. Rename the .DLL files produced by LINK4 to .EXE files.
    2. Explicitly set the output file name to .EXE in the LINK4 command
 line.

    You could put an extra line in your Make file to copy the *.DLL
 file to *.EXE.


 342. Location of Cursor in a List Box

 Problem:
    I need to use list boxes with multiple selections with my
 application, which is why I specified LBS_MULTIPLE. When LBS_MULTIPLE
 is specified, I need to determine which item in the list box has the
 cursor after an LBN_DBLCLK message.
    I know how to find the selected items using LB_GETSEL, but I do not
 know how to detect which of the selected items also has the cursor.

 Response:
    There is no way to determine which item in the list box has the
 cursor when the LBN_DBLCLK message is received. You must keep track of
 which item has the cursor as it moves between the items. When you
 receive the double-click message, you will know which box has the
 cursor.


 343. Message Box Not Regaining the Focus

 Question:
    When I bring up a Message box, a Dialog box appears. When the
 Dialog box is finished, the focus does not return on the Message box,
 even though it appears for the parent. Is there any way to return the
 focus to the Message box?

 Response:
    The problem lies with the Dialog box function using the hWndParent,
 which you are providing as the GetFocus function, and determining if
 it is the actual parent.
    If the Dialog box is not using hWndParent, it determines the
 top-level parent and executes a DisableWindow on the parent. When the
 dialog finishes, it restores the hWndParent, which is not the Message
 box you want.
    You can avoid this problem by doing the following:

    1. Before you call the Dialog box function, execute a GetFocus to
 save the handle to the window with the current focus.
    2. Set a flag so that when your Parent receives a WM_FOCUS message,
 it can check the flag to see if the Message box was up and execute a
 SetFocus back to the Message box.


 344. Creating a Resizeable List Box in Windows

 Problem:

 When I try to create a resizeable, movable list box in Windows Version
 2.00, the following problems occur:

 1. Incorrect dimensions

 2. Incorrect resizing

 3. Window will not move to a designated location

 Response:

 You can create the effect of a resizeable list box by creating a list
 box inside a child window with a thick frame. The file LISTSIZE.ARC in
 the Software Library is an example of doing so. This file can be found
 by searching on the filename, the Q number of this article, or S12039.
 Please note that this is a sample program; placing thick frames on
 list boxes is not supported by Microsoft. Microsoft does not claim
 this program is correct or complete.

 When the child window is resized (i.e., a WM_SIZE message is
 received), the list box can be moved to fit exactly within the child
 window. List boxes can be sized only to an integral number of
 system-font text heights. If you do not size the child window to an
 integral number of text heights, your child window will be larger than
 the list box.

 In the example, the maximum vertical height of the child window (and
 the list box) is limited by the number of items in the list box. The
 height of the child window also prevents the list box from being sized
 shorter than two list-box item heights. These limitations prevent any
 problems resulting from trying to size a window smaller than the
 minimum allowed by Windows.

 SetWindowPos() is used to resize the child window. MoveWindow() is
 used to move the list box within the child window. You can change the
 size of the window borders with the control panel while the
 application is running. To get the current window-frame height and
 caption height, GetTextMetrics() is automatically called every time a
 WM_SIZE message is received.

 Window dimensions are necessary because while the client area must be
 an integral number of system-font text heights, the height passed to
 SetWindowPos() is the entire window size, including the borders.


 345. Mapsym Causes Out-of-Memory Error

 Problem:

 When I run mapsym on a fairly large medium-model application, the
 following error message appears:

    "mapsym: out of memory"

 Response:

 You can experience the out-of-memory error in mapsym if you have
 modules that contain only data (no code) and there is no SEGMENTS
 section in your .DEF file.

 To correct this problem, add a line to your .DEF line similar to the
 following:

 SEGMENTS
     _TEXT   PRELOAD DISCARDABLE MOVEABLE

 Because you have multiple modules, it may be helpful to use the -NT
 switch when you compile them; this will allow you to give each segment
 a meaningful name other than _TEXT. Examine the .MAP file to determine
 the name the compiler has given the segments.

 If you still have trouble with mapsym running out of memory, remove
 the -Zd switch from the modules that you currently are not debugging.
 Doing so will reduce the number of symbols taking up space you are not
 using.

 If you do not need to debug at SYMDEB source-code level, remove the
 /LI option from the LINK command to decrease the size of your .MAP
 file.

 Finally, you may hand edit the .MAP file, removing entries for
 variables you do not intend to use. Use caution when doing this hand
 editing, as each entry is listed twice in the .MAP file. If it is
 removed in one location, it MUST be removed in the other location as
 well.


 346. ExtTextOut Underline/Strikeout Problem

 Question:
    I am having difficulty getting underlining to work when using
 ExtTextOut. I changed HelloPaint to set the MapMode to MM_LOENGLISH,
 set up a font with Courier 10 and underlining, then displayed Hello
 Windows! with ExtTextOut() and TextOut().
    In MM_TEXT mode, underlining works correctly with both TextOut()
 calls. In MM_LOENGLISH mode, underlining appears only for part of the
 line in the ExtTextOut() call; however, it appears for all of the line
 in the TextOut() call. If I change the character width array in
 ExtTextOut() to NULL, the underlining appears correctly.

 Response:
    This is a problem in the code. Whenever the lpDx parameter to
 ExtTextOut is not NULL, and the font has either strikeout or
 underline, the coordinates for the strike/underline are transformed
 twice in any mapping mode other than MM_TEXT.
    One workaround is to perform the transformations yourself and make
 the call in MM_TEXT mode. (Note that the double transformation is only
 for the strikeout or underline. It does not affect the text itself
 and only occurs if lpDx is not NULL.)
    Another workaround is to create the font without the underlining
 and perform the underlining yourself.


 347. Spooler Sometimes Fails to Close Itself

 The spooler sometimes fails to terminate itself when receiving an
 ABORTDOC escape from an application. This problem is due to the way
 GDI and the spooler interact to control print spooling.

 One of the features of Windows Versions 2.03 and 2.10 is that the
 spooler terminates after printing is completed. For the spooler to
 terminate itself, the following must be true:

 1. It must not have any current or pending print jobs.

 2. It must not be the active application.

 3. It must be running as an icon.

 The spooler performs these tests (to see if it should terminate
 itself) when the GDI spooling routines tell the spooler application to
 terminate or abort a print job (as with the ABORTDOC escape).

 There is one special case, however: if none of the document's pages
 have been sent to the spooler yet, the GDI spooling routines don't
 bother to tell the spooler to end or abort the print job. You can't
 tell the spooler to stop printing if it hasn't even started yet. This
 design works properly except for the situation in which the spooler is
 never sent messages that cause it to perform the termination test.

 This behavior is more of a design limitation than a bug.

 There is at least one workaround to this restriction. In your
 application, you could keep track of whether you've sent anything to
 the spooler yet (i.e., whether you have sent a NEWFRAME yet), and if
 the user aborts during the first page, send a NEWFRAME and then an
 ABORTDOC. This way, the spooler is told by GDI to start a print job
 and then stop the print job, which causes the spooler to check to see
 if it should remove itself.

 This workaround has the disadvantage of starting the printing of a
 page, which could waste some paper. However, it is preferable to an
 alternate workaround: sending a WM_CLOSE to the spooler. The problem
 with WM_CLOSE is that you can't be sure that there aren't other print
 jobs in the spooler's queue, so you shouldn't close the application.


 348. Dynamically Initializing Menus

 Question:
    I am writing the software so that it will work properly regardless
 of the order of the individual menu items. I also would like to make
 the software independent of the order of the pop-up items on the main
 menu.
    I have some menu items (within pop-up menus) that should be grayed.
 To do this, I waited until a WM_INITMENUPOPUP message was sent, then I
 grayed the pop-up menu items in that pop-up menu.
    Unfortunately, the WM_INITMENUPOPUP message only gave me the pop-up
 index. Because the writer on this project might reorder the pop-up
 index after I am finished with the software, I will not know at
 compile time which pop-up index corresponds to which pop-up menu. Is
 there a way around this problem?

 Response:
    There are many ways to handle this problem, all of which involve
 writing coding to decipher which menu items are at which locations.
    The simplest method of doing this involves using a table of
 predefined constants that define the menu order. Whenever your
 independent writer rearranges the menus, he or she must also reorder
 this array. If you inspect the array within your code, you will know
 where everything is because the menus are predefined constants.
    A method that does not involve input by your writer, but does
 involve greater coding on your part, is to use the following routines:

    GetMenuItemCount(hMenu);
    GetMenuItemID(hMenu, nPos);
    GetMenuString(hMenu, wIDItem, lpString, nMaxCount, wFlag);

 Note that wParam of the WM_INITMENUPOPUP is the handle of the menu
 being initialized.

    Using GetMenuItemID() instead of GetMenuString() is a more
 desirable route, especially if your writer decides to change the text
 of the menu item. It is also faster.
    However, you will need to give each menu item a distinct ID so that
 no menu item IDs match, regardless of the pop-up menu in which they
 are located. Such a routine might look like the following, where
 InitMenuItem() is a function you will write:

    case WM_INITMENUPOPUP:
      nCount = GetMenuItemCount(wParam);
      for (nItem = 0; nItem < nCount; nItem++)
        InitMenuItem(wParam, GetMenuItemID(wParam, nItem));
      break;

    You can alter the calls to EnableMenuItem() through a switch
 statement that selects the appropriate action based on the
 distinct menu ID.


 349. Moving Window Without Caption Bar

 Problem:
    My application can create a window with no caption bar; however, I
 would like to move the window by clicking it in one area and then
 dragging it to a new location. I tried posting a WM_SYSCOMMAND message
 with the SC_MOVE type when I clicked in the window to move it, but the
 window still would not move.

 Response:
    The WM_SYSCOMMAND with SC_MOVE type is the message used to move a
 window through the keyboard.
    To drag the window with the mouse, your application must post a
 WM_SYSCOMMAND with the constant 0xF012 as the type. This is the same
 message that is posted when you click in the caption bar and drag the
 window to move it.
    This value is safe and is being considered for inclusion in future
 documentation.


 350. WM_SETVISIBLE No Longer Sent when Window is Covered/Uncovered

 Problem:
    Page 592 of the "Microsoft Windows Software Development Kit
 Programmers Reference" manual states that the WM_SETVISIBLE message
 will be sent to your window immediately before it is made visible or
 hidden. This worked in Windows Version 1.x, but does not work in
 Windows Version 2.03.

 Response:
    This is a documentation error. The WM_SETVISIBLE message is no
 longer sent before it is made visible or hidden. In Windows Version
 2.03, no messages are sent to a window as it is being covered or
 uncovered.


 351. Call Back Functions and Expanded Memory

 Question:
    How can I ensure that my notification function used with the
 GlobalNotify function will not be banked into EMS memory when it is
 called?

 Response:
    All call-back functions that can be called when your application is
 not banked in need to be placed in a FIXED code segment of a dynamic
 link library. This procedure is the only way you can guarantee that
 the application will not be banked out when it is called.
    This includes the hook functions used with SetWindowsHook, the
 notification functions used with GlobalNotify, and the timer functions
 used with SetTimer.


 352. Creating Fonts for Landscape Devices

 Question:
    How can I create a font for a Landscape device? How can a program
 detect if the printer is in Landscape mode?

 Response:
    To define a font as Landscape, set the following fields in the font
 file to the appropriate values for your device when it is in Landscape
 mode.
    These fields are set when you create the font using the font edit
 program.
    The following is a list of fields:

    1. dfHorizRes and dfVertRes: These fields need to correspond to the
 aspect ratio and resolution of your device in Landscape mode.
    2. dfPoints: This field should specify the font's physical height
 when it is printed. The field will specify the font in 72nds of an
 inch.
    3. dfPixHeight: This field will specify the height in pixels of the
 font.

    You should be able to print items in Landscape mode with no
 problems if you set these fields as shown above and use the control
 panel to set the printer into Landscape mode.
    Your program can detect that the device is in landscape mode by calling
 the following:

    GetDeviceCaps(hDC, HORZSIZE) and GetDeviceCaps(hDC, VERTSIZE)

    If the HORZSIZE value is greater than the VERTSIZE value, the
 device is in Landscape mode.



 353. Focus Bug when DialogBox Fails

 Problem:
    When putting up a dialog box, Windows notifies the parent window of
 the box that it has lost focus by sending it a WM_KILLFOCUS message.
    When the box appears normally, Windows then sends the parent window
 a WM_SETFOCUS message.
    However, when the DialogBox function call fails, Windows sends a
 WM_KILLFOCUS, but not a WM_SETFOCUS message. The parent window never
 seems to lose the focus, even though it has been notified that
 it has lost the focus.
    To duplicate this problem, allocate all of the memory immediately
 before the call to the DialogBox. You also can try passing in an
 invalid parameter to the DialogBox function (e.g., the lpTemplateName)
 and trace the messages that comes through the parent window procedure.

 Response:
    This is a problem in the code of DialogBox() and
 DialogBoxIndirect(). To work around this problem, do the following:

    1. Call GetFocus() prior to calling DialogBox().
    2. Call SetFocus() if the call to DialogBox() fails.

    The following code calls SetFocus more often than is necessary:

   hWnd = GetFocus();
   retval = DialogBox(hInst, (LPSTR) "Name", hDlgParent, lpfnDlgProc);
   if (!retval)
     SetFocus(hWnd);

    However, it is smaller than the following code, which calls
 SetFocus() only when necessary:

   hWnd = GetFocus();
   hWnd2 = hDlgParent;
   while (GetWindowLong(hWnd2, GWL_STYLE) & WS_CHILD)
     hWnd2 = GetParent(hWnd2);
   bResetFocusIfFail = (hWnd == hWnd2) ? TRUE : FALSE;
   retval = DialogBox(hInst, (LPSTR) "Name", hDlgParent, lpfnDlgProc);
   if (!retval)
     if (bResetFocusIfFail)
       SetFocus(hWnd);



 354. Reprinted Article on Windows 2.03's Use of Expanded Memory


 The following article was reprinted from the January 1988 issue of the
 "Microsoft Systems Journal." It was written by Paul Yao, coauthor of
 the "Programmer's Guide to Windows." The article discusses the
 Lotus/Intel/Microsoft (LIM) Expanded Memory Specifications (EMS)
 support of Windows Version 2.03. The original LIM/EMS article
 contained several detailed illustrations that could not be reproduced
 below. For more information on these diagrams and the author, please
 refer to the "Microsoft Systems Journal."

 In 1981, IBM introduced its Personal Computer, which, among other
 things, gave application developers more memory to work with. You may
 recall that the best selling machine in those days was the Apple II,
 which, with its 6502 processor, was able to address 64K of RAM memory.
 The IBM Personal Computer, with its Intel 8086 processor, was able to
 address a full megabyte of memory. Once the memory reserved for the
 hardware was subtracted, the program address space of 640K was ten
 times the available memory on the Apple computer. All at once
 programmers had more memory than they knew what to do with.

 In the years that followed, three things caused users to push the
 limits of 640K of memory: software became larger as new features were
 added, spreadsheets and documents grew as users became more
 sophisticated, and with advances in chip technology, the price of
 memory dropped. What once had seemed an enormous amount of memory
 suddenly was not enough. Microcomputer users again needed more system
 memory.

 To meet this need, the expanded memory specification (also called EMS)
 was introduced in 1984 to give memory above the 640K memory limit to
 applications. EMS defines the software interface for a program to
 access expanded memory. Using EMS, an application can store up to
 eight additional megabyte of data in RAM memory. A superset of EMS,
 called the enhanced expanded memory manager, or EEMS, was developed by
 AST. EEMS provided even greater flexibility in the way that expanded
 memory was mapped for use by applications.

 In August of this year, the EMS and the EEMS specifications were
 superceded by the Lotus/Intel/Microsoft expanded memory specification
 version 4.0 (sometimes called LIM/EMS 4.0). EMS 4.0 provides up to 32
 megabytes of expanded memory, four times the 8 megabytes supported
 under EMS 3.2. By consolidating two similar, but distinct, memory
 specifications, LIM/EMS 4.0 simplifies expanded memory support for
 Windows. This is because any EMS or EEMS card can be made compatible
 with EMS 4.0 by simply adding a device driver. In addition to unifying
 the expanded memory interface, EMS 4.0 provides multitasking support
 for expanded memory.

 This article will discuss how expanded memory works, how expanded
 memory is used by Windows 2.0, how virtual memory is implemented in
 Windows 386, and how expanded memory support will affect your Windows
 programming.

 Expanded Memory

 Expanded memory lets application programs access more than 640
 kilobytes of memory. EMS 4.0 performs this feat by setting aside a
 portion of the 8088's 1 megabyte address space as a porthole into
 expanded memory. This porthole is called a page frame. Depending on
 how system memory is used, page frames range in size from 16K to
 1024K.

 Page frames are divided into physical pages, which are typically 16K
 in size. Expanded memory is divided into logical pages, also 16K
 large. Applications obtain expanded memory by asking the expanded
 memory manager (EMM) to allocate a certain number of logical pages. To
 use a set of logical pages, the application must ask the EMM to map a
 set of logical pages into the physical address space of the hardware.
 The application then accesses expanded memory just as it would access
 conventional memory. To use another set of logical pages, the
 application asks the EMM to map in the new set of pages. It is
 important to realize that an application can not access a page when
 that page is not mapped in. While the physical address space of the
 8088 is one megabyte, expanded memory can have up to 32 megabytes.

 Diagram 1 contains a memory map showing the relationship of a DOS
 application, such as LOTUS 1-2-3, to some expanded memory logical
 pages. In this example, there are two slots in the physical address
 space, which are labeled Slot-1 and Slot-2. The two logical pages,
 labeled Page-3 and Page-6 are currently mapped into the two slots.
 Note that the shaded portion are reserved for the operating system,
 and for hardware use (display memory is located here). In this
 example, the single page frame resides above 640K, although the EMS
 4.0 specification allows expanded memory to be mapped into any portion
 of the physical address space.

 There are two components to expanded memory: special hardware and
 special software. The hardware consists of a card that contains memory
 chips. The software consists of a device driver, called the expanded
 memory manager (EMM), which processes the requests for expanded memory
 actions, and makes the expanded memory board perform its mapping
 magic.

 We mentioned that applications must allocate memory by calling the
 expanded memory manager, and that another call causes the allocated
 page to be made accessible by hardware mapping. The third task that
 applications must perform is to free allocated expanded memory once it
 is no longer needed. Windows programmers will note that these three
 steps are analogous to the way that memory is allocated from the
 Windows memory manager: memory is allocated (either with GlobalAlloc
 or LocalAlloc), it is mapped into a physical address (via GlobalLock
 or LocalLock), and it is released when no longer needed (via
 GlobalFree or LocalFree). The only additional step, of unlocking
 memory (using GlobalUnlock or LocalUnlock) has as its analogue the
 unmapping of expanded memory pages, which in EMS is part of the
 mapping call.

 Some EMS boards only allow a single 64K page frame, which must
 physically appear above 640K. Other boards allow multiple page frames
 with no limit on the size or location of the page frames. Even though
 an EMS 4.0 driver can be written for either of these two types of
 boards, the different capabilities require that Windows deal with each
 a little differently. Older boards, on the one hand, leave the memory
 below 640K untouched, and give a relatively small page frame. Newer
 boards, on the other hand, can be configured to take over everything
 above 256K, allowing total bank space to exceed 600K. As I will
 discuss later, the size of the available page frame affects how the
 Windows memory manager makes use of expanded memory. For this
 discussion I will refer to older style EMS support, which only allows
 a single page frame, as "small frame " EMS. I will refer to the newer
 style EMS support, which allows multiple page frames, as "large frame"
 EMS. Note that the presence of terminate and stay resident programs,
 network device cards, and other dedicated memory applications, may
 cause Windows to use a newer EMS card in "small frame" EMS mode.

 The division between the bankable and the nonbankable portions of a
 given EMS memory configuration is called the "bank line."  Knowing
 where objects will be allocated ("above the bank line" or "below the
 bank line") will not effect you if you are writing a single,
 stand-alone Windows application. But if you develop applications that
 "talk" to each other, or if you write your own dynamic link library,
 use Windows hooks, or wish to use the clipboard or DDE, you should
 familiarize yourself with the location of memory objects relative to
 the EMS bank line.

 The mapping and unmapping of logical expanded memory pages does not
 involve copying large blocks of data. Instead, it involves modifying a
 few mapping registers. Once these mapping registers are changed,
 memory mapping automatically occurs on the expanded memory card. The
 result is that mapping is fast and efficient, with little performance
 degradation.

 Expanded Memory and Windows

 Windows 2.00 uses expanded memory to bank Windows applications. This
 expanded memory support lets multiple large Windows applications such
 as PC-Excel and Pagemaker, run simultaneously as fast as if each were
 running in Windows by itself. Windows 1.x uses expanded memory to swap
 traditional DOS applications, but not for swapping Windows
 applications. Windows 2.00 continues this "old application" support,
 but with the added advantage of banking windows applications in
 expanded memory.

 Memory is the most severely constrained resource in Windows. There are
 two reasons for this, one due to the hardware, and the other related
 to the multitasking nature of Windows. The hardware constraint is
 simply that the address space of the 8086 family of processors is one
 megabyte. The multitasking nature of Windows means that the demand on
 memory is potentially unlimited, as more and more applications are
 run. This problem is partially solved in the design of Windows, which
 provides a type of virtual memory support. Unneeded code and resource
 segments which have been marked "discardable" (and which are "least
 recently used") can be destroyed on demand, freeing up memory. Of
 course, when the destroyed objects are needed again, Windows will
 re-read them from the disk (code and resource segments in memory are
 "read-only", which means that the disk image is guaranteed to be
 correct). The memory problem is not entirely solved by discarding,
 however, as each application has a minimum memory requirement. This
 memory requirement means that two or more large applications may not
 be able to run simultaneously. This is a problem because users will
 most likely want to use the large, powerful applications together, to
 cut and paste data, initiate DDE conversations, and in general to take
 advantage of Windows' ability to move quickly between applications.

 Windows 2.00 provides a solution to this problem by having an enhanced
 memory manager which banks each application into its own set of
 expanded memory pages, so that each application, in effect, is running
 on the machine by itself. This works assuming the presence of the
 proper hardware (an EMS card) and the proper software (an EMS 4.0
 driver). The banking of application code and data is entirely
 transparent to the Windows programs. The Windows memory manager
 performs all required handshaking with the expanded memory manager to
 allocate memory, map and unmap pages, and to free EMS memory when an
 applications is closed.

 Why was this design chosen when the memory manager could have been
 enhanced to page in EMS memory to give a single Windows application up
 to the full 32 megabytes of memory that EMS 4.0 supports?  There are
 three reasons: implementation considerations, the performance goals
 required, and the ease with which alternative solutions were already
 available.

 Among the implementation considerations for the current design has to
 do with the need for Windows 2.0 to be backwardly compatible with
 Windows 1.x applications. This means that the interface to the memory
 manager must be the same. To make use of EMS memory, a new interface
 would be required. However, this interface already exists -- in the
 form of the EMS 4.0 specification. Therefore, if an application has a
 need for megabytes of memory, it can talk directly to the EMS memory
 manager. Shortly, we will discuss the role of the resource compiler in
 setting up this support.

 The second reason for the current design relates to the performance
 goals for supporting expanded memory. The chief goal of banking is to
 allow multiple large Windows applications to run simultaneously, with
 a minimum overhead incurred at context switching time. This goal is
 met in the current implementation, which insists only that each
 application be able to run with acceptable performance in 640K. After
 all, not every computer will have expanded memory, and thus windows
 developers could not assume its existence, except, perhaps, in
 vertical market packages where a turnkey system was being developed.

 The third reason that Windows' EMS support does not provide megabytes
 of memory for applications is that a disk cache serves the same
 purpose. Code, resource, and other discardable segments can be
 destroyed, and quickly re-read from the disk cache, using the loading
 and discarding capabilities of the memory manager. Windows 2.0
 includes a disk cacher, which dynamically allocates and frees EMS
 pages, as needed. Unlike a RAM drive, optimum performance of the disk
 cache does not depend on a user decision.

 Windows 2.0, unlike the earlier versions of Windows, will run in the
 compatibility box of OS/2. In this situation, much to my surprise,
 Windows will make use of expanded memory. Windows 2.0, of course,
 needs the proper hardware (card) and software (an EMS driver) support
 in order to perform this magic.

 How EMS Support Works

 The Windows memory manager is responsible for controlling the dynamic
 allocation of memory from the global heap (system at large memory), as
 well as allocation from each task or library's local heap. For the
 purposes of this discussion, we are only interested in memory
 allocated from the global heap. The Windows memory manager arbitrates
 requests for blocks of memory, handling all the administrative overhead
 when blocks must be moved or discarded to satisfy allocation requests.
 When a block of memory is freed (returned to the available pool of
 memory) the Windows memory manager makes note of this.

 Windows pages in a fresh bank of EMS memory for every application that
 is loaded. Diagram 2 contains a memory map showing three Windows
 applications, Write, Pagemaker, and Excel, each in its own bank of
 expanded memory pages. In this diagram, the Excel pages are all
 currently mapped into physical addresses. Note that only the minimum
 number of expanded memory logical pages are allocated for a given
 application, so that expanded memory can be used as effectively as
 possible.

 Diagram 2 also shows that all of Excel's pages are mapped in. This
 will happen whenever Excel is processing, or, in Windows terms,
 whenever Excel receives a message via the call to GetMessage. If the
 user clicks on the Pagemaker window, a context switch occurs to make
 Pagemaker the active application. During context switching, the memory
 manager will bank in all of the Pagemaker expanded memory pages. Note
 that this will cause all of the Excel pages to be banked out. This has
 implications for sharing memory between applications, which we will
 discuss later in this article.

 While the application, in this case, Excel, is processing, the
 allocation of memory banks remains fixed. This means that, as
 mentioned earlier, Windows applications are still running in 640K
 (actually up to 256K more, or 896K, with a 64K EGA card installed),
 and should be written to provide satisfactory performance in this
 amount of memory.

 Discarding will only occur to objects that appear in the physical
 address space, that is, in either the currently mapped EMS memory, or
 below the bank-line. Discarding never occurs to memory objects that
 are banked out.

 Memory Protection

 When an application's EMS memory is not mapped into the physical
 address space, it is protected from other "misbehaving" applications.
 When Windows is running with "large" EMS, that is, when expanded
 memory pages can be mapped into any portion of the physical address
 space, each application is completely protected from others. When
 "small" EMS is running, only those portions which are in EMS memory,
 that is, the code segments, will be protected. Although this memory
 protection is not as complete as the memory protection of OS/2, in
 which illegal addressing results in a general protection fault, it
 minimizes damage in a way that Windows 1 could not.

 Windows Applications and EMS 4.0

 While Windows is running with EMS memory, there are in fact two memory
 managers present: the Windows memory manager and the expanded memory
 manager. The Windows memory manager is the primary memory manager
 under Windows 2, calling on the expanded memory manager to control
 expanded memory. Its task consists of allocating, mapping and
 unmapping, and freeing expanded memory, plus keeping track of the EMS
 registers. Because there are separate memory managers, your Windows
 applications can directly call the expanded memory manager (once the
 application knows that EMS is present), to allocate memory on its own.
 The mechanics of this will be discussed later in this article.

 The description of Expanded Memory earlier in this article mentioned
 that the division between bankable and non-bankable memory is called
 the "bank-line." It is of interest to know which objects are allocated
 above the bank-line (and are, therefore, bankable), and which are
 allocated below the bank-line (and are thus inherently shareable).
 However, this discussion is complicated by the fact that EMS 4.0
 supports two types of page frames: small and large. You will recall
 that the page frame is the EMS "porthole" into expanded memory. The
 small page frame for EMS memory is 64K, while the large page frame for
 EMS memory can be up to a 896K, given the proper hardware setup.
 Because almost the entire address space (above 256K) can be banked
 using the large page frame, it offers the most flexibility to the
 Windows memory manager. Table 1 shows how expanded memory is used, for
 both types of page frames.

                                              Above the Line
                                Always        --------------
                               Below the      small  | large
                                 Line         frame  | frame
                               =========      ==============
 Task databases                   X
 Module (EXE) headers             X
 Library data segments            X
 Library fixed Code               X
 Thunks                           X
 Library resources                X

 Task code segments                             X        X
 Task resources                                 X        X

 Task data segments                                      X
 Library discardable Code                                X
 Dynamically Allocated Memory
      (via GlobalAlloc)                                  X

 Multiple instances of a given application, in the current
 implementation, will share a given bank (although each instance will
 have its own data segment), thus removing any potential problems for
 the sharing of instance data, either explicitly (through
 GetInstanceData) or implicitly, through sharing memory handles. Future
 version may give developers the option of a requesting a separate EMS
 bank for each instance of an application.

 Certainly large frame gives the memory manager the greatest
 flexibility, but even with small frame, there are significant
 advantages to EMS support. For small applications, all of the code and
 resources will fit into banked memory, allowing many small application
 to run with little performance degradation. Applications like the
 control panel, the calculator, or the calendar, can be loaded together
 with a large application like Excel, with a minimum of interference.
 Small frame EMS support, then, allows the user to take better
 advantage of Windows' multitasking abilities.

 Dynamic Link Libraries and Large Frame EMS

 When small frame EMS is present, only the resources and code segments
 from a task are loaded into the EMS banks. With large frame EMS, on
 the other hand, the memory manager places several other types of
 objects above the line in the EMS banks. These include task data
 segments, library discardable code segments, and memory allocated with
 GlobalAlloc. These last two deserve some attention, because they raise
 important implementation issues.

 Dynamic link libraries are accessible from any task. Examples of
 dyna-link libraries under Windows include Kernel, User, and GDI, as
 well as all device drivers (keyboard, mouse, timer, display, and
 printer). Clearly, library routines, such as the GDI routine TextOut,
 must be accessible from any task at any time. When large frame EMS is
 present, the code in dyna-link libraries are handled in one of two
 ways: fixed code segments live below the line, discardable code
 segments live above the line.

 Because large frame EMS support places library discardable segments
 above the line, that is, in the bank of the calling task, there is a
 possibility that at any given time, a given library code segment may
 appear in multiple banks. For example, multiple copies of the dialog
 box control manager, which contains the window procedures for the
 edit, pushbutton, and other dialog box controls, might concurrently
 reside in EMS memory. Although there is redundancy, the net result is
 that applications have access to the library segments that they need,
 and little room is taken up in the scarce portion of memory below the
 bank line.

 Ordinarily, when dynamic link libraries allocate memory with
 GlobalAlloc, and a large EMS frame is present, the allocation occurs
 above the line. There are times, however, when it is necessary for a
 library to have its global memory below the line. Some libraries may
 have global memory objects that they need to access at all times. In
 such a case, the GMEM_NOT_BANKED flag is used in the GlobalAlloc call.

 Windows 1.x printer drivers run into compatibility problems, however,
 because they do not use the GMEM_NOT_BANKED flag. To address this
 issue libraries are handled in the following manner. Printer drivers
 tend to be the only libraries that are loaded via the LoadLibrary
 routine; other libraries are loaded as "dependent modules" of tasks. A
 dependent module is a library that must be present for a task to
 execute. Therefore, any libraries loaded with LoadLibrary have
 GlobalAlloc allocate below the EMS bank line. Other libraries, that
 is, dependent modules, default to allocating global memory above the
 bank line.

 Changes to the Windows API

 Only a single routine, LimitEMSPages, has been added to the Windows
 API. This routine allows an application to control the number of EMS
 pages that Windows allocates on its behalf. It does not, however,
 limit the number of pages that an application can allocate for itself
 from the EMS memory manager.

 Disabling Windows EMS Support

 When you want to run Windows with EMS memory disabled, give the /n
 switch to the WIN command at the DOS line, as shown below:

    C> win /n

 Virtual Memory in Windows 386

 The biggest advantage of Windows 386 occurs in the support of
 traditional DOS applications. The current implementation of Windows
 386 creates multiple virtual machines (VM's), each of which behaves as
 an independent 8086. Because each is given its own 8086 machine, with
 640K of address space, applications like Lotus 1-2-3, and Rbase System
 V, can run independently without interfering with each other. In
 addition, because of the ability of the Intel 386 to intercept direct
 memory mapped video output, DOS applications can each run in a Window
 on the screen.

 Note, however, that Windows 386 does not give each Windows application
 its own 640K partition; instead, all Windows applications run in the
 same 640K partition. This means, in the current implementation, that
 Windows applications do not have the same memory advantages that
 traditional DOS applications have running under Windows 386.

 Memory banking support in Windows 386 is the same as EMS support in
 Windows 2. Although it works a little bit differently, due to the fact
 that the 386 has hardware support for memory management, the
 differences is transparent to the Windows application. The programming
 recommendations given in the following section, therefor, apply to
 expanded memory support in Windows 2, as well as to virtual memory
 support in Windows 386.

 Programming Considerations

 The programming guidelines listed here should not be a surprise to
 Windows programmers, who have been told to be "nice neighbors" and
 "good windows citizens" since they started writing their first window
 procedure. It is useful to reiterate them, however, because Windows
 EMS support and virtual memory support require that you follow them
 strictly. A few of the guidelines are specific to the addition of EMS
 support, and will help you insure that your applications run in both
 Windows 2 and Windows 386.

 Efficient Use of Memory

 None of the plans for the future of Windows memory management include
 allowing Windows applications to allocate beyond the one megabyte
 physical address space of the 8086. Instead, as has been discussed,
 Windows performs bank switching of memory, so each application
 behaves as if it had the entire computer to itself.

 The Windows memory manager lets you specify that memory allocated from
 the global heap, when EMS is running, be below the bank-line, that is,
 be visible at all times from all applications. You do this with the
 GMEM_NOT_BANKED flag to the GlobalAlloc call. You should regard this
 as a method of last resort for sharing memory. The reason is that
 memory below the line is a very scarce resource, and is only used for
 the most critical system objects. Appropriate methods for regular data
 sharing will be discussed in a moment, but unless you have a very
 compelling reason, do not perform this type of allocation. A good
 example of an global allocation that must be below the line is the
 allocation required by a printer driver, which I discussed earlier.

 Code Structure

 To optimize the performance of your application with small frame EMS,
 mark the most important segments as PRELOAD in the module definition
 (DEF) file, and list their names at the beginning of the SEGMENTS
 list. Performance is improved because, when Windows starts loading an
 application into memory, it starts with the bankable EMS memory. Only
 after the EMS bank is filled does Windows start using space below the
 line. This improves the performance of your application because, with
 small frame EMS, the memory manager will not discard objects from
 above the bank line.

 Data Sharing

 Three methods of data sharing are guaranteed to work in current and
 future versions of Windows: the clipboard, DDE, and libraries. Other
 than the clipboard and DDE, you should not share memory by passing
 global memory handles between applications. Also avoid passing long
 pointers to data objects. You have no guarantee with either method
 that the target data will be banked in when the receiving application
 is processing. Applications which ignore this guideline will not run
 under planned versions of Windows 2 or Windows 386.

 When reading from the clipboard, be sure to copy the data from the
 clipboard into your own data areas before you close and release the
 clipboard. Windows supports EMS by copying clipboard objects that have
 been banked out into the currently banked memory. After the clipboard
 is closed, these objects will be destroyed, rendering them
 inaccessible to the receiving application.

 When an application receives a global handle through the clipboard or
 DDE, it must check the return value to the GlobalLock call. The
 Windows memory manager copies into the current bank those DDE or
 clipboard objects that reside in unbanked EMS memory. When the memory
 manager is not able to perform this copying (such as in a low memory
 situation), it informs the receiving application by returning null to
 GlobalLock.

 Code Sharing

 Certainly one of the innovations of Windows is the ability to share
 code. There is only a single copy of any given Windows routine loaded
 into memory at any given time, and it can be accessed, via the dynamic
 linking mechanism, by multiple applications. Window procedures are
 shared between multiple instances of windows, and fine-tuned through
 subclassing. How is code sharing affected by EMS memory?  If you write
 "well-behaved" Windows applications, code sharing is not affected.

 As mentioned earlier, multiple instances of an application run in the
 same bank of EMS memory. In this case, there is no problem in sharing
 code between them, just as there is no problem in sharing data or
 resources.

 Dynamic link libraries are one method for sharing code between
 multiple cooperating applications. Because Windows loads library
 discardable segments above the bank line, there can be multiple copies
 of some code segments in EMS memory. This redundancy can be avoided by
 converting the library into a windowless task, and to communicate with
 the calling applications via the various message posting routines
 (PostMessage and PostAppMessage).

 You can never execute code that belongs to another task by directly
 calling it. In Windows routine terms, you can never call
 GetProcAddress on another task, in order to directly call the target
 code yourself. Although this worked in Windows 1.x, the Windows 2.0
 memory manager will not allow you to do this. Instead, use the
 SendMessage routine to perform inter-task calls.

 An application can directly call the expanded memory manager to
 allocate EMS memory, provided it follows EMS 3.2 (it may use the
 reallocation function (function 17) from LIM/EMS 4.0). Windows must be
 notified so that it does not try to use the 64K EMS 3.2 page frame:
 you do this by adding the switch -LIM32 to the call to the resource
 compiler. Windows will avoid putting code in the 64K EMS 3.2 window
 for your application, but it will still be able to use this page frame
 to bank other applications.

 Allocating Global Memory

 It is worth reiterating some important points on global memory. The
 first is you should not allocate below the line, that is, should not
 use the GMEM_NOT_BANKED switch to GlobalAlloc, except as a last
 resort. When global memory objects are shared between applications,
 the call to GlobalLock takes care of copying the appropriate blocks of
 memory, as necessary. Of course, this should only be used in the
 context of the clipboard and DDE, to guarantee compatibility with
 future versions of Windows 2, most notably, Windows 386.

 Another point worth restating is that your program should always check
 for failure in (a) allocating memory, (b) reallocating memory, and (c)
 locking memory. This is critical when using DDE or the clipboard to
 share data, but is a good programming practice for all Windows
 programming. An invalid far pointer is a deadly weapon, which you must
 take care to defuse, for it allows data to be written or read from
 anywhere in the computer's address space.

 Windows Hooks

 Windows hooks allow you to trap certain types of events before they
 are placed into the system queue. For example, you can write a
 keyboard hook to watch for the <Alt> - P key, which might cause a
 background screen capture program to print the screen on the default
 printer.

 The impact of EMS memory on Windows hooks requires some extra
 programming effort on your part. First of all, windows hooks must
 appear below the bank-line, and so should be placed in fixed library
 segments. In addition, because a window hook has no way of knowing if
 the initiating task is currently banked in, it must communicate with
 the initiating task via messages (most likely SendMessage).

 Debugging Support

 You can now use Codeview, Microsoft's premier debugging tool, to debug
 Windows applications under Windows 2.0. This is a welcome improvement
 over Symdeb, a powerful but cryptic debugging tool that appeals mostly
 to assembly language programmers. Codeview requires either a
 monochrome monitor, or some sort of dumb terminal, because when
 Windows is running, it takes over the screen. An additional hardware
 requirement, if you wish to use Codeview to debug your Windows
 programs, is that you must have EMS memory installed. This is because
 Codeview talks directly to the expanded memory manager, to minimize
 the amount of memory taken from the program being debugged.

 To facilitate debugging when EMS is present, you may wish to place the
 following switch in your WIN.INI file:

    [kernel]
    EnableEMSDebug=1

 which allows you to set breakpoints within discardable library code.
 Recall that there can be multiple copies of discardable library code
 segments, each in a different EMS bank. This switch tells the memory
 manager to update the debugger's table of library discardable segments
 during every context switch. Any debugger that works with Windows,
 including Symdeb, Codeview, Answer, or Atron, will work properly with
 EMS.

 Conclusion

 For the most part, expanded memory support under Windows will be
 transparent to the Windows programmer. With the exception of a single
 new Windows routine, LimitEMSPages, a few flags, and resource compiler
 switches, no changes have been made to the programming interface that
 Windows programmers work with. What has changed, however, is that
 there must be stricter adherence to interactions between tasks. Data
 sharing, for example, must be performed using one of three supported
 methods: the clipboard, DDE, or via a shared dyna-link library.
 Directly calling into another task's code, never a recommended method,
 is not possible with the Windows 2.0 memory manager.

 Where Windows programmers will benefit from Windows 2.0 EMS support is
 due to the fact that improved performance when running multiple large
 applications will entice users to purchase products that interact with
 applications like Excel and Pagemaker. Towards this market, Windows
 programmers can also, of course, write their own large, powerful
 applications. Because the EMS support is transparent to the Windows
 programmer, she can use the existing programming interface, and let
 the Windows memory manager take advantage of EMS if it is installed.


 355. Displaying Raster Image Files

 Question:
    I am trying to display a raster image file. Currently I am able to
 do this by setting up the window and doing a SetPixel() command for
 the entire image.
    Is there a better way to do this?
    I know that the Windows Version 2.03 allows bitmaps that are
 greater than 64K. Is there any limitation on the maximum size of the
 bitmap?

 Response:
    Rather than using SetPixel() to display your raster image, use
 SetBitmapBits(). This will allow you to load a number of bits into the
 bitmap at a time. You also can allocate a second smaller bitmap, use
 SetBitmapBits() to load up the temporary bitmap, and use Bitblt to
 copy the data into the big bitmap in reasonably sized amounts.
    There is no limit on the size of bitmaps other than the available
 memory at the time you create them.



 356. Use lseek() to Reposition File Pointer when Reopening File

 Question:

 The "Microsoft Windows Software Development Kit Programmer's Learning
 Guide" Page 191, Section 11.6, "Reopening Files," states:

    "When a file is reopened, the file pointer marking the current
    position in the file is moved to the same position it was in just
    before the file was closed."

 I have not been able to make OpenFile() reposition the file pointer as
 documented, even when I open it with the OF_REOPEN flag. What do I
 need to do?

 Response:

 You cannot reopen a file with the file pointer reset to its last
 location. The structure elements of OFSTRUCT do not include a file
 position element. The description in the manual is incorrect.

 When OpenFile() opens or reopens a file, the file pointer is set to
 the beginning of the file. Use the lseek() function to find the
 desired position in a file. Lseek() is a C run-time library function,
 not a Windows API call.


 357. Background Not Erased without WS_CLIPCHILDREN

 Problem:
    My application seems to have a problem in the child window paint
 routine. When a child window that was not overlapping a parentless
 popup window is moved to overlap some of the area previously occupied
 by the popup window, the child window will not repaint thoroughly.
 This problem is present in the Party program presented in Charles
 Petzold's book, "Presenting Windows."

 Response:
    This problem occurs because the parent was not created with the
 WS_CLIPCHILDREN style. This style assures that the area occupied by
 child windows is removed from the clipping region of the parent.
    In the situation described, part of the area occupied by the child
 is included in the parent's clipping region, and the parent erases its
 background after the child. This causes the parent's background to
 look like the child's background is not being properly erased.
    Parent windows should be created with the WS_CLIPCHILDREN style to
 prevent this problem. Sibling windows should be created with the
 WS_CLIPSIBLINGS style to prevent similar problems.


 358. What EMS Means to Developers

 Question:
    How will using the expanded memory of Windows Version 2.03 affect
 my application development? How can I best take advantage of the
 transparent use of EMS? Will using EMS harm my application?

 Response:
    Please note that when expanded memory is mentioned in this article, it
 also means extended memory. Extended memory is used as expanded
 memory by Windows/386.
    In Windows Version 2.03, expanded memory can be used by the Windows
 Memory Manager. In Windows/386, the Memory Manager can take advantage
 of extended memory by virtualizing it as expanded memory. In
 Windows/386, all applications are run in one virtual machine, but each
 DOS application gets its own virtual machine to run in.
    Expanded memory in Windows Version 2.03 presents the following
 problems:

    1. Applications cannot share memory through global handles
    2. The efficient use of available EMS in a small frame

    The only way applications can share global data is through the DDE
 protocol or through the clipboard because when large frame EMS is
 used, global objects are allocated above the EMS line.
    For example, in large frame EMS, if task A allocated some global
 memory and passed the handle to task B, the object would no longer be
 in memory when task B has the processor because task A's EMS has been
 banked out. If task B tried to lock this object, it would fail.
    DDE and clipboard objects can be shared because the Windows memory
 manager will copy the contents of these objects to the currently
 running application's bank of EMS. The application must check the
 return value from GlobalLock() because the memory manager may not be
 able to copy the object in a low memory situation. Clipboard objects
 should be copied by the receiving application before the clipboard is
 closed because the memory manager may delete the global object.
    Applications can share data through DLLs, as well as through DDE and
 clipboard objects, but only data located in the DLL data segment can
 be shared because global objects allocated by DLLs are allocated above
 the EMS line, just like objects allocated by an application.
    However, there is one exception to the rule of not sharing global
 memory handles. This exception occurs by allocating global objects
 with the GMEM_NOT_BANKED flag and keeps the object below the EMS line
 so all applications can access it. Using this flag should be avoided
 because of the already limited memory below the EMS line when large
 frame is used. Some device drivers will find it necessary to use this
 flag, but try at all costs to avoid using it from an application.
    The second issue involves using most effectively EMS that is
 available to an application when in small frame. When small frame is
 used, the only items in EMS that are available to an application are
 code and resources. Once code or resources are loaded into EMS they
 will never be discarded. However, they will be banked out when
 another application has the processor.
    The Windows loader fills up EMS before it loads code or resources
 into conventional memory. It is important to list first in the DEF
 file and as PRELOAD those code segments which will be important to the
 application throughout its life. To do this, you must list first in
 the DEF file the segment that contains your window procedure,
 instead of the segment that contains your initialization procedures.
    If you do not have enough code to fill the EMS available (or enough
 code that is important to the application throughout its life), mark
 those resources that will be important to your application throughout
 its life as PRELOAD in your resource file
    For small applications there is a new call, LimitEMSPages(). This
 new call allows an application to limit the amount of EMS that is
 allocated on its behalf. The argument passed to this function is the
 maximum number of kilobytes of EMS that Windows will allocate for the
 application.  Small applications may want to call this function so
 that 400 or 500K of EMS won't be allocated for it when it knows that
 it can comfortably run in 100K.
    If you plan to use the LimitEMSPages function, keep in mind that in
 large frame (where DLL code goes above the EMS line) a small
 application will need more room than the sum of its segments because
 library code will share the EMS that has been allocated for the
 application.
    Please note: Hook callback functions must be placed in FIXED
 library code so that the code will always be present. The same is true
 for GlobalNotify() callback functions.


 359. Small-Frame EMS Versus Large-Frame EMS

 Problem:
    When I have less conventional memory available (due to installing
 network software and TSRs), I notice that Windows Version 2.03 uses
 more conventional memory and less expanded memory.
    When there is less conventional memory, I expect that expanded
 memory would be used as much as possible, reducing the impact on
 conventional memory.

 Response:
    In Windows Version 2.03, expanded memory can be used by the Windows
 Memory Manager. In Windows/386, the Memory Manager can take advantage
 of extended memory by virtualizing it as expanded memory. Also, please
 keep in mind that in Windows/386, all Windows applications are run in
 one virtual machine, but each DOS application runs in its own virtual
 machine.
    Expanded memory in Windows Version 2.03 is not used to give an
 application more than 1 megabyte of addressability. Expanded memory
 does not swap code segments to and from conventional memory; however,
 it does allow fast context switching because for each application, its
 associated copies of EMS page frames are banked in to the addressable
 memory below the 1-megabyte line when it has the processor.
    Instead of discarding an application's code segments when it loses
 the processor, the Version 2.03 copy of EMS page frames are banked
 out. When it gets the processor again, the pages are banked in again,
 not reloaded from disk.
    Depending on the amount of free memory below the 640K line after
 Windows has loaded, the Windows Memory Manager will use large frame or
 small frame EMS. Large frame can be used only when the EMS board is
 able to bank memory down to 256K. When large-frame EMS is used, more
 than 500K of EMS may be banked in with some memory above the 640K line
 and some below. With small-frame EMS, all page frames are above 640K.
    There must be a certain amount of available memory below 640K
 before large-frame EMS can be used because certain items must be
 stored in nonbankable memory (below the EMS line). The following items
 are nonbankable:

    1. Task databases
    2. Executable headers
    3. Library data segments
    4. Library fixed code
    5. Thunks
    6. Library resources

    If not enough nonbankable memory exists below the line to hold
 these objects when large-frame EMS is used, small-frame EMS must be
 used. When your TSRs are not loaded, you should have enough memory
 below 640K for large frame EMS to be used. When your TSRs are loaded,
 there will not be enough memory below the EMS line to use large-frame
 EMS, so small-frame EMS must be used. Thus, you use less expanded
 memory when you start with less conventional memory.


 360. Dialog Boxes Aligned Off Screen

    Moveable dialog boxes can be moved off screen; however, they then
 cannot be made visible by using a mouse.
    The problem is the use of the CS_BYTEALIGN-class style bit
 in the dialog box's frame window. When the left border of the dialog
 box is moved within a few pixels of the right edge of the display and
 the mouse button is released, the window jumps to the right to byte
 align the window frame. However, the byte boundary is off the screen.
    To work around this problem, make sure the size of dialog boxes
 created with a caption bar are such that the horizontal-width module
 eight (8) is four (4) or less.
    Give the dialog box a system menu (which any moveable dialog box
 should have so that it can be moved by the keyboard as well as the
 mouse) to prevent the dialog box from disappearing off the right
 edge of the screen.


 361. LIM 4.0 and Windows Use of Expanded/Extended Memory

 Question:
    I am developing a very large application that will use Excel and
 Windows. I have projected that it will run best on an IBM PS/2 Model
 80 with 8 megabytes of memory. I also want it to be able to run on
 smaller, less expensive hardware.
    Can I run a large generic application on the following equipment
 using Windows, or are there other recommended configurations? I have
 thought of the following configurations:

    1. IBM PC AT, VGA, mouse, 8 megabytes expanded memory, Windows
 Version 2.03
    2. IBM PS/2 Model 80, VGA, mouse, 8 megabytes extended memory,
 Windows/386
    3. Compaq DeskPro 386, VGA, mouse, 8 megabytes extended memory,
 Windows/386

 Response:
    Please note that in the following paragraphs, when expanded memory
 is mentioned, it also means extended memory used as expanded memory by
 Windows/386 Version 2.03.
    In Windows Version 2.03, expanded memory can be used by the Windows
 Memory Manager.
    In Windows/386 Version 2.03, the Windows/386 Memory Manager can
 take advantage of extended memory by virtualizing it as expanded
 memory. In Windows/386 Version 2.03, all Windows applications are run
 in one virtual machine, but each DOS application gets its own virtual
 machine to run in.
    Windows Version 2.03's use of expanded memory allows multiple large
 applications to run together with fast context-switching. Depending on
 the amount of memory available below the 640K line when Windows is
 loaded, "large frame" Expanded Memory Specifications (EMS) or "small
 frame" EMS will be used by the Windows Version 2.03 Memory Manager.
 (For an in-depth discussion of the differences between large- and
 small-frame EMS, refer to the article by Paul Yao mentioned below.)
 In brief, when large-frame EMS is used, multiple large applications
 will be more likely to run with fast context-switching.
    Windows uses expanded memory to bank applications. One application
 will be actively banked in at a time. Therefore, each application will
 almost seem as though it is the only application in the system because
 other applications will be banked out while it is running.
    Expanded memory in Windows Version 2.03 is not used to swap code or
 data to and from conventional memory; rather it is used to map in (to
 addressable memory below the 1-megabyte line) different page frames
 depending on which application is currently running. Expanded memory
 will not increase the maximum size of a Windows application. Expanded
 memory is only used to save the state (image) of the application while
 it is not currently executing.
    Regardless of whether they will be run on a machine with expanded
 memory, Windows applications should always be written using a multiple
 segment approach with mostly discardable segments. This way, it is
 possible to write applications greater than 1 megabyte (the physical
 address space of the 8086). If a new segment must be loaded, another
 segment can be discarded (and reloaded again later, if necessary).
 Segments are not swapped to expanded memory. Discarding and reloading
 from disk is still used even when expanded memory is present.
    The use of LIM 4.0 described above is done transparently to your
 application. However, if you wish to access expanded memory directly,
 your application can use LIM 3.2 calls to manage large amounts of
 data. The application can do this by directly calling the LIM 3.2
 memory manager to access data in expanded memory. Just as in DOS, the
 number of physical page frames one application can address at a time
 is four 16K page frames. This does not limit you to 64K of LIM 3.2
 memory; however, only 64K can be addressable at any one time.
    To use LIM 3.2 calls, you must add the "-lim32" switch to the "rc"
 resource link line to let the Windows Version 2.03 Memory
 Manager know that the application will be making LIM 3.2 calls. If
 your application expects to have access to large amounts of data
 through LIM 3.2 calls, its performance will suffer greatly on machines
 without expanded memory.
    Windows Version 2.03's use of expanded memory is explained at
 length in an article by Paul Yao in the January 1988 issue of the
 Microsoft Systems Journal.
    For more information on expanded memory, use the following keywords
 in your query:

    1. expanded
    2. EMS
    3. LIM 4.0
    4. LIM 3.2


 362. Font Editor Produces Improper Version 2.x Fonts

 The Version 2.x font editor produces improper Version 2.x fonts. The
 problem appears only when an application requests a large font and the
 font mapper tries to bit-replicate the font to a larger size. This
 situation produces an enlarged font with either the bottom half of its
 characters or the second half of its character set destroyed.

 This problem occurs because of the way the font editor calculates the
 dfWidthBytes field. When the font editor is writing a font, it
 miscalculates the dfWidthBytes field to be too small; only a portion
 of the font bitmap is replicated by the font mapper. Unenlarged fonts
 are displayed correctly because the text output routines do not
 reference the dfWidthBytes field.

 To work around this problem, do the following:

 1. Edit the fonts in Version 1.x format.

 2. Build the .FON resource in Version 1.x format.

 3. Convert the resource to Version 2.x format using NEWFON.

 This problem affects the fonts provided with the VGA and 8514 display
 drivers.

 Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10. We are researching this problem and will post new information
 as it becomes available.


 363. Expanded BANDINFO Documentation


 As of July 1988, the "Microsoft Windows Software Development Kit
 Programmer's Reference," the "Microsoft Windows 2.00 Software
 Development Kit Update," and the "Microsoft Windows Adaptation Guide"
 all incompletely document the use and implementation of the BANDINFO
 printer escape.

 The following is an expanded version of the documentation of the
 BANDINFO escape in those manuals. Refer to the index or table of
 contents section to determine where the original BANDINFO information
 is located.

 short Escape(hDC, BANDINFO, nCount, lpInData, lpOutData)

 Purpose

 Provides better communication between a banding driver and the
 application.

 Parameters

    Parameter     Definition

    hDC           A handle to the printer display context

    nCount        Not used and can be set to NULL

    lpInData      A long pointer to a data structure that has the
                  following structure:

                  typedef struct {
                      BOOL    GraphicsFlag;
                      BOOL    TextFlag;
                      RECT    GraphicsRect;
                      } BANDINFOSTRUCT;

                  This data structure provides the primary
                  communication between the driver and the
                  application as to what (graphics and/or text)
                  is actually on the page. The notes below
                  describe its use.

    lpOutData     A long pointer to a data structure having the same
                  items as lpInData, a BANDINFOSTRUCT

 Return Value

    Return Value  Result

    nResult       1 if the function is successful
                  0 if the function isn't successful or if the escape
                  isn't implemented

 Notes

 This escape is only implemented for devices that use banding to output
 to the printer. It's called immediately after the NEXTBAND escape,
 every time the NEXTBAND escape is called.

 The BANDINFO data structure is used to communicate between the driver
 and the application as follows:

    Data structure  Used as lpInData            Used as lpOutData
    field           (application communicates   (driver communicates
                    to the driver)              back to the application)

    GraphicsFlag    TRUE if there are graphics  TRUE if the driver is
                    on the page                 expecting graphics in
                                                this band

    TextFlag        TRUE if there is text on    TRUE if the driver is
                    the page                    expecting text in this
                                                band

    GraphicsRect    The bounding rectangle for  No valid return data
                    all graphics on the page

 This escape is always executed immediately after a NEXTBAND escape and
 is in reference to the band the driver returned to that escape. This
 escape is designed to be used in the following manner:

 1. On the first band, the driver gives the application a full-page
    band and asks for text only (GraphicsFlag == FALSE, TextFlag ==
    TRUE). The application sends only text to the driver.

 2. If in the first band the application said it had graphics,
    (GraphicsFlag == TRUE), or if the driver encountered any vector
    fonts, the driver will have to band the rest of the page. (The
    driver can detect vector fonts by noting if RealizeObject() was
    ever asked to realize a vector font, or if the driver encountered
    any graphics primitives during the first full-page text band.
    These can be generated by GDI simulating vector fonts through
    PolyLine(). The driver should set a flag in the PDEVICE noting
    this.) If there are no graphics or vector fonts, the next
    NEXTBAND will return an empty rectangle, indicating that the
    application should move on to the next page.

 3. If there are graphics and no vector fonts, (i.e., the application
    set GraphicsFlag == TRUE, and no graphics were noted in the first
    full-page text band), then for subsequent bands the driver only has
    to band into the rectangle the application passed, which bounds all
    graphics on the page. If there are vector fonts, the driver will
    band the entire width and depth of the page with the TextFlag ==
    TRUE. (It will also set the GraphicsFlag == TRUE if the application
    set it.)

 The driver assumes that an application using BANDINFO will send only
 text in the first full-page text band because that's all the driver
 asked for. Therefore, if the driver sees a vector font or any graphics
 in the band, it assumes they were generated by a text primitive and
 sets the TextFlag == TRUE for all of the subsequent graphics bands so
 that they can be output as graphics. If the application does not meet
 this assumption, the image will still be generated properly; however,
 some time will be wasted by sending spurious text primitives to
 graphics bands.

 Older Device Drivers

 On older devices that were written before the BANDINFO escape was
 designed, full-page banding for text was used. If the BANDINFO escape
 is not supported on a device, but RC_BANDING is set, the application
 can detect full-page banding for text by observing if the first band
 on the page covers the entire page. The implementation for full-page
 banding for text is documented as follows under "Full Page Banding for
 Text" in the "Microsoft Windows Adaptation Guide" (note that Strblt()
 is the low-level implementation of TextOut()):

    "Banding devices that offer high-quality device fonts may benefit from
    making two complete passes over each page to be output. The first pass
    outputs all text on the page, while the second pass divides the pages
    into bands and outputs graphics. This method eases the memory
    requirements for the device, since text lines are handled by the
    device fonts instead of being treated as bitmaps.

    When the device driver receives the first NEXTBAND call, it returns
    the device coordinates of the entire page to GDI. This means that no
    clipping takes places as GDI plays the metafile for the page. The
    device driver processes all Strblt() calls for the page and ignores
    all other calls.

    When GDI finishes playing the metafile and calls NEXTBAND again, the
    device driver initializes a buffer for the first band of graphics and
    returns the coordinates of the band to GDI. From this point on,
    banding is handled in the standard fashion, except that the device
    driver now ignores all Strblt() calls, since they have already been
    processed. The device driver continues processing and outputting bands
    until the last band on the page is processed; the device driver then
    returns 0,0,0,0 to GDI.

    Some pages may contain only text and no graphics. To detect this case,
    the device driver can maintain a flag during the first (text) pass
    over the page. If the GDI metafile makes calls to any routine besides
    Strblt(), the flag is set (although the call to the routine is
    ignored). Before returning the coordinates of the first graphics band
    to GDI, the device driver checks the flag. If the flag is not set, no
    graphics are required and the device driver can simply return
    0,0,0,0."

 Code Outline

 The general outline of a routine using BANDINFO to output a page
 should be as follows:

    do {
        if (BandingDevice) {
            NEXTBAND
            BANDINFO
            }
        else {

            /* It's not a banding device. */

            /* Set the band to the entire page. */
            BandRect.left   = 0;
            BandRect.top    = 0;
            BandRect.right  = dxPrinter;
            BandRect.bottom = dyPrinter;

            /* Output everything. */
            BandInfoOut.GraphicsFlag = TRUE;
            BandInfoOut.TextFlag     = TRUE;
            }

        if (TextFlag) {
            Do any text.
            }
        if (GraphicsFlag) {
            Do any graphics.
            }

        } while (BandingDevice &&
                 more bands to do);

    /* NEWFRAME and NEXTBAND are exclusive, but at least one must be
       used. Hence we need to do a NEWFRAME if we're not banding. */
    if (!BandingDevice) {

        /* Put out the page. */
        if ((SpoolerError = Escape(hDC, NEWFRAME, 0, 0L, 0L)) <= 0) {
            Result = ConvertSpoolerError(SpoolerError);
            Escape(hDC, ABORTDOC, 0, 0L, 0L);
            goto ErrorExit3;
            }
        }

 For more information on BANDINFO, query the OnLine KnowledgeBase for
 NEXTBAND, NEWFRAME, BANDINFO, banding, and printing.


 364. LineTo Clipping Problem

 Problem:
    Under certain conditions, the clipping region used when drawing
 lines within the application window or its child windows seems to be
 incorrect.
    Create two windows, the application (or parent) window and a
 modeless dialog box. The diagram below indicates the window layout
 required to cause the problem.
    To reproduce the problem, perform the following actions:

    1. Make sure that the right side of the dialog box extends beyond
 the right side of the application window.
    2. Draw the following four line segments:
       a. Start line 1 below and to the left of the dialog box and
 finish above the dialog box, passing through the left side.
       b. Start line 2 above the dialog box and finish below and to the
 left of the dialog box (opposite of line 1).
       c. Start line 3 above and to the left of the dialog box and
 finish below the dialog box.
       d. Start line 4 below the dialog box and finish above and to the
 left of the dialog box (opposite of line 4).

    In the following diagram, lines 1 and 3 are drawn correctly, but
 lines 2 and 4 are not drawn correctly:

    -------------------------------------------------|
    |Parent         4\  \    /  /1                   |
    |Window     - - - - - - -------------------------------
    |                      \| Modeless Dialog Box         |
    |                       |                             |
    |                       |                             |
    |                     / |                             |
    |           - - - - - - -------------------------------
    |              2/  /       \  \3                 |
    |-------------------------------------------------

 Response:
    The problem is internal to GDI. The problem lies in walking the
 list of clipping regions when the line to be drawn is defined right to
 left.
    If the line enters a nonclipped region from a clipped region
 through the vertical edge, the line will not be drawn properly if
 there is a clipping rectangle at that height.
    The workaround is to always call LineTo with lines defined left to
 right. Code similar to the following will correct the problem:

 MyLine(hDC, X1, Y1, X2, Y2)
 HDC hDC;
 short X1, Y1, X2, Y2;
 {
   if (X1 > X2) {
     short register temp;

     temp = X2;
     X2 = X1;
     X1 = temp;
     temp = Y2;
     Y2 = Y1;
     Y1 = temp;
     }
   MoveTo(hDC, X1, Y1);
   return(LineTo(hDC, X2, Y2));
 }


 365. Input Sample Source Error Caused by Small Global Array

 Problem:
    There is a problem with a sample Windows program that comes with
 the Windows Software Development Kit Version 2.03. When I am running
 INPUT.EXE on the Sample Source Code disk in the \LEARNING\INPUT
 directory, if I select the About dialog box, I will get a message box
 saying "Unable to write to AUX." This message box appears if I drag
 the elevator of the vertical scroll bar up and down and leave the
 elevator at the bottom of the scroll bar. Clicking the Retry or Cancel
 button for the message box does not work. I have to reboot the machine
 to get out of the loop.

 Response:
    ScrollText[40], the global array that is used to store the output
 of the elevator position in the scroll bar, is not long enough to hold
 the generated string if the elevator is left at the bottom of the
 scroll bar.
    When the generated string overflows, it will overwrite the instance
 handle of the application. The application will crash when you bring
 out the About dialog box because the instance handle is no longer
 valid. Windows will attempt to write a fatal exit code to the AUX
 port. If you do not have a secondary monitor or terminal connected to
 your COM1: port for debugging purposes, you will get the message box
 and will need to reboot.
    To work around this problem, simply make the global array bigger.
 Using ScrollText[45] will solve this problem.


 366. EDITCNTL.EXE Sample Source Error Caused by Incorrect Parameter

 Problem:
    There is a problem in a sample Windows application that comes with
 the Windows Software Development Kit Version 2.03. When I am running
 EDITCNTL.EXE from the Sample Source Code disk in the
 \LEARNING\EDITCNTL directory, if I resize the top-level window, the
 scroll bars of the edit box will disappear. If I then try to click the
 area of the window where the scroll bars used to be (i.e., the bottom
 right), they will reappear bit by bit.

 Response:
    The MoveWindow() call in the WM_SIZE message of the window
 procedure needs to have TRUE as the last parameter. The original value
 is FALSE, which means that the window is not repainted after it is
 resized. This is why the scroll bars are not being repainted.


 367. Escape Codes Omitted from Version 2.03 Programmer's Reference

 The following ESCAPE codes were omitted from the Windows Version 2.03
 "Microsoft Windows Software Development Kit Programmer's Reference"
 manual:

 GETVECTORPENSIZE
 nCount = 4  [sizeof(LPPEN)]
 lpInData contains a FAR pointer to the pen
 lpOutData contains a long pointer to a POINT structure which
           contains the X and Y width and height of the pen.

 GETVECTORBRUSHSIZE
 nCount = 4  [sizeof(LPBRUSH)]
 lpInData contains a FAR pointer to the brush
 lpOutData contains a long pointer to a POINT structure which
           contains the X and Y width and height of the brush.

 SELECTPAPERSOURCE
 nCount = 2  [sizeof (int)]
 lpInData contains a long pointer to a short integer value
      0   Manual Feed
      1   Primary Paper Tray
      1<  Other Paper sources, device specific

 lpOutData contains a long pointer to a structure containing the
 following items:

       WORD  X, Y;            Paper size in DEVICE units
       RECT imageRect;        Rectangular region which can be printed on.
       WORD orientation;      1 = portrait, 2 = landscape


 368. C Run-Time Stream I/O Routines Under Windows

    The buffered I/O C run-time routines use malloc to allocate their
 buffers. This can cause problems in medium or large memory models. The
 following sequence can occur:

    1. Program calls fwrite.
    2. fwrite calls malloc(mapped to LocalAlloc).

    If the local heap is not large enough to satisfy the memory allocation
 and there is no room to expand the local heap at its current location,
 the following occurs:

    1. Internal memory manager calls GlobalCompact.
    2. Code segments get moved or discarded.
    3. Windows traces bp chain fixing up code addresses.
    4. Fixups fail when they reach the fwrite call because the function
 was not compiled with /Gw (hence incorrect bp linkage). This will
 cause a fatal exit under the debugging version of Windows.

    Generally the stream I/O routines do not gain your application
 anything under Windows. Low level I/O routines should be used in
 conjunction with OpenFile. The file should be opened, the information
 read, and the file closed so your application will not tie up system
 file handles.


 369. Opening Files from a Shared Library

 Question:
    Is it possible to open (create) an MS-DOS file from within a shared
 library?

 Response:
    Yes. For more information on this subject, refer to the DLL
 example in Chapter 18 of "Programming Windows" by Charles Petzold.
    The following program uses a DLL library to accept and store
 strings in a sorted array. When added to this source, this program
 will quickly move the strings to a file.
    Some changes are needed in other files to make this work (e.g.
 export it in the STRLIB.DEF, import it in the STRPROG.DEF, use it in
 the STRPROG.C).
    Please note that this is a rather terse example; some extra
 error-checking and recovery operations should be done in a "real"
 application.
    The following is an example of the program:

 HANDLE hFile;

 BOOL FAR PASCAL SaveStrings (lpFileName)
   LPSTR lpFileName;
   {
     OFSTRUCT    reOpenBuff;
     WORD        wStyle;
     int         strLength, wriLength, i;
     NPSTR       npString;

     hFile = OpenFile (lpFileName, &reOpenBuff, OF_CREATE | OF_WRITE);
     /* you should check hFile for success */
     for (i = 0; i < nTotal; i++) {
         npString  = LocalLock (hStrings [i]) ;
         strLength = lstrlen ((LPSTR)npString);
         wriLength = _lwrite (hFile, (LPSTR)npString, strLength);
         if (wriLength == strLength) {
             _lwrite (hFile, "\r\n", 2);
         } else {
           /* something went wrong, handle it here */
         }
         LocalUnlock (hStrings [i]) ;
     }
     _lclose (hFile);
     return TRUE;
   }


 370. Rectangles and Map Modes in Windows

 Question:

 If I draw a rectangle from (1,1) to (2,2), how many pixels high and
 wide is the rectangle (assuming MM_TEXT mapping mode)? How does map
 mode affect this call and other calls?

 Response:

 The quick answer to your first question is that it is a 1 x 1
 rectangle, i.e., it only changes the pixel at (1,1).

 For a general answer, assume we are in MM_TEXT mode. (We will discuss
 other modes later.) Rectangles are specified in coordinates describing
 the lines dividing the pixels. Hence, the height, width, and area of
 the rectangle are based on the difference of the rectangle
 coordinates. BitBlt extents give the number of pixels along the sides
 of the rectangle, as follows:

    Height = rect.right  - rect.left
    Width  = rect.bottom - rect.top

    Area = Height * Width

    For BitBlt:
    OrgX = rect.left
    OrgY = rect.top
    ExtentX = rect.right  - rect.left
    ExtentY = rect.bottom - rect.top

 The following diagram shows the pixels affected by the rectangle ((1,
 1), (5, 3)):

            0_____1_____2_____3_____4_____5_____6____
          0 |     |     |     |     |     |     |
            |     |     |     |     |     |     |
            |_____|_____|_____|_____|_____|_____|____
          1 |     |     |     |     |     |     |
            |     |  X  |  X  |  X  |  X  |     |
            |_____|_____|_____|_____|_____|_____|____
          2 |     |     |     |     |     |     |
            |     |  X  |  X  |  X  |  X  |     |
            |_____|_____|_____|_____|_____|_____|____
          3 |     |     |     |     |     |     |
            |     |     |     |     |     |     |
            |_____|_____|_____|_____|_____|_____|____
          4 |     |     |     |     |     |     |
            |     |     |     |     |     |     |

 GDI map modes determine the scaling and translation transformations
 from logical (user) units to device (screen) units. In GDI, transforms
 are of the following form:

    X' = (ViewportExtX * (X - WindowOrgX)) / WindowExtX + ViewportOrgX
    Y' = (ViewportExtY * (Y - WindowOrgY)) / WindowExtY + ViewportOrgY

 For MM_TEXT, they are of the following form:

    ViewportExtX = WindowExtX = ViewportExtY = WindowExtY = 1 and
    ViewportOrgX = WindowOrgX = ViewportOrgY = WindowOrgY = 0.

 For MM_ANISOTROPIC, the origins and extents can be set arbitrarily.
 For other modes, they are set to achieve the desired scale or aspect
 ratio. If you want to see how this works you can use
 GetWindow/ViewportExt/Org to query the values. Note that these will
 change from one device to another.

 When making a GDI output call, the coordinates and/or extents are
 transformed to pixels by LPtoDP, and the call is completed in pixels.
 The only complications are Bitblt and logical pens. In Bitblt, the
 extents are transformed on both the source and destination. If the
 results differ, a StretchBlt is performed. (This only applies to
 Windows Versions 2.x.) Pens are a special case since they only have one
 width. The width is transformed by the X-coordinate transform and used
 as the width in pixels for both X and Y.

 Note that when transforming extents, it is necessary to transform both
 (ExtX, ExtY) and (0, 0) and subtract (0, 0)' from (ExtX, ExtY)' to
 eliminate any translation in effect. In MM_TEXT mode, the transform
 has no effect since it is the identity transform.

 Please see Sections 2.3.4.x in the Windows Version 2.00 programmer's
 reference.


 371. Pop-ups and Focus

 Problem:
    I thought that dialog boxes by default were supposed to come up as
 APPLICATION MODAL. I have a windows application that creates several
 pop-up style windows--each of which has its own menu. There is a menu
 item that will create a dialog box to request information specific to
 that pop-up. Doing this, however, does not prevent me from clicking
 and activating the other pop-ups.

 Response:
    What the dialog box manager does is the following:

    1. It takes the passed value of hWndParent and determines if it is
 the parent. If it is not the parent, then it finds the parent.
    2. It then disables the parent window with the following function:

          EnableWindow(..., FALSE)

    This routine only makes dialogs work one level deep.
    The problem that you may have hit upon is that you are giving your
 pop-up windows a NULL parent. If this is the case, you need to call
 the EnableWindow(..., FALSE) function yourself for each of the pop-up
 windows with a NULL parent which you have created.


 372. Naming .EXE Files the Same as .FON Files

 Question:
    I have created a windows executable and named it SCRIPT.EXE.
 Windows Version 2.03 will not execute this file. However, simply by
 renaming to X.EXE, Windows will run it. Is SCRIPT a special word?
 It seems I cannot have a Windows program named SCRIPT.EXE.

 Response:
    You cannot name your executables the same name as font files. The
 .FON files themselves are .EXE files that get loaded when Windows is
 started.


 373. Keyboard Selection of Windows

 Question:
    Is there anything that I can do with a Windows Version 2.03 SDK that
 will allow keyboard selection of my popup windows?

 Response:
    To select your popup window with the keyboard, use the ALT-F6 key
 to switch between the popup window and its parent window. This is
 true only for a popup window with a parent. If the popup window does
 not have a parent, then the ALT-ESC key sequence will be make the
 popup window active. Use CTRL-F6 among siblings within a window.


 374. 0 dwBytes Parameter with GlobalReAlloc

 Problem:
    Page 330 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual states the following about the
 GlobalReAlloc function:

    "If dwBytes is Zero, this flag causes a previously moveable object to
 be discarded..."

     In reality, the object needs to be previously allocated with
 GMEM_MOVEABLE| GMEM_DISCARDABLE so that it will be discarded.

 Response:
    This is a documentation error. This passage should state the following:

    "If dwBytes is Zero, this flag causes an object previously allocated
 as moveable and discardable to be discarded..."


 375. Getting List Box to Not Sort

    To get the list box to not sort, you should not use the LBS_STANDARD
 style. You should list the styles that you want and not include the
 LBS_SORT style bit.


 376. Ending a Session from an Application

 Question:

 In my application, I want to properly end the current Windows session.
 To do this, I send the WM_QUERYENDSESSION to all active windows to
 allow them to perform a function, or to refuse to exit.

 I have set up this process by using a call to SendMessage() from
 within a function that is being called through EnumWindows(). This
 seems to work, but many of the message boxes that come up do not
 accept mouse input, and must be answered with the keyboard only. How
 can I get the message boxes to properly handle mouse input, or is
 there a better way to submit the WM_QUERYENDSESSION message?

 Response:

 The problem with the message boxes not allowing mouse input is related
 to the SendMessage() function. Using the PostMessage() function
 corrects the problem of the message boxes not working with the mouse.
 However, this process will not return the result of the query to your
 procedure. Because of this, you will be unable to keep track of any
 applications that refuse to exit.

 Since the MS-DOS Executive is already performing this function, the
 best way for you to properly force the shutdown of Windows is to ask
 the MS-DOS Executive to do it for you. This can be done by enumerating
 the windows, finding the MS-DOS Executive, and then posting to this
 window the SC_CLOSE message.

 Another way to do this, would be to use the 'ExitWindows' command.
 The main difference with this command, is that it will not send your
 window a WM_QUERYENDSESSION. Rather it will assume that since your
 application is asking to end the windows session, that it has
 perfromed any 'QueryEndSession' processing that it would be needing.

 There is a sample program in the Software Library that demonstrates
 three different ways of performing this function. This file can be
 found in the Software Library by searching on the filename
 KAMAKAZI.ARC, the Q number of this article, or S12109.


 377. GlobalWire() and GlobalUnWire() and Necessary Input Parameters

 Question:
    I am confused after reading the documentation for GlobalWire and
 GlobalUnWire, over what the necessary input parameters are.
    For these two calls, the documentation describes the hMem variable
 as follows:

    "Identifies the segment that will be moved and locked"

    All other functions that use a hMem refer to GlobalWire and
 GlobalUnWire as follows:

    "Identifies the global memory block"

    Does this mean that GlobalWire and GlobalUnWire need a Segment
 address?

 Response:
    GlobalWire and GlobalUnWire are newer functions. This probably is
 the cause of the variation in description of the hMem parameter. Both
 functions require a 'HANDLE' parameter, as do GlobalLock,
 GlobalUnlock, GlobalSize, and all the other Global Memory functions
 that utilize 'hMem' values. An example of code that uses GlobalWire
 and GlobalUnWire can be seen below:

 /*
  * GlobalWire
  * globwire.c, jeffst, v1.00, 21-Sept-1987
  *               roberth, v1.05, 13-June-1988
  *
  * The following program demonstrates the use of the GlobalWire
  * function. The GlobalWire function locks a block into low memory.
  * GlobalWire is called from WinMain in this sample application.
  * GlobalWire is often used when a block is going to be locked in
  * memory for a long time so there will be larger continuous free
  * spaces than if GlobalLock was used. Before allocation and after
  * GlobalWire'ing, GlobalCompact is called to show the affect of
  * locking the block in memory has on the largest continuous block
  *
  * Microsoft Product Support Services
  * Windows Version 2.00 function demonstration application
  * Copyright (c) Microsoft 1987
  *
  */

 #include "windows.h"

 int sprintf();
 LPSTR FAR PASCAL lstrcpy (LPSTR, LPSTR);

 typedef struct {
                 char smallstruct[20];
                } SMALLSTRUCT;         /* structure we're going to allocate */
                                                 /*   space for    */
 typedef SMALLSTRUCT far *lpSMALLPtr;  /* pointer to SMALLSTRUCT structure */

 int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
 HANDLE hInstance, hPrevInstance;
 LPSTR lpszCmdLine;
 int cmdShow;
 {
     HANDLE         hMemBlock;        /* Handle to memory block   */
     lpSMALLPtr     ThisPtr;          /* Pointer to myStruct      */
     char           szBuff[80];       /* buffer for output        */
     DWORD          ContigFreeBytes;  /* return value from GlobalCompact */

 /* Allocate space for SMALLSTRUCT in global heap */
     hMemBlock = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE,
                     (long)sizeof(SMALLSTRUCT));
 /* if memory allocated properly */
     if (hMemBlock != NULL)
         /* GlobalWire the block into low memory */

         ThisPtr = (lpSMALLPtr)GlobalLock (hMemBlock);
         lstrcpy (ThisPtr->smallstruct, (LPSTR)"This is a test\000");
         GlobalUnlock (hMemBlock);

         ThisPtr = (lpSMALLPtr)GlobalWire(hMemBlock);
         if (ThisPtr == NULL)
             MessageBox(NULL,(LPSTR)"GlobalWire Failure",
                             (LPSTR)" ",MB_OK);
         else
             MessageBox(NULL,(LPSTR)"GlobalWire worked properly",
                             (LPSTR)ThisPtr->smallstruct,MB_OK);

 /* GlobalUnwire the block */
     GlobalUnWire(hMemBlock);

 /* Free the block */
     GlobalFree(hMemBlock);

     return 0;
 }


 378. LINK4 Uses TMP Environment Variable to Make Temporary Variable

    LINK4 will use the directory specified by the TMP environment
 variable for the purpose of creating a temporary file. For example, if
 the TMP environment variable was set to C:\TEMP, then LINK4 would put
 the temporary file in C:\TEMP.
    If there is no TMP environment variable, or if the directory
 specified by TMP does not exist, LINK4 will put the temporary file in
 the current working directory.
    If you want to direct LINK4 temporary files to C:\TEMP, for
 example, you could place the following command in your AUTOEXEC.BAT
 file, or enter the command from the DOS prompt:

    SET TMP=C:\TEMP


 379. Font Selection when Creating a Font

 Question:

 In what order are the parameters to CreateFont and CreateFontIndirect
 considered?

 Response:

 Within the font creating routines, each font is assigned a penalty for
 each element of the description that does not match. Penalties are
 weighted, with some penalties so heavy that any font matching the
 corresponding element will be chosen before fonts that do not match
 the element. Penalties are assessed as indicated below, with no
 penalty incurred if DEFAULT is listed in the description (with two
 exceptions as noted):

 Notes

     Description Element             Penalty for Not Matching

     Character Set                   75000
     Fixed Pitch requested           15000
     Variable Pitch requested          350
     Face Name                       10000
     Family                           9000
     Vertical size                     150 * (size difference after scaling)
      (request larger than font)      + 20 * (integer scaling factor)
     Vertical size                     150 * (size difference)
      (request smaller than font)    + 600
  1. Width size                         20 * (integer scaling factor)
                                      + 50 * (rounding after scaling)
     If any scaling was performed       50
     If scaling of height and width    400 * vert. scale/horz. scale OR
       are not the same                400 * horz. scale/vert. scale,
                                             whichever is greater
  2. Weight Difference                   3 * weight difference / 10
     Italic                              4
     Underline                           3
     Strikeout                           3

 Please note the following:

 1. If no width is requested, a width is calculated based on the aspect
    ratio of the device.

 2. If no weight is requested, the weight penalty is 3 * (weight
    difference from 400) / 20.

 If the calculated penalty from the above sequence is identical for two
 fonts, the following penalties are considered (listed here from most
 to least severe):

 Italic synthesis performed
 Bold synthesis performed
 Strikeout synthesis performed
 Underline synthesis performed
 Vertical Scaling performed
 Horizontal Scaling performed


 380. Clipboard and the WM_RENDERFORMAT Message

 Question:
    I can find no return codes for the WM_RENDERFORMAT message that
 would specify a failure to supply the requested data. Is there a way
 to inform anyone that I cannot supply the data? When I terminate my
 application, should the TIFF object remain in memory?

 Response:
    If you are not able to render the data, pass back a NULL handle.
 You can try this procedure with Excel and Paint and watch the message
 traffic with the SPY application. If the bitmap is too big, then Excel
 will pass back a NULL handle.
    Because most applications provide at least the CF_TEXT with real
 data (not rendered), these applications know that they can provide
 that amount of information.
    Since you are using the normal GlobalAlloc calls, you do have the
 option to discard and compress memory.
    If the problem still exists, you also can provide the information
 to a file. However, this process has to be set up by the two
 applications so they know that the information is on a disk and what
 the filename and format are.
    CF_TIFF is not a format that the clipboard program supports by
 itself. It requires the owner of the format to be running.


 381. WM_SYNCTASK, WM_SYNCPAINT Messages Should Not Be in WINDOWS.H

    WM_SYNCTASK and WM_SYNCPAINT are two messages defined in the
 WINDOWS.H file, but not documented in the reference manuals.
    These messages are used internally by Windows for painting, and
 should not be included in WINDOWS.H.


 382. Creating Small _TEXT Segments

 Problem:
    I am trying to make the code segments for my application as small
 as possible. However, I still have a large _TEXT segment.

 Response:
    In order to make a _TEXT segment as small as possible, you should
 compile for a medium model, using the /NT compile option to rename
 your text segments. This process moves all of the code for this object
 file to a new text segment, except the code for the C run-time
 libraries, and the start-up code. Keeping your source files small, and
 compiling them separately using the /NT compile switch for each
 compilation, will keep the _TEXT segment small.
    If you want the _TEXT segment even smaller, you could purchase the
 C run-time source files and recompile the run-time library routines
 you need using the /NT compile switch. If you are interested in
 purchasing the source code, contact Microsoft Customer Service at
 (206) 882-8088. Please note there is no support for assistance in
 altering this code.


 383. Difference between Active Window and Focus Window

 Question:
    What is the difference between the active window and the focus
 window?

 Response:
    There is a slight difference between an active window and a window
 with input focus. Generally, if a window is active, either the active
 window or one of its descendants should have the focus.
    The differences can be seen when an overlapped window owns a child
 window that accepts keyboard input. The system will draw the caption
 of the active window in the active window color. The window drawn with
 the active color is normally the overlapped window. The window with
 the focus will contain the system caret or have current keyboard
 focus, such as an edit-control child of the overlapped window.
    The PIFEDIT utility can be used to demonstrate the active/focus
 difference. By selecting the Program Name edit-control child, Windows
 will activate the PIFEDIT overlapped window and the system caret will
 appear in the Program Name edit-control child. At the time the PIFEDIT
 overlapped window becomes active, the window procedure for that window
 sets the keyboard focus to the edit control.


 384. Defining New Control Classes

 Question:
    I understand that it is possible to define and register a new
 control class, e.g I would like to use this to create a kind of
 numeric entry field.
    There does not seem to be much information in the documentation
 about how to define and register a new control class. In particular, I
 need to know the following:

    1. What should the window procedure look like for a control, i.e.,
 what messages should it expect and what notification messages should
 it send?
    2. How do I register a new control class? Presumably, I would use
 RegisterClass, but I think the window procedure should be defined in a
 DLL, not an EXE. Are there any other applicable rules?

 Response:
    You can add additional control classes to the system. The window
 procedure for the new control should be designed exactly the same as
 any other window procedure. This means that the window procedure
 should handle those messages that pertain to that control class and
 should send notification messages to the control window parent when
 pertinent things have happened to that control (e.g. a list box
 control sending an LN_SELECT notification message when a selection has
 occurred).
    To register a public class, the registration must occur in a DLL,
 usually during initialization of the DLL. The windows procedure must
 appear in the DLL and be exported.


 385. Changing the Mouse Cursor for a Window

 Problem:
    When I wish to change the mouse cursor in one instance of my
 application, it changes in all of them because the cursor shape is
 stored in the window class, rather than in the window itself.

 Response:
    The only ways to change the cursor are to change the window class
 owning the mouse cursor, or to use the SetCursor() function.
    If you want to change the mouse cursor for a particular window
 rather than for the class of windows, use the SetCursor() function
 each time that window receives a WM_SETCURSOR message.


 386. Passing GDI Objects through DDE

 Question:
    How can I send GDI bitmaps between applications using DDE? I want
 to send the object, not a copy of the bits making up the object.

 Response:
    Note that GDI objects are local to GDI, so the handle can be shared
 directly. Creating a compatible memory bitmap and passing the
 resulting GDI object handle will be sufficient.
    The following code can be used to create a memory bitmap that
 can be passed to another application through DDE:

 HBITMAP MakeDDEBitmap(hWnd)    /* make a bitmap compatible with display */
 HWND hWnd;
 {
 HDC      hDC,hMemDC;
 HBITMAP  hMemBitmap;

 hDC = GetDC(hWnd);       /* get a display DC */
 hMemDC = CreateCompatibleDC(hDC); /* create DC compatible with display */
 hMemBitmap = CreateCompatibleBitmap(hDC,50,50); /* compatible bitmap */
 hMemBitmap = SelectObject(hMemDC,hMemBitmap); /* associate bitmap with DC */

 Rectangle(hMemDC,0,0,50,50);  /* perform output to bitmap */
 MoveTo(hMemDC,5,5);
 LineTo(hMemDC,40,40);

 hMemBitmap = SelectObject(hMemDC,hMemBitmap); /* disassociate bitmap */

 DeleteDC(hMemDC);   /* destroy compatible DC */
 ReleaseDC(hWnd,hDC); /* release display DC */
 return(hMemBitmap);  /* return bitmap handle */
 }

    Either application should use the appropriate GDI function calls to
 delete these bitmaps when the bitmaps are no longer needed. The
 release data flag should be used to determine which application
 deletes the object.


 387. Executable File-Format Corrections

    The executable file-format information in the "Microsoft Windows
 Software Development Kit Programmer's Reference Guide" is incorrect.
 There are documentation errors in the New-Style Header Information
 Block (Pages 646-648). The description of the Resource Table (Pages
 649-650) is also very confusing. Both structures are defined below.

    The following is the new EXE structure:

 struct new_exe                        /* New .EXE header */
   {
     unsigned short int  ne_magic;     /* Magic number NE_MAGIC */
     char                ne_ver;       /* Version number */
     char                ne_rev;       /* Revision number */
     unsigned short int  ne_enttab;    /* Offset of Entry Table */
     unsigned short int  ne_cbenttab;  /* Number of bytes in Entry Table */
     long                ne_crc;       /* Checksum of whole file */
     unsigned short int  ne_flags;     /* Flag word */
     unsigned short int  ne_autodata;  /* Automatic data segment number */
     unsigned short int  ne_heap;      /* Initial heap allocation */
     unsigned short int  ne_stack;     /* Initial stack allocation */
     long                ne_csip;      /* Initial CS:IP setting */
     long                ne_sssp;      /* Initial SS:SP setting */
     unsigned short int  ne_cseg;      /* Count of file segments */
     unsigned short int  ne_cmod;      /* Entries in Module Reference Table */
     unsigned short int  ne_cbnrestab; /* Size of nonresident name table */
     unsigned short int  ne_segtab;    /* Offset of Segment Table */
     unsigned short int  ne_rsrctab;   /* Offset of Resource Table */
     unsigned short int  ne_restab;    /* Offset of Resident Name table */
     unsigned short int  ne_modtab;    /* Offset of Module Reference Table */
     unsigned short int  ne_imptab;    /* Offset of Imported Names Table */
     long                ne_nrestab;   /* Offset of Nonresident Names Table */
     unsigned short int  ne_cmovent;   /* Count of movable entries */
     unsigned short int  ne_align;     /* Segment alignment shift count */
     unsigned short int  ne_cres;      /* Count of resource segments */
     unsigned short int  reserved[5];  /* reserve words for future reference *
   };

    The resource table is built from two structures. The first
 structure, res_info, defines each resource contained in the file. The
 second, res_type, defines each type of resources. The entire table is
 built as follows:

 struct  {
         WORD     id;  /* As documented (i.e., 0x8000 is or'd to res. type) */
         WORD     count; /* number of resources of this type */
       LONG     reserved;
         } res_type;

 struct  {
         WORD     offset; /* offset to res. data (shifted by alignment ) */
         WORD     lenght; /* length of res. data (shifted by alignment ) */
         WORD     flags;  /* mem. managment flags */
         WORD     id;     /* identifier of resource */
         WORD     reserved1;
         WORD     reserved2;
         } res_info;

    Therefore, the format of the resource table is as follows:

 WORD  alignment; /* shift res_info.offset and length left by this amount */
 struct res_type; /* first resource type */
    struct res_info; /* there will be (res_type.count) res_info structures */
     ...
 struct res_type; /* second resource type */
    struct res_info; /* there will be (res_type.count) res_info structures */
     ...

 ...

 struct res_type; /* Nth resource type */
    struct res_info; /* there will be (res_type.count) res_info structures */
     ...

 WORD 0;  /* zero indicates end of resource table */


 388. List Box Manager Not Releasing Memory

 Problem:
    I have found a problem with the behavior of the list box manager.
 If I create a list box, fill it with data and then keep deleting and
 inserting the first item of the list box, I find the list box
 procedure does not free up the memory of the items that I have
 deleted.

 Response:
    Microsoft has confirmed this to be a problem in Version 2.03.
    The list box manager will call GlobalRealloc() as needed to enlarge
 the area needed to hold strings. The problem occurs when the strings
 are to be deleted. The global area is not reclaimed using
 GlobalRealloc(); eventually the 64K segment limit is reached, and the
 list box manager returns error codes.
    There is no direct workaround to this problem. You could monitor
 the number of characters added to the list box and purge the list box
 when the count exceeds 50K.
    Microsoft is researching this problem and will post new information
 as it becomes available.


 389. Floating-Point Exceptions and Win87EM

 Exception handling routines may not be CALLed correctly from the
 Win87EM library. If a coprocessor is installed and a Windows
 application uses the math libraries, the application will never see
 exceptions. The first assumption is that all coprocessor instructions
 will be expressed as interrupts that will get to the Win87EM DLL.

 If there is no coprocessor present, the pseudo-instructions will not
 be modified; however, the pseudo-FWAITS will be turned into NOPs. If
 there is a coprocessor present, the pseudo-instructions (except for
 FWAIT) will be changed into real instructions.

 The FWAITS are handled specially to get the exceptions to the
 application. Before an application can use coprocessor instructions,
 it must make an INIT CALL to the DLL to do initialization and set an
 exception handler. This exception handler is CALLed whenever an
 exception occurs; generally exceptions are generated when an FWAIT is
 executed.

 The sequence is as follows:

 1. An application performs a pseudo-FWAIT.

 2. The pseudo-FWAIT goes to the DLL.

 3. An exception gets raised.

 4. The DLL vectors the exception off to the handler.

 5. The DLL will NOT vector the exception if it did not do the FWAIT.

 A problem occurs because the first assumption is incorrect. Because
 Windows uses a new style EXE header, the compiler puts in the correct
 coprocessor instructions. The loader will change these instructions
 into pseudo-instructions if there is no coprocessor present. If a
 coprocessor is present, the DLL never sees a pseudo-FWAIT, so it will
 never get to vector an exception if it occurs. Because of the nature
 of divide-by-zero errors, this exception is the only one that occurs.

 Although the system will not crash, you will not receive exceptions
 other than divide by zero.

 Microsoft is researching this problem and will post new information as
 it becomes available.

 Please note that Windows does not recognize the 80387 chip, i.e., the
 problem does not appear on 386 machines, and exceptions are generated.


 390. InitAtomTable() Atom-Hash Table Size

    The description of the InitAtomTable() on Page 338 of the "Microsoft
 Windows Software Development Kit Programmer's Reference" incorrectly
 states that the atom-hash table size is set to 37 by default, limiting
 the maximum number of atoms to 37.
    The default size is 37, but it does not limit the maximum number of
 atoms. The size parameter to InitAtomTable() sets the size of the hash
 table; however, because each table entry points to a linked list of
 atomized strings, the size does not affect the maximum number of
 atoms.

    As strings are atomized, they are assigned to a "bucket" by a
 repeatable algorithm based on the body of the string. If more than one
 string is assigned to a single bucket, it is linked onto a list. This
 process gives better performance than a single linked list because the
 length of the list needing to be searched for a lookup is reduced. It
 also prevents the maximum number of atoms from being limited by the
 capacity of some fixed-size array.
    InitAtomTable() is used to change the number of buckets. If an
 application will be using large numbers of atoms, it can reduce
 Add/FindAtom() search time by increasing the number of buckets. This
 comes with a cost of increased memory usage.
    Reasonable hash table sizes prevent the linked chains from
 exceeding about 10 to 20 items each. They also should be prime, so
 that the distribution among the buckets will be fairly even. This
 information implies that nSize should be a prime number, as follows:

         nSize =  (estimated number of atoms in the table)  /  (10 or 20)

    The global atom manager's hash table cannot be changed from its
 default size of 37.



 391. Allocating and Using Class and Window Extra Bytes

 The WNDCLASS structure contains two fields named cbClassExtra and
 cbWndExtra, which may be used to specify a number of additional bytes
 of memory to be allocated to the class structure itself or to windows
 created using that class.

 Class Extra Bytes

 For instance, setting cbClsExtra=4 will cause four extra bytes to be
 added to the end of the class structure when the class is registered.
 This memory is accessible by all windows of that class. The number of
 additional bytes allocated to a window's class may be retrieved by a
 call to the GetClassWord() function, as follows:

 nClassExtraBytes = GetClassWord(hWnd, GCW_CBCLSEXTRA);

 The additional memory may be accessed one word at a time by specifying
 an offset, in BYTES (starting at 0), as the nIndex parameter in calls
 to GetClassWord(). Alternatively, these values can be set with
 SetClassWord(). Keep in mind that even though the functions get/set a
 word, the offset is in bytes, so that the function call
 "SetWindowWord(hWnd, 3, SOME_VALUE);" sets a word starting at the
 fourth BYTE, not the fourth word.

 Window Extra Bytes

 Assigning a value to cbWndExtra will cause additional memory to be
 allocated to every window of the class. If, for instance, cbWndExtra
 is set to 100, every window created using that class will have one
 hundred extra bytes automatically allocated to it. This memory is
 accessible only by using a handle to the window and calling
 GetClassWord(). Also, these values can be set by calling
 SetClassWord().

 A good use for these window extra bytes is an editor that will have a
 variable number of files open, each one displayed in its own window.
 The file handles and other window-specific variables can be stored in
 the window extra bytes of each window. The application programmer is
 therefore absolved of having to keep a linked list of variables that
 is modified every time a window is created or destroyed.

 The additional window memory may be accessed, again one word at a
 time, by specifying a byte offset as the nIndex parameter in calls to
 GetWindowWord() and SetWindowWord(). For example, the following line

 nWindowDataWord = GetWindowWord(hWnd, 8);

 will return the value of the fifth word of additional window memory.


 392. GetMessage() Documentation Is Incorrect

 The description of hWnd is incomplete in the second paragraph of the
 description of GetMessage() and in the parameters section on Page 285
 of the "Microsoft Windows Software Development Kit Programmer's
 Reference" for Version 2.00.

 The hWnd descriptions should be changed as follows:

    GetMessage() retrieves only messages associated with the window
    specified by the hWnd parameter, or any of its children as
    specified by IsChild(), and within the range of message values
    given by the wMsgFilterMin and wMsgFilterMax parameters. If hWnd is
    NULL, GetMessage() retrieves messages for any window that belongs
    to the application making the call. (The GetMessage() function does
    not retrieve messages for windows that belong to other
    applications.) If hWnd is -1, GetMessage() returns only messages
    with a hWnd of NULL as posted by PostAppMessage(). If wMsgFilterMin
    and wMsgFilterMax are both zero, GetMessage() returns all available
    messages (no range filtering is performed).

    hWnd     HWND    Identifies the window and children whose messages
             are to be examined. If hWnd is NULL, GetMessage() retrieves
             messages for any window that belongs to the application
             making the call. If hWnd is -1, GetMessage() returns only
             messages with a hWnd of zero as posted by PostAppMessage().


 393. Spooler Limit of 20 Print Jobs

 Question:
    We have an application that will allow the user to process data and
 generate reports and graphics in a batch mode of operation. Currently,
 the spooler has a limit of 20 jobs at one time. How can we tell, at
 the application level, when we have reached this limit?

 Response:
    We have added the WM_SPOOLERSTATUS message for broadcasting this
 message from the spooler. Under this, if wParam is PR_JOBSTATUS,
 lParam will indicate the number of jobs remaining in the spooler. This
 message is sent whenever an ENDJOB message is processed (job printing
 is finished).
    The following is some sample WndProc code demonstrating how to use
 it:

     case WM_SPOOLERSTATUS:
         if (wParam == PR_JOBSTATUS) {  /* PR_JOBSTATUS == 0 */
            itoa ( LOWORD(lParam), buff, 5);
            MessageBox( hWnd, (LPSTR)buff,
                (LPSTR)"Number of Jobs Remaining", MB_OK);
         }
         else {
             itoa ( wParam, buff, 5);
             MessageBox( hWnd, (LPSTR)buff,
                 (LPSTR)"Unknown Spooler Message", MB_OK);
         }
         break;


 394. MAPSYM 3.10 Truncating Line-Number Information

 Problem:
    I am getting an incorrect entry point (Program entry point at 0000:
 0000) for .SYM files and the map-file information (the line numbers)
 is being truncated. I am using Version 3.10 of the MAPSYM utility that
 came with the Windows Development Toolkit.
    I am also using close to 100 segments and generating more than 1000
 line numbers.

 Response:
    Version 3.10 of MAPSYM has a limitation of 10,000 symbols and 150
 segments. Additionally, if a pathname and segment name line (in the
 map-file line numbering) is greater than 80 characters, the
 line-number information is truncated.
    To correct this problem, shorten this line to less than 80
 characters. For example, change the following line

    Line numbers for D:\TESTER\C\FOOBAR.OBJ(d:\tester\c\foobar.c) segment
 foobar_TEXT

 to the following:

    Line numbers for D:\FOOBAR.OBJ(d:\foobar.c) segment foobar_TEXT


 395. Initializing DLLs

 Question:

 How do I initialize a DLL to have a movable DS and save the hInstance
 so that I can load resources (such as an icon or cursor) and create
 dialog boxes from within the DLL

 Response:

 To use local allocation, a DLL must define an entry point that will be
 called when the DLL is loaded. In this entry point, it must call
 LocalInit() to initialize the local heap. LocalInit() has the side
 effect of leaving the DS locked. This makes a simple initialization
 possible, but makes it impossible for other applications to move the
 DLL's DS when they call GlobalAlloc(). This makes the GlobalAlloc()s
 more likely to fail.

 To be a well-behaved module, the DLL should leave its DS unlocked.
 This requires some additional effort on the part of the DLL, but isn't
 too difficult. The idea is to unlock the DS when control leaves
 initialization, and lock/unlock it around all of the DLL's entry
 points. This is accomplished by calling UnlockSegment() before leaving
 the initialization routine, and calling LockData() and UnlockData() as
 control enters and leaves each entry point. If the DS isn't locked
 inside of each entry point, any far call or allocation could move it
 and invalidate any far pointers. This is illustrated in the sample
 code included below.

 Note
 ----

 It isn't necessary to use MakeProcInstance() on dialog and call-back
 functions whose code is contained in a DLL. However, the functions
 must be EXPORTed to be accessible outside of the DLL, such as from any
 application or Windows routine. Using MakeProcInstance() in this case
 only consumes extra system resources and produces slower code. In some
 cases, it can also cause Fatal Exits.

 Since a DLL has only one DS (instance) under Windows, the standard
 thunk created by the EXPORT line can set up the DS, without knowing
 any instance information.

 Code Examples
 -------------

 The following samples are from the UTILSLIB sample code:

 START libinita.asm --------------------------------------------------------

 ; LIBINITA.ASM - - - Define entry point and perform initialization
 ;                    for libraries that have their own data segments.
 ;

         TITLE   LIBINITA

 ?PLM = 1
 ?WIN = 1
 memM = 1

 .xlist
 include cmacros.inc
 .list

 externFP    <LocalInit>
 externFP    <UnlockSegment>

 externNP    <LibInitC>  ; Since this is NEAR it must be in the same segment.

 ;
 ;   CX = size of the heap as defined in the .def file.
 ;
 ;   DI = "Instance handle". This is the C hInstance passed to WinMain.
 ;
 ;        This is a handle to the global object containing the DS if there
 ;        is one. If there is no DS for a library, it is the module handle,
 ;        which is also a pointer to the module since that's a fixed global
 ;        object.
 ;
 ;        NOTE: The meaning and contents of hInstance are undocumented and
 ;           should not be relied upon. That is, you may assume that the
 ;           value in DI may be passed to any routine expecting hInstance.
 ;           Making any other assumptions is VERY dangerous since the
 ;           contents of hInstance may change in future versions.
 ;
 ;        NOTE: For a Windows library, hModule is interchangeable with
 ;           hInstance.
 ;
 ;   DS = data segment for our heap.
 ;
 ;   ES:SI = pointer to the command line.
 ;

 _INIT SEGMENT BYTE PUBLIC   'CODE'

 assume CS: _INIT

 assumes DS,NOTHING
 assumes ES,NOTHING

 cProc   LibInitA, <FAR, PUBLIC, NODATA>, <si,di>
 cBegin

     xor     ax,ax                   ; Return failure if there is no heap.
     jcxz    ourexit
     cCall   LocalInit,<ds,ax,cx>    ; Set up our DS for doing LocalAllocs.
                                     ; Note that LocalInit leaves DS locked.

     or      ax,ax
     jz      ourexit

     cCall   LibInitC,<di>           ; Do any C initialization.
                                     ; di = hInstance since we have a DS.

     push    ax                      ; Save the return value.

     mov     ax,-1
     cCall   UnlockSegment,<ax>      ; NOTE that we leave DS unlocked.
                                     ; This implies that we must use
                                     ; Lock/UnlockData as we enter and
                                     ; leave any routines which accesses
                                     ; our data segment.
                                     ; Leaving our DS unlocked when we're
                                     ; not using it makes life easier on the
                                     ; global memory manager so that other
                                     ; folk's GlobalAlloc()s are less likely
                                     ; to fail.

     pop     bx

     or      ax,ax                   ; Check if either one failed.
     jz      ourexit
     or      bx,bx
     jnz     ourexit
     xor     ax,ax

 ourexit:

 cEnd

 _INIT ENDS

 end LibInitA

 END libinita.asm ----------------------------------------------------------

 START libinitc.c ----------------------------------------------------------

 /*
  * C initialization called from the asm entry point initialization.
  *
  * We require that the library have exactly one DS. See libinita.asm.
  * The DS can (should) be movable.
  *
  * We assume the DS is locked by libinita.asm.
  *
  * Since this routine is NEAR, it must be in the same segment as libinita.asm
  *
  */

 int NEAR PASCAL LibInitC(hInst)
 HANDLE          hInst;
 {

     /* We need the instance for loading resources, creating dialogs, etc. */
     /* We save it in a global in our DS. */

     hInstance = hInst;

     /* Any other global data can also be initialized at this point. */

     pAbortList = NULL;

     pNonameMess = "Noname";

     return (1);
 }

 END libinitc.c ------------------------------------------------------------

 START version.c -----------------------------------------------------------

 /*
  * UtilVersion returns version information about the library.
  * A return of -1L indicates an error.
  */

 DWORD FAR PASCAL UtilVersion(Request)
 DWORD           Request;    /* Reserved, must be 0L for now. */
 {
 DWORD           Result;

     /* Lock down our DS so we can access it. */
     LockData(0);

     if (Request != 0L) {
         /* We don't implement specific requests yet. */
         Result = (DWORD)~(0L);
         }
     else {
         Result = UL_VERSION;
         }

     /* Unlock our DS to make life easier on the global memory manager. */
     UnlockData(0);

     return (Result);
 }

 END version.c -------------------------------------------------------------


 396. Windows SDK: Polyline() Not Drawing Line

 Problem:

 I am using MM_ANISOTROPIC mapping mode and am passing an array of
 points to Polyline(). When I zoom in on a specific region of the line
 using calls to SetWindowOrg() and SetWindowExt(), the line disappears
 entirely if I zoom in close enough. The drawing is performed
 successfully when I make explicit calls to MoveTo() and LineTo()
 throughout the array, using a FOR loop.

 Response:

 When coordinate transforms are performed on points that will fall
 outside of all device space (beyond the 32K limit, positive or
 negative), the point is placed at the boundary that it would have
 exceeded. For the positive case, this is 0x7FFF. Due to the
 inclusive/exclusive clipping-region design, when a clipping rectangle
 for this drawing is defined, the bottom and right ends are
 incremented. The positive values are incremented to 0x8000, a negative
 value. Recognizing that the clipping rectangle now defined is an empty
 region, no drawing is performed by Polyline().

 Due to this error in Polyline(), when an application uses a mapping
 mode other than MM_TEXT for drawing, the application must make certain
 that the points it requests to be converted do not fall out of device
 space. This situation is easily maintained by setting point limits at
 the time the application sets its window and viewport origin and
 extent. As a result, to check that its points are inside device space
 demands only that the application fall in the rectangle it has
 established by performing the transform from -32K and 32K in device
 space to logical space. Segments entirely inside this area can be sent
 to Polyline().

 The same information applies to a call to TextOut(), although the
 error is caused by a problem in the device driver rather than in GDI.
 If the point being passed falls outside of device space on systems
 using the EGA or VGA drivers, the vertical bands will appear. The same
 check should be performed, and TextOut() should be called only if the
 point passed remains inside device space.


 397. Return Value of LocalUnlock() Windows Function

 Problem:

 In the retail version of Windows, the LocalUnlock() function always
 returns 0. With the debugging version, it returns 0 if the lock count
 after the call is greater than or equal to 1. It returns nonzero if
 the lock count is 0 after the call.

 The two versions should be consistent in their return and consistent
 with the documentation, which does not agree with either case.

 Response:

 The only reliable way to know if the memory is locked before calling
 LocalUnlock() is to call LocalFlags() and use the LMEM_LOCKCOUNT mask
 on the return.


 398. MENUITEM HELP Option

    The HELP option for a MENUITEM is discussed on Page 38 of the
 "Programming Tools, Application Style Guide." The description states
 that this option will cause the item to be placed flush right on the
 menu bar, with a vertical separator to its left. However, the HELP
 menu option only places a vertical bar to the left of the given item;
 the item is not placed flush right.
    This is an error in the documentation. To place the item flush
 right on the menu bar, place an "\a" at the beginning of the text,
 e.g. "\aHelp". The \a will place the item flush right.


 399. Adjusting Mouse Acceleration within a Driver

 Question:
    Is there any way to turn off mouse acceleration from my driver? I
 am writing an absolute mouse driver, so acceleration must not be on.

 Response:
    To turn off mouse acceleration from your driver, set the two
 threshold entries in the MOUSEINFO structure to zero.


 400. Dialog Editor Saves Changes in .DLG Files

    The DIALOG.EXE program included with the Windows SDK saves all of
 its information on dialog boxes to two different files: the .RES and
 .DLG files. It stores a compiled resource in the .RES file and it
 stores the resource script that defines the dialog box(es) in the .DLG
 file. If the dialog definition is in the .RC file, the changes appear
 to be lost the next time the .RC file is compiled. This is because the
 dialog editor is writing the changed resource script to the .DLG file,
 and the .RC file is being compiled.
    To avoid this problem, you must save the dialog box to a file with
 a .DLG. extension. This file then must be included in the .RC file
 using the RCINCLUDE command. In this way, when the resource file is
 later compiled (after editing a menu or a stringtable, for instance),
 the changes made to dialog boxes with the dialog editor will be
 recompiled. The following is an example of how to include the file
 into the .RC file:

    RCINCLUDE DIALOG.DLG

    This new file will include the text from DIALOG.DLG, which contains
 all of the dialog box's information. Each time the .RC file is
 recompiled, the .DLG file will be read in and compiled into the .RES
 file.


 401. EnableWindow: Graying Dialog Box Controls

    The text displayed with most Dialog Box controls can be grayed
 through use of EnableWindow, as follows:

    hControl=GetDlgItem(hDlg, nIDControl);
    EnableWindow(hControl, FALSE);

    This code will disable the control with the ID number "nIDControl".
 The handle to the dialog box or window with the control is "hDlg".
 GetDlgItem returns a handle to the control specified by the
 "nIDControl" parameter in the dialog box with the handle "hDlg".
    A disabled control will not respond to any user input. Any text
 associated with the following controls is grayed:

    1. Check boxes
    2. Radio buttons
    3. Push buttons
    4. Static text
    5. Group boxes
    6. Auto Check boxes
    7. List boxes (entire list is grayed and scrolling is disabled)

    The following controls are disabled but are not grayed:

    1. Edit controls (text is not grayed)
    2. Scroll bars
    3. Static frames
    4. Static boxes


 402. Color Bitmaps and the Resource Compiler

 Problem:
    I am having trouble getting color bitmaps to load correctly. I have
 tried the color bitmap example from the "Programming Windows" book by
 Charles Petzold, but it does not work.

 Response:
    The resource compiler does not read color bitmaps in the same
 way that it does monochrome bitmaps. A properly created color bitmap
 actually should contain two bitmaps: a monochrome version and a color
 version appended together. The resource compiler, when looking at a
 bitmap, will examine the size of the file and compare it to the size
 that the file should be based on the header. If the two do not match,
 the compiler will look for a second header at the end of the first
 bitmap. This bitmap will be the color version, and will be the bitmap
 that the resource compiler loads.
    For an example of how your color bitmaps should be created, the
 following is what the file COLORBIT.BMP would have looked like as
 entered directly from Charles Petzold's example on Page 346 of
 "Programming Windows":

    02 00 00 00 08 00 08 00-02 00 03 01 00 00 00 00
    00 00 7E 00 7E 00 18 00-18 00 00 00 00 00 00 00
    00 00 00 00 00 00 7E 00-7E 00 00 00 00 00 00 00
    00 00 00 00 00 00 18 00-18 00 7E 00 7E 00 00 00

    However, the file should appear as follows:

    02 00 00 00 08 00 08 00-02 00 01 01 00 00 00 00 - Dummy Header
    FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF - Dummy Bitmap
    00 00 08 00 08 00 02 00-03 01 00 00 00 00 00 00 - Real Header and bitmap
    7E 00 7E 00 18 00 18 00-00 00 00 00 00 00 00 00
    00 00 00 00 7E 00 7E 00-00 00 00 00 00 00 00 00
    00 00 00 00 18 00 18 00-7E 00 7E 00 00 00

    Notice that an actual monochrome version was not supplied; a dummy
 bitmap was supplied to take care of the requirement of having two
 bitmaps stored in the file. Also notice that the second header is
 shifted two bytes to the left. The "02 00" is needed as the very first
 two bytes of the file only to identify the type of file it is; the
 second header should not have this information.
    If you already have created an "improper" bitmap, use the following
 method to correct your bitmap:

    1. Create a file that contains only the dummy bitmap information.
    2. APPEND the two together, as follows:

       C:> COPY DUMMY.BMP+COLOR.BAD COLOR.BMP

    3. DEBUG the file.
    4. Using the MOVE command, move the bytes starting at location 122
       to location 120, as follows:

       -M 122 15D 120

    (The "15D" value above will depend on the actual length of your
 bitmap. Refer to the documentation on DEBUG for proper use of the
 MOVE command.)

    This bitmap should now run through the resource compiler properly
 and should be attached to your executable file.


 403. Using the IconEditor to Create Bitmaps

 Problem:
    I used the Icon Editor to create a bitmap that is 96 pixels high
 and 16 pixels wide. When I load the bitmap in my program and use
 GetObject, the values returned report that it is now 48 pixels high
 and 8 pixels wide.

 Response:
    When using the IconEditor to create your bitmap, you will notice
 that there is a check box called "Device Dependent" in the dialog box
 that comes up when you select New. If you check this box, the bitmap
 will be the exact same height and width that you created it in when
 you load it; however, different devices will display it in different
 aspect ratios. That is, some monitors might show it as tall and
 narrow, while others will show it as short and wide. If you leave this
 box unchecked, the aspect ratio of the current device will be used to
 transform the bitmap to maintain a constant form of the image from one
 device to another when you load the bitmap. Currently, the IconEditor
 does not provide a feature to change this setting once a bitmap has
 been created.


 404. Dynamically Adding HELP Items to a Menu Bar

    To alter HELP menu items dynamically using the ChangeMenu()
 function call, you must specify the MF_HELP flag as well as placing a
 0x08 before the text for the menu item.
    The MF_HELP flag causes the vertical bar to be drawn to the left of
 the text, but it does not right justify the item in the menu bar. To
 make an item right justified, you must place a 0x08 character as the
 first character in the text string. This action has the same effect as
 placing a "\a" before the text in the RC file, because the
 preprocessor in the RC compiler converts the "\a" into a 0x08. If you
 place a "\a" in the C source code, it will not be translated into a
 0x08 because the C preprocessor converts a "\a" to a BELL character.
    The following is a sample ChangeMenu() function to add a HELP menu
 item to a menu:

 ChangeMenu(hMainMenu, NULL, (LPSTR) "\x008stuff", hHelpMenu,
           MF_APPEND | MF_BYCOMMAND | MF_POPUP | MF_HELP);


 405. Detecting Carriage Return Key in a Multiline Edit Control

 Problem:
    I am trying to create a dialog with a Multiline EDIT control that
 scrolls. However, I cannot get the carriage return key to move to the
 next line in the edit control.

 Response:
    To be able to detect the carriage return key and have it move you
 to the next line in the edit control, you must subclass the control
 and return DLGC_WANTALLKEYS in response to the WM_GETDLGCODE message.
    There is a file called SUBEDIT.ARC on the Software Library that
 contains a sample program that demonstrates subclassing an edit
 control to be able to process the carriage return key. This file can
 be found in the Software Library by searching for the filename, the Q
 number of this article, or S12006.


 406. Paint File Format Documentation

 Problem:

 In the paint file format, KEY1 = 0x6144, and KEY2 = 0x4d6e; however,
 if we use SYMDEB to read the file generated by Windows Paint Version
 2.03, the values for KEY1 and KEY2 are 0x694C and 0x536E,
 respectively.

 Response:

 Your observation is correct. KEY1 = 694C and KEY2 = 536E. This error
 has been corrected in the current version of the Paint file format
 application note, which can be obtained by calling Microsoft Product
 Support Services at (206) 454-2030 and requesting the Paint File
 format application note.

 There is also a file called PAINT.ARC in the Software Library. This
 file can be found in the Software Library by searching for the
 filename, the Q number of this article, or S12017.


 407. EXTTEXTMETRIC Structure

    The EXTTEXTMETRIC structure is not documented or included in
 windows.h; it is as follows as of June 1988. More fields may be added
 in future versions. An application can detect the version by checking
 the etmSize field.

    The EXTTEXTMETRIC structure is as follows:

 typedef struct {
   short etmSize;              /* size of structure in bytes                */
   short etmPointSize;         /* nominal point size in twips               */
   short etmOrientation;       /* 1 = portrait, 2 = landscape,  0 = both    */
   short etmMasterHeight;      /* size for exact extent table values        */
   short etmMinScale;          /* minimum value valid for userSize          */
   short etmMaxScale;          /* maximum value valid for userSize          */
   short etmMasterUnits;       /* # units per em of the masterSize          */
   short etmCapHeight;         /* height of upper case chars in font        */
   short etmXHeight;           /* height of lower case chars (of 'x')       */
   short etmLowerCaseAscent;   /* distance above baseline of ascenders      */
   short etmLowerCaseDescent;  /* distance below baseline of descenders     */
   short etmSlant;             /* angle in degrees ccw from vertical        */
   short etmSuperScript;       /* Y offset for superscript chars (negative) */
   short etmSubScript;         /* Y offset for subscript chars (positive)   */
   short etmSuperScriptSize;   /* recommended size of superscript chars     */
   short etmSubScriptSize;     /* recommended size of subscript chars       */
   short etmUnderlineOffset;   /* offset down from baseline of underline    */
   short etmUnderlineWidth;    /* thickness of underline (in font units)    */
   short etmDoubleUpperUnderlineOffset;
   short etmDoubleLowerUnderlineOffset;
   short etmDoubleUpperUnderlineWidth;
   short etmDoubleLowerUnderlineWidth;
   short etmStrikeOutOffset;   /* offset up from baseline of strikeout      */
   short etmStrikeOutWidth;    /* thickness of strikeout bar                */
   WORD  etmNKernPairs;        /* number of kerning pairs                   */
   WORD  etmNKernTracks;       /* number of kerning tracks                  */
   } EXTTEXTMETRIC;

 EXTTEXTMETRIC      struc
   etmSize              dw  0   ; size of structure in bytes
   etmPointSize         dw  0   ; nominal point size in twips
   etmOrientation       dw  0   ; 1 = portrait, 2 = landscape,  0 = both
   etmMasterHeight      dw  0   ; size for exact extent table values
   etmMinScale          dw  0   ; minimum value valid for userSize
   etmMaxScale          dw  0   ; maximum value valid for userSize
   etmMasterUnits       dw  0   ; # units per em of the masterSize
   etmCapHeight         dw  0   ; height of upper case chars in font
   etmXHeight           dw  0   ; height of lower case chars (of 'x')
   etmLowerCaseAscent   dw  0   ; distance above baseline of ascenders
   etmLowerCaseDescent  dw  0   ; distance below baseline of descenders
   etmSlant             dw  0   ; angle in degrees ccw from vertical
   etmSuperScript       dw  0   ; Y offset for superscript chars (negative)
   etmSubScript         dw  0   ; Y offset for subscript chars (positive)
   etmSuperScriptSize   dw  0   ; recommended size of superscript chars
   etmSubScriptSize     dw  0   ; recommended size of subscript chars
   etmUnderlineOffset   dw  0   ; offset down from baseline of underline
   etmUnderlineWidth    dw  0   ; thickness of underline (in font units)
   etmDoubleUpperUnderlineOffset dw  0
   etmDoubleLowerUnderlineOffset dw  0
   etmDoubleUpperUnderlineWidth  dw  0
   etmDoubleLowerUnderlineWidth  dw  0
   etmStrikeOutOffset   dw  0   ; offset up from baseline of strikeout
   etmStrikeOutWidth    dw  0   ; thickness of strikeout bar
   etmNKernPairs        dw  0   ; number of kerning pairs
   etmNKernTracks       dw  0   ; number of kerning tracks
 EXTTEXTMETRIC     ends

    Please refer to the Windows Version 2.x programmer's reference for
 a description of the TEXTMETRIC structure.



 408. hMetaDC illegal in PlayMetaFile() and EnumMetaFile()

 Question:
    In Version 2.10 and before, when I try to use PlayMetaFile() or
 EnumMetaFile() to play one meta file into another, the system RIPs and
 stops. The Windows programmer's reference implies that this is
 possible, but it does not seem to work. Is it possible to use a
 metafile DC as the destination of PlayMetaFile() or EnumMetaFile()?

 Response:
    PlayMetaFile() and EnumMetaFile() require a real DC, created with
 CreateDC(), for their destination (the first parameter). Meta DCs are
 not acceptable.
    Microsoft is researching this problem and will post new information
 as it becomes available.


 409. Windows SDK: Superclassing Sample Code

 A sample application is available that demonstrates the concept of
 superclassing. Superclassing provides the same capabilities as does
 subclassing, but it can be much more convenient to use and produce
 smaller code if there are multiple controls to be altered in the same
 fashion.

 By creating an entirely new class that uses the standard control
 window proc instead of the default window proc, you can process the
 messages you want and pass the others along, having them handled
 properly for this type of control.

 To do this, create a window of the type of control you want to
 superclass. Then query everything about the control and make a new
 class with all the same parameters, except with your new class name
 and window proc substituted. When your window proc is called, just
 handle the messages you want, and pass the others on to the standard
 control window proc that we queried earlier.

 Registering a new class this way allows you to create superclassed
 controls through the .RC file or CreateWindow() without any further
 trouble. This can save a number of SetWindowLong() calls if multiple
 controls are to be modified in the same way.

 In this example, we create a new type of control called "SuperEdit,"
 which is identical to edit controls, except that it refuses the "b"
 key.

 Search for the string "SuperEdit" in the .C, .RC, and .DEF files to see
 the affected areas of the code.

 The source code is called SUPERCLS. This file can be found in the
 Software Library by searching on the keyword SUPERCLS, the Q number of
 this article, or S12007.

 For information on controlling which DS an edit control uses, please
 search in the KnowledgeBase on the keyword GLOBEDIT.


 410. Cannot Use Wild Cards for OpenFile

    The "Microsoft Windows Software Development Kit Programmer's
 Reference Guide" Page 384 for Version 2.00 states that if wild card
 characters are used in the filename, the OpenFile function searches
 all directories in the PATH environment variable for a matching
 filename.
    This is not the case. Wild cards cannot be used in OpenFile. Wild
 cards are not expanded and the file will not be found if wild cards
 are used. If you do not use wild cards, the PATH environment variable
 will be searched.


 411. Representing an Application as an Icon

    It is possible to have a Windows application that is only displayed
 as an icon. To accomplish this you need to disable most of the options
 in the System Menu and catch the WM_SYSCOMMAND SC_RESTORE message that
 you receive when you double click the icon.

    You can disable the items in the System menu by using the
 GetSystemMenu function in combination with the EnableMenuItem function
 to disable the appropriate items in the System menu.
    The following is a sample window procedure that demonstrates how to
 make an application be represented by an icon. Note in the code below
 that the SC_MAXIMIZE command is also not passed to the DefWindowsProc.
 Since the application will not receive this command from the menu
 because it has been disabled, including this case statement is merely
 an additional precaution.

 long FAR PASCAL WindowProc (hWnd, message, wParam, lParam)

 HWND       hWnd;                     /*  Handle of the window  */
 unsigned   message;                  /*  Message type          */
 WORD       wParam;                   /*  Message 16 bit param  */
 LONG       lParam;                   /*  Message 32 bit param  */
 {
 HMENU hSystMenu;

     switch (message)  {              /*  Check the mess type   */
         case WM_SYSCOMMAND:

             switch (wParam)
             {
             case ID_ABOUT:           /*  Handle ABOUT box processing */
                 ProcessBaseAbout (hWnd);
                 break;

             case SC_MAXIMIZE:        /* Eat the maximize command
             case SC_RESTORE:         /* Eat the restore command from double
                                         clicking on the icon */
                 break;

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

         case WM_INITMENU:     /* disable system menu items that could restore
                                  the window something other than an icon */

             hSystMenu = GetSystemMenu(hWnd, FALSE);
             EnableMenuItem (hSystMenu, SC_MAXIMIZE,
                         MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
             EnableMenuItem (hSystMenu, SC_RESTORE,
                         MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
             EnableMenuItem (hSystMenu, SC_MAXIMIZE,
                         MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
             DrawMenuBar(hWnd);
             break;

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


 412. Determining Which Windows Are Drawn on Top

 Question:
    My application displays several overlapped windows, each of which
 has a menu. Is there any way to force one window to always stay below
 the others on the screen (i.e., a mouse click will activate the
 window, but not bring it to the top)?

 Response:
    When Windows is figuring out the Z ordering (i.e., which window goes
 on top of which on the screen), it keeps child Windows on top of their
 parents.
    If you want to have a number of windows in one application drawn so
 that one of the windows is always below the others, you need to make
 that window the parent of the other windows. This is done in the
 CreateWindow call for each of the other windows by specifying the hWnd
 of the parent window in the hWndParent parameter to CreateWindow.
    Please note that declaring one window as the parent of another is
 separate from declaring the window's style.


 413. Invalid Windows .EXE Filenames

 The following six filenames will not execute or load under Windows:

    COMM.EXE, DISPLAY.EXE, KEYBOARD.EXE, MOUSE.EXE, SOUND.EXE,
    SYSTEM.EXE, FONTS.EXE, WINOLDAP.EXE

 These are the names of reserved Windows support modules. If any of
 these files are double-clicked, nothing happens. Likewise, if they are
 selected and either the Load or the Run option is chosen from the
 MS-DOS Executive File menu, nothing happens. Do not name your
 applications or DLLs any of the above names.



 414. SetClassLong() and GCL_WNDPROC

 Problem:
    I am using SetClassLong() with nIndex equal to GCL_WNDPROC to
 insert my window procedure into a class definition to subclass a
 control. However, when I do this, messages still go to the control's
 original window procedure, not to mine.

 Response:
    When you change a class-function address, you affect windows
 created after the change. The class-function address is merely
 inserted into the window structure of windows in that class that are
 created while that function address is defined in the class. When the
 class-function address is changed, existing windows of that class are
 not affected. This change affects only windows created after the
 change.
    Subclassing is usually performed by changing the function addresses
 of individual windows. If any predefined window control's function address
 was changed, this would cause controls of that type in other
 applications to be subclassed. Even if this is the desired behavior,
 it leads to problems when run on machines with LIM 4.0 memory: the
 application that subclasses the class may be banked out when other
 applications are running, and when the control's function (located in
 the application's code segment, and currently banked out) is called,
 the system will hang.


 415. SYMDEB under Windows/386

 To test Windows/386 through SYMDEB, do the following:

 1. Rename WIN86.COM to WINX.COM.

 2. Copy COMMAND.COM to WIN86.COM.

 3. Run WIN386.EXE.

 4. When you get the DOS prompt, execute your SYMDEB as follows:

       symdeb x.sym y.sym z.sym WINX.COM

 5. Windows will be brought up.

 When Windows appears, use ALT+SYS RQ or CTRL+ALT+SYS RQ to break to
 SYMDEB. Then set your breakpoints, etc.


 416. Font Metrics and the Use of Negative lfHeight

    The following illustrations show the most commonly used font
 metrics, including Character Height, which is selected with negative
 values of lfHeight.
    Each illustration is followed by notes describing the metrics.

 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

                     O    O
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                       /\
                      /  \
                     /    \
 _ _ _ _ _ _ _ _ _  /______\ _ _ _ ___   _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                   /        \     /   \|
                  /          \   |     |
 _ _ _ _ _ _ _ _ /_ _ _ _ _ _ \ _ \___/| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                                       |
                                       |
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\___/_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                                                          |
                                                          |- External Leading
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _
                                                          |
                     O    O                               |- Internal Leading
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _
              |        /\
              |       /  \
              |      /    \
              |     /______\ _ _ _ ___   _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
 Char Height -|    /        \     /   \|
              |   /          \   |     |
              |  /_ _ _ _ _ _ \ _ \___/| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
              |                        |
              |                        |
 _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _\___/_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
              |                                           |
              |      O    O                               |
              |                                           |
              |        /\                                 |
              |       /  \                                |
              |      /    \                               |- Ascent
 Cell Height -|     /______\       ___                    |
              |    /        \     /   \|                  |
              |   /          \   |     |                  |
              |  /_ _ _ _ _ _ \ _ \___/| _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _
              |                        |                  |
              |                        |                  |- Descent
 _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _\___/_ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _

 Notes:

 Cell Height
    This is the height selected on positive lfHeight in the LOGFONT. It
 is the height of the font bitmap for raster fonts. It is returned as
 tmHeight in the TEXTMETRIC structure from GetTextMetrics().

 Character Height
    This is the height selected on negative lfHeight in the LOGFONT.
 (i.e., the mapper will try to match a font whose character height
 matches the absolute value of the requested height).

 Internal Leading
    This is returned as tmInternalLeading in the TEXTMETRIC structure
 from GetTextMetrics() and describes how much space has been left
 inside the font bitmap for diacritical marks (accents). Internal
 leading is primarily used for supporting European fonts. It is zero
 for many US fonts.

 External Leading
    This is returned as tmExternalLeading in the TEXTMETRIC structure
 from GetTextMetrics() and describes how much extra space the font
 designer expects the application to leave between rows of the font. It
 is not included in the bitmap, and is not modified by
 TextOut/ExtTextOut(), even in OPAQUE mode. When outputting multiple
 lines of text, they should be separated by (tmHeight +
 tmExternalLeading).

 Character Width                       Character Width
        |                                     |
 |-------------|                       |-------------|

 |          |                         /          /   |
 |          |                        /          /    |
 |          |                       /          /     |
 |          |                      /          /      |
 |----------|                     /----------/       |
 |          |                    /          /        |
 |          |                   /          /         |
 |          |                  /          /          |
 |          |                 /          /           |
                                            |________|
 |_____________|              |_____________|    |
        |                            |           Overhang
   Character width        Character width
 (including white space)  (including white space)

   Overhang = 0                  Overhang > 0

 Notes:

 Overhang
    tmOverhang specifies the per string extra width that may be added
 to some synthesized fonts. When synthesizing some attributes such as
 bold or italic, GDI or a device may have to add width to a string on
 both a per character and per string basis. For example, GDI makes a
 string bold by expanding the intercharacter spacing and overstriking
 with an offset and italicizes a font by skewing the string. In either
 case, there is an overhang past the basic string. For bold strings, it
 is the distance by which the overstrike is offset. For italic strings,
 it is the amount the top of the font is skewed past the bottom of the
 font. tmOverhang allows the application to determine how much of the
 character width returned by a GetTextExtent() call on a single
 character is the actual character width and how much is the per string
 extra width. The actual width is the extent less the overhang. In
 other words, tmOverhang is the difference between the width of a
 character when it is output singly versus its width when it is in the
 interior of a string.
    For more information, look up the following words in the "Microsoft
 Windows 2.00 Software Development Kit Programmer's Reference Guide":

    1. CreateFont()
    2. TEXTMETRIC

    In the OnLine Knowledge Base, query on "EXTTEXTMETRIC".


 417. MakeProcInstance Is Not Needed in DLLs

    It is not necessary to use MakeProcInstance() on dialog and
 callback functions whose code is contained in a DLL.
    Dialog and callback functions whose code is contained in a Windows
 DLL do not require the use of MakeProcInstance(). However, the
 functions must be EXPORTed to be accessible outside of the DLL, such
 as from any application or Windows routine. Using MakeProcInstance()
 in this case only consumes extra system resources, and produces slower
 code. In some cases it can also cause Fatal Exits.
    Since a DLL has only one DS, (instance), under Windows, the
 standard thunk created by the EXPORT line can set up the DS, without
 knowing any instance information.


 418. ValidateFreeSpaces, GlobalCompact, and Wild Pointers

    The ValidateFreeSpaces() function does not work as documented on
 Page 493 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" (Versions 2.03 and 2.10).

    ValidateFreeSpaces() returns a void. Under the debugging kernel, it
 will RIP if it finds overwritten free space. Under the retail kernel,
 it does nothing.
    To use ValidateFreeSpaces(), your WIN.INI file must include the
 following statements:

    [KERNEL]
    EnableHeapChecking=1
    EnableFreeChecking=1

    If an application calls ValidateFreeSpaces() and it discovers an
 error, it will RIP with a Fatal Exit code of 0x00ff, the message
 "FREE MEMORY OVERWRITE AT", and the address of the byte that was
 overwritten as Segment:Offset.
    To build a fairly solid detector of wild pointers, an application
 writer can use the following statements in the [KERNEL] section:

    1. EnableHeapChecking=1 to cause the kernel to regularly check the
       global arena.
    2. EnableFreeChecking=1 to let ValidateFreeSpaces() check global
       free space.
    3. EnableSegmentChecksum=1 to cause the kernel to check for
       corrupted code segments.

    If the application then calls ValidateFreeSpaces() and
 GlobalCompact(-1) regularly, it will catch almost all wild global
 pointers. ValidateFreeSpaces() will check for any overwritten free
 blocks, and GlobalCompact(-1) will move and discard memory, thereby
 forcing heap and checksum checking.
    Please note the following:

    1. CCh is used for the free-space fill byte because it will cause
       the debugger to breakpoint if a jump into free space is
       executed. (The "Microsoft Windows Software Development Kit
       Programmer's Reference Guide" incorrectly states on Page 493
       that the fill byte is 0Ch.)
    2. If you use EnableSegmentChecksum=1, setting breakpoints may
       cause spurious 0x0409 RIPs.
    3. Setting all of these checks will noticeably slow the system.
    4. If you have bad free spaces, check for fatal exit FFs and
       LLLLLLLs before the address actually appears. You may have to
       ignore a few fatal exits before the address appears.


 419. Placing Text in an Edit Control

    Text can be placed into an edit control in three different ways. If
 the edit control lies within a window, a WM_SETTEXT message can be
 sent to the edit control with the SendMessage function. If the edit
 control lies within a dialog box, this message can be sent with
 SendDlgItemMessage or SendMessage. This message can also be changed
 directly with the SetDlgItemText function. The best time to initialize
 the text in an edit control in a dialog box is at WM_INITDIALOG time.
    There are two different ways to get text from an edit control; A
 WM_GETTEXT message can be sent to the edit control, or the text can be
 retrieved directly with GetDlgItemText.

    The following are some examples of setting the text in an edit
 control:

    1. Assume that hEditControl is a handle to an edit control that is
       a child of a window, as follows:

    SendMessage( hEditControl, WM_SETTEXT, NULL,  /*  wParam Not used  */
                 (LONG)(LPSTR)"Text to be edited" );

    2. Assume that hDlg is a handle to a dialog box containing the
       above edit control, which has the ID of ID_EDITCONTROL:

    SetDlgItemText( hDlg, ID_EDITCONTROL,
                    (LONG)(LPSTR)"Text to be edited" );

    The following are some examples of getting the text from an edit
 control:

    1. Assume that hEditControl is a handle to an edit control that is a
       child of a window, and szBuffer is a 30-byte text buffer:

    SendMessage( hEditControl, WM_GETTEXT, 30, (LONG)(LPSTR)szBuffer );

    2. Assume that hDlg is a handle to a dialog box containing the
       above edit control, which has the ID of ID_EDITCONTROL, and
       szBuffer is a 30-byte text buffer:

    GetDlgItemText( hDlg, ID_EDITCONTROL, (LONG)(LPSTR)szBuffer, 30 );


 420. WM_MENUSELECT and Pop-Up Menus

 Question:

 I have been trying to implement a small status window that keeps track
 of which menu item I have currently selected and gives me a more
 detailed statement of what the selection will do (as Excel does in the
 lower-left corner). I do this by trapping the WM_MENUSELECT message
 and grabbing the ID of the menu item from the wParam. This works
 correctly for MENUITEMs.

 However, the POPUP keyword in the resource file does not have a
 constant ID number associated with it. To circumvent this problem, I
 have used a debugger and trapped the ID number of a pop-up menu (the
 pop-up menus have ID's such as 0xA86, 0xA8A, 0xA92, 0xA96, 0xA9A,
 etc.). I am wondering if this is a valid and approved way of doing
 this, or is there a more proper path that I should be following?

 Response:

 For these pop-up menus, the wParam in the WM_MENUSELECT message is the
 handle to the pop-up window, so if you obtain the handle to the
 pop-ups you can switch off of these as well as your ID constants when
 a WM_MENUSELECT message is received. A pop-up menu handle can be
 obtained through GetSubmenu(). You can obtain a handle to the main
 menu for the window through GetMenu(). Therefore, to get the handle to
 the first pop-up menu (assuming the first top-level menu item is a
 pop-up), you would do the following:

    hMenu = GetMenu(hWnd);
    hPopup = GetSubMenu(hMenu,0); /* first menu item in position 0 */

 hPopup can now be used in your "switch (wParam)" statement under
 WM_COMMAND messages in your window procedure.


 421. ES_RIGHT and ES_CENTER Require ES_MULTILINE Style

 Problem:
    I am trying to use an edit control with centered text in one of my
 dialog boxes. I have given the edit control the ES_CENTER style but
 the text is still left aligned in my edit control.

 Response:
    Edit controls with centered text (ES_CENTER) or right-justified
 text (ES_RIGHT) must have a multiline style (ES_MULTILINE). To make
 your edit control have centered or right-aligned text, OR in the
 ES_MULTILINE style to its definition.


 422. Overhead of GlobalAlloc() and LocalAlloc()

 Question:
    How much memory is actually consumed when you do a GloballAlloc()
 or a LocalAlloc()?

 Response:
    The header on global memory objects is 16 bytes. In addition, each
 global memory object is rounded up to the next 32-byte multiple. A
 GlobalAlloc() of 1 byte would actually consume 32 bytes from the
 global heap because 1+16 = 17 and then the object allocated would be
 rounded up to 32 bytes (to make the size of the entire object an even
 32-byte multiple).
    The header on local memory objects is 6 bytes. In addition, each
 local-memory object is rounded up to the next 4-byte multiple. The
 smallest local memory object is 12 bytes. A LocalAlloc() of 1 byte
 would consume 12 bytes (because of the minimum) and a LocalAlloc() of
 17 bytes would consume 24 bytes from the local heap because 17+6 = 23
 and then the size of the object is rounded up to 24 bytes (to make the
 size of the entire object an even 4-byte multiple).
    Knowing the overhead of these functions can help you design a more
 efficient memory-allocation scheme. Obviously, a linked list of 1-byte
 characters is not going to be a very efficient memory-wise.


 423. SIMTEST.ARC: Windows GDI Demonstration with Off-Screen Bitmaps

 There is a file named SIMTEST.ARC in the Software Library that
 demonstrates how to move information to and from off-screen bitmaps.
 It also shows GDI's ability to color single-bit off-screen bitmaps
 when moved to a color display.

 SIMTEST.ARC can be found in the Software Library by searching for the
 filename, the Q number of this article, or S10040. SIMTEST.ARC has
 been archived with PKARC so you will need to unarchive it with PKXARC.
 The PKXARC utility is located on your OnLine Utilities Disk 2.


 424. Description of "Fatal Exit 409" Messages

 Question:
    My application occasionally receives a "fatal exit 409 segment
 trashed" message. I have now seen a new "fatal exit 409" message with
 the text "ERROR READING RELOCATION RECORDS FROM XXXX:XXXX." What does
 this mean? Are there any more messages?

 Response:
    The following is a list of possible "fatal exit 409" messages, their
 meanings, and the definition of the xxxx:xxxx number:

    1. Code segment checksumming failed.
       "Segment contents trashed   a:b"
       Where "a" is the segment of the application database containing
       the checksum value of the loaded code segment and "b" is the
       segment address of the code segment that failed the checksum.

    2. DOS read handle failed to read relocation records from module
       "Error reading relocation records from  a:b"
       Where "a" is the handle to the module and "b" is zero.

    3. Global memory reallocation failed.
       "Out of memory loading segment from   a:b"
       Where "a" is the segment address of code segment record in
       database and "b" is zero.

    4. DOS readhandle failed to read segment contents from module.
       "I/O error reading segment contents from a:b"
       Where "a" is the segment address of code segment record in
       database and "b" is the return code from the DOS readhandle
       function call.

    5. Checksumming error during segment read.
       "Segment contents invalid  a:b"
       Where "a" is the segment address of code segment record in
       database and "b" is zero.


 425. Dialogs Move when Viewed or Read from a File

 Problem:

 Using DIALOG.EXE Version 2.00, my listboxes move around when saved,
 and then read or viewed again. For example, every time I view a
 dialog, the listbox and dialog will move one or more units
 horizontally and sometimes vertically. If I view the same dialog 10
 times, the problem becomes even worse.

 Response:

 Microsoft has confirmed this to be a problem in Version 2.03. We are
 researching this problem and will post new information as it becomes
 available.

 You can see this problem by looking at the x, y, cx, and cy values in
 the status window placed in the lower-right corner by the dialog box
 editor.


 426. Windows Semaphore Utility

 The following is an application note concerning the Windows
 "Semaphore" utility.

 To receive this application note, contact Microsoft Product Support
 Services at (206) 454-2030. This program can also be found in the
 Software Library by searching for the filename SEMAFORE.ARC, the Q
 number of this article, or S12021.

 This application note is a semaphoring package suitable for Microsoft
 Windows. It is written as a Windows library.

 The resource count for a semaphore governs the number of threads of
 execution allowed in the critical region. A boolean semaphore is a
 semaphore whose resource count is 0 .. 1 inclusive. In this package
 there are MAXSEMAPHORES, which are a resource protected by a boolean
 semaphore EXCLUSION, and a resource semaphore RESOURCE.

 A semaphore is allocated using GetSemaphore() and released using
 FreeSemaphore().

 MAXWORD-1 resources are allowed on a semaphore. MAXWORD signifies a
 free semaphore, zero signifies a blocked semaphore.

 The classical Dijkstra operators P and V are defined on the
 semaphores. P is used to enter a critical region and V to leave a
 critical region. P and V may also be used to monitor resources--
 producers/consumers.

 Sample 8086 source code is given for the primitives P and V. Lock
 instructions are used for multiple processor or dual-ported machines.


 427. Clipboard Formats

 Question:
    The "Microsoft Windows Software Development Kit Windows Extensions"
 manual for Version 2.00 lists the following clipboard formats on Page
 132:

    CF_RTF
    CF_WK1
    CF_BIFF
    CF_CSV

    There are no #define values for these in WINDOWS.H. Also, Excel
 supports the following clipboard formats, some of which are defined in
 WINDOWS.H:

    CF_TEXT
    CF_BITMAP
    CF_METAFILEPICT
    CF_SYLK
    CF_DIF
    CF_TIFF
    CF_OEMTEXT

    However, the following clipboard formats are not defined:

    CSV
    BIFF
    Printer_Picture
    Printer_Bitmap
    WK1
    Link
    Rich Text Format

    Could you also explain these formats?

 Response:
    The extensions manual incorrectly implies that the following four
 formats are defined in the WINDOWS.H file. Because these formats are
 not an industry standard, there are no #defines for them. Next to
 these values are the correct names for them. You can put them in your
 own #define.

    CF_RTF  -->  "Rich Text Format"
    CF_WK1  -->  "WK1"
    CF_BIFF -->  "Biff"
    CF_CSV  -->  "Csv"

    What you have to do is ask for these formats by name. The code below
 demonstrates how to get the current Biff data from the clipboard, if
 there is any:

     OpenClipboard(hWnd);
     wCBFormat = RegisterClipboardFormat ( (LPSTR)"Biff" );
     hBiff = (HBITMAP)GetClipboardData(wCBFormat);
     if (hBiff == NULL) {     /* verify there is biff data */
       CloseClipboard () ;
       MessageBox( GetFocus(), (LPSTR)"in clipboard",
                               (LPSTR)"No BIFF", MB_OK);
       break;
     }

    To see what other kinds of different formats are registered by
 other applications, save an item from that application to the
 clipboard and bring up the CLIPBRD.EXE application. The formats will
 be listed at the top of the CLIPBRD window. The following is a list
 of the formats:

    CF_TEXT         = ASCII Text string
    CF_BITMAP       = Bitmap
    CF_METAFILEPICT = Metafile
    CF_SYLK         = Microsoft Symbolic Link format
    CF_DIF          = DIF as defined by VisiCalc
    CF_TIFF         = Tag Information File Format
    CF_OEMTEXT      = OEM text, for OEMs to keep their own character sets

    However, there are still other formats that are not defined. The
 following are the formats that Excel uses:

    "Csv"              = Text with comma-separated values
    "Biff"             = This is for Excel's "binary information file
                         format"
    "Printer_Picture"  = Normal Windows metafile format designed to be
                         printed
    "Printer_Bitmap"   = Normal bitmap designed to be printed
    "WK1"              = Lotus file format
    "Rich Text Format" = Rich Text Format
    "Link"             = 3 null-terminated strings for DDE; consists
                          of application, topic, and item

    For additional information on Csv, refer to the READ and WRITE
 statements in a BASIC programming language manual.
    For additional information on the SYLK, Biff, RTF and TIFF file
 formats, query on their names. The TIFF format can also be found in
 the Windows extensions manual and the Metafile format can be found in
 the "Microsoft Windows 2.00 Software Development Kit Update" manual in
 Appendix D. (Note that the update manual is not in the binder, but is
 stapled together.)
    You will have to obtain the WK1 format from the Lotus manual, and
 DIF from the VisiCalc or SuperCalc manuals.


 428. Incorrect SetSwapAreaSize() Documentation

 There is an error on Page 448 of the Microsoft Software Development
 Kit "Programmer's Reference Manual" concerning the SetSwapAreaSize()
 function.

 The following is the correct information:

 SetSwapAreaSize returns a double word, as follows:

    lo word is Size actually set (or current size if you passed in 0)
    hi word is Max size you can get

 As such, the function should be declared as follows:

    DWORD  FAR PASCAL SetSwapAreaSize ( WORD );

 If the parameter (in 16-byte paragraphs) exceeds the total amount
 available, SetSwapAreaSize() will set the size to the amount
 available. There are no error codes returned.

 Please note that SetSwapAreaSize() was intended for use only by
 stand-alone applications and is not recommended for use by standard
 Windows applications.


 429. Alternate-Math Library and Trapping Zero Divide

    There are a few problems with using the alternate-math library when
 trapping the zero divide floating-point exception. Use the signal()
 function to set the interrupt handler for the zero divide. If the
 interrupt handler and floating-point math exception are contained
 within an application, the signal interrupt handler will be called. If
 the interrupt handler is in an application, but the floating-point
 exception occurs in a DLL called by that application, the signal
 interrupt handler will not be called and the application will be
 terminated. If the interrupt handler is in a DLL, the signal interrupt
 handler will not be called and, again, the application will be
 terminated.
    If the emulation library is used, all of the above situations will
 cause the interrupt handler to be called, and the application will not
 be terminated.
    Microsoft has confirmed this to be a problem in Version 2.03. We
 are researching this problem and will post new information as it
 becomes available.


 430. List Box Memory Problem

    There is a problem with list boxes. Repeatedly doing LB_ADDSTRINGs
 followed by LB_DELETESTRINGs consumes memory until finally,
 LB_ERRSPACE occurs.

    Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10.
    This is a design feature of the list box manager. The list box
 manager GlobalReAllocs memory for each string added, but does not free
 up memory or compact the global-memory block after deleting strings.
 This is by design, but the documentation does not detail this
 limitation.
    The following are two methods that can be used to clean up the
 global memory:

    1. Copy out all of the current strings from the list box and save
       them.
       a. Send a LB_RESETCONTENT message to the list box. This will
          destroy the global-memory object.
       b. Restore the list box by adding in the strings again. The
          frequency of using this technique is dependent on the rate
          that strings are added and deleted and the size of the
          strings.

    2. Destroy the list box control and recreate it. This has the same
       effect as above: the global memory is freed by destroying the
       window.

    This behavior does not change in Windows Version 2.10. However,
 this feature is under review and will be considered for inclusion in a
 future release.


 431. Two Bitmap Colors for a Control Icon

 Question:
    I am using an icon in a control statement for a dialog template. I
 trap the WM_CTLCOLOR message to pass a brush set from the system
 background color back to Windows. Although the icon background color
 changes, Windows is apparently not using both icon bitmaps the same
 way it ddoes when the icon appears against the screen background. Both
 white and screen color appear as the screen background, and black and
 screen inverse appear as black. How can I force Windows to utilize
 both bitmaps of an icon in a dialog control the same way it utilizes a
 window-minimized icon?

 Response:
    To override this default behavior, you will need to subclass the
 icon-control window in the dialog box, handle the WM_PAINT message,
 and use DrawIcon at that time. This will draw the icon using both
 masks and achieve the appearance that you desire.


 432. Drawing Outside the Client Area without Clipping

 Question:
    Is there any way to draw lines outside the client area of the main
 window? Whatever I draw seems to be clipped out.

 Response:
    Clipping occurs at the edge of the client window when the DC that
 is used is returned from BeginPaint or GetDC. This is the normally
 desired effect. Painting should not corrupt the nonclient area of the
 window or other windows on the display. The function, GetWindowDC,
 will allow you to draw anywhere within the window, including the
 nonclient area.
    If you require a DC for the entire display, use the CreateDC
 function as follows:

    hDC = CreateDC( "DISPLAY", NULL, NULL, NULL );

    Be sure to call DeleteDC as soon as possible, before completing
 processing of the current message.


 433. New Alternative to the List Box Control

 In the Software library you will find a sample application with source
 code that provides a list box that only takes up one line. It does this
 by providing a little box on the side of the text that you click on so
 that the possible items cycle through the list.

 This file can be found in the Software Library by searching on the
 filename SELECTLB.ARC, the Q number of this article, or S12083.


 434. Windows: GDI Dialog-Box Bitmaps

 Problem:
    Sometimes when running our application, available memory decreases
 to virtually nothing and performance becomes so poor that it is
 unusable. It seems to only occur when using relatively large dialog
 boxes that happen to contain one or more multiline edit boxes.
    In researching the problem with HEAPWALK, I have noticed that the
 offending memory user is GDI. When the problem occurs, I notice that
 there is a GDI "Shared" segment allocated that is over 80K in size. It
 goes away just as mysteriously as it appears; if I switch between a
 couple of applications, it usually goes away. Once it is gone,
 performance again returns to normal.

 Response:
    Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10. We are researching this problem and will post new information
 as it becomes available.
    The problem occurs on certain display adapters; the VGA is the most
 common. The VGA driver does not save the bitmap under a dialog box as
 does the EGA driver. The VGA relies on GDI to do this. However, GDI
 creates a nondiscardable bitmap under the dialog box, which is why
 your global compacts do not free up the memory. The memory is freed
 after bitblt restores the screen, or GDI determines that the bitmap is
 invalid (the windows under the bitmap have been modified).
    Although this is a feature of the product, there is a workaround.
 You can tell GDI not to save the bitmap. When the dialog box
 disappears, normal paint messages will be used to redraw the affected
 windows, instead of a quick bitblt.
    To instruct GDI not to save the bitmap, use SetClassWord to modify
 the GCW_STYLE index of the dialog box, disabling the CS_SAVEBITS flag:

    1. Use GetClassWord to get the current value of GCW_STLE.
    2. AND this with the NOT of CS_SAVEBITS (~CS_SAVEBITS).
    3. Use this new style and call SetClassWord. This will disable the
       bitmap saving. Make these calls (SetClassWord and GetClassWord)
       in your dialog-box routine.
    4. Handle the WM_SHOWWINDOW message and make the same calls.
    5. Before you quit the dialog box, be sure to restore the GCW_STYLE
       index to its original state; otherwise, all dialog boxes will
       not be able to SAVEBITS.


 435. Changing Fonts Used by Edit Controls

 Problem:
    In an effort to find the best technique to change the font in an
 edit control, we have tried intercepting the following WM_CTLCOLOR
 message that is sent to the parent of the edit control just before a
 paint is executed:

    case WM_CTLCOLOR:
           DeleteObject (SelectObject ((HDC)wParam, hTmsRomanFont));

    This process changes the font of the displayed text. However,
 selecting portions of the text (i.e., for deletion, etc.) does not
 work properly because the edit control procedure believes the system
 font is still being displayed. Presumably, this occurs because the
 WM_CTLCOLOR message is not sent prior to a text selection, and the
 display context used has the default system font associated with it.

 Response:
    With the presently defined Edit Control, there is no proper way to
 change the font used.
    If an application processes the WM_CTLCOLOR message, it must
 return a handle to the brush that is to be used for painting the
 control background. Note that failure to return a valid brush handle
 will place the system in an unstable state.
    As you have noticed, you can change the font in response to a
 WM_CTLCOLOR message, but you should not. You should only pass back a
 handle to a brush in response to this message; even though you are
 passed the DC in wParam, you should not take this opportunity to make
 modifications. The edit control will not check the brush it is given
 for validity, and it will not check the DC to determine if any changes
 have been made to it.
    The internal code that Windows uses to process edit controls is
 hardwired to assume the SystemFont is being used. It assumes this
 because no means was directly supplied to change the font. Also, it
 does not provide the means to change the font; if it did,
 it would place a great deal of overhead code on everybody that used
 edit controls just to allow them to use different fonts. We wanted the
 generic controls provided by Windows to be as fast as possible.
    If you want an edit control that uses a different font than the
 SystemFont, you will need to create your own control that properly
 handles this situation.


 436. Shared Memory and DLLs using GMEM_NOT_BANKED/GMEM_DDESHARE

 Question:

 How do applications share memory between themselves, and how does this
 relate to the use of GMEM_NOT_BANKED and GMEM_DDESHARE?

 Response:

 Applications share memory between themselves through DDE, the
 Clipboard, or library DS's. They may not pass handles back and forth
 in any other manner. If they use DDE, the blocks to be shared must be
 allocated with the GMEM_DDESHARE flag so that the KERNEL may do the
 necessary work of copying blocks from one set of banks to another. In
 addition, they must follow the DDE spec with regard to reading and
 writing to such blocks.

 The GMEM_NOT_BANKED and GMEM_DDESHARE flags are used in special
 circumstances where more precise control of memory placement is
 needed. For example, some printer drivers use the GMEM_NOT_BANKED flag
 to allocate blocks that must be accessible at all times by all
 applications.

 A more detailed description of the actions of these flags follows.

 Detailed Descriptions

 Calling GlobalAlloc() with the GMEM_NOT_BANKED flag places the block
 below the bank line so that it is resident at all times.

 The GMEM_NOT_BANKED flag controls the banking of a block. If a block
 is banked, ("above the line"), it is only available when its bank is
 active and loaded into real memory. A bank is active when the
 application that owns the bank is the current task. Note that multiple
 instances of an application all share the same bank. Since multiple
 instances share a bank, they can all use any block in the bank. If a
 block is not banked, ("below the line"), it is always in "real"
 memory and therefore always accessible no matter what the current
 task is. You cannot allocate DISCARDABLE, GMEM_NOT_BANKED memory if
 large-frame EMS is being used by Windows. Note that GMEM_NOT_BANKED
 uses SCARCE below-the-line memory and should not be used without good
 reason. Usually DDE can be used instead.

 You cannot GlobalRealloc() with GMEM_MODIFY | GMEM_NOT_BANKED. Once a
 block is allocated either above or below the line, it stays there
 forever.

 The GMEM_DDESHARED flag is what allows interbank copying. Let us say
 that one application allocated a DDESHARE block. This block is
 allocated above the line. Data is written to the block, and the handle
 is passed to another application through a DDE message. When the
 second application does the GlobalLock() on the handle, Windows makes
 a COPY of the block into the second application's bank. The second
 application can then read the data. When GlobalUnlock() is called with
 this handle, the copy is destroyed.

 GMEM_DDESHARE also has an interesting side effect. Calling
 GlobalAlloc() with the GMEM_DDESHARE flag sets the owner of the block
 to be the owner of the code segment that made the GlobalAlloc() call,
 instead of setting it to the current task (instance of an
 application).

 The side effects of GMEM_DDESHARE become apparent when an instance of
 an application, the last instance of an application, or a DLL
 terminates. When an instance terminates, all global blocks belonging
 to it are freed. When the last instance of an application terminates,
 all global blocks belonging to that application's code and all memory
 in that bank is freed. When a DLL is freed, all global blocks
 belonging to it are freed.

 In summary, GMEM_NOT_BANKED affects when a block is available, since
 it implies the block is never banked out, and GMEM_DDESHARE allows
 interbank copying of blocks for DDE. As a side effect, it also
 controls when the block is destroyed by changing the owner. These
 flags are then used to control passing and sharing of memory between
 instances and applications.

 DLLs come into play because they are code, (that is, a module capable
 of ownership), that is shared between applications. This allows blocks
 to be allocated that will be shared and not destroyed with the
 termination of any given instance or application. Note that if a DLL
 is loaded by IMPLIB reference from one or more applications, it will
 be freed when the last referencing application terminates. An IMPLIB
 reference is when an application links to a .LIB created by IMPLIB
 from the DLL's .DEF file. This causes the DLL to be loaded when the
 application is loaded.

 Note that calling GlobalRealloc() with GMEM_MODIFY | GMEM_DDESHARE,
 although possible, does not make much sense and is not guaranteed to
 have the desired effect.

 The following are the four possible types of memory blocks:

 1. Banked, not DDEshared

    This is the default case where the block is freed when the instance
    of the application that caused the allocation terminates. This is
    true even if the block was allocated while in a call to a DLL.

 2. Banked, DDEshared

    If allocated from an application, this allows the block to remain
    until the last instance of the application terminates, regardless
    of which one allocated it. If allocated from within a DLL, the
    block can exist until either the bank is destroyed with the last
    instance of the application, or the DLL is freed.

 3. Not banked, DDEshared

    If allocated from within a DLL, the block will remain until the DLL
    is freed, and will be available to all instances of all
    applications. If allocated from within an application, the block
    will be available to all instances of all applications, but the
    block will be destroyed with the last instance of the application.

 4. Not banked, not DDEshared

    This is an unusual case with only two known uses. From an
    application, it is used for blocks involved in message broadcasts.
    From printer drivers, it is used for data (such as font
    directories) maintained between calls.

 The lifetime of a DLL can be extended by using LoadLibrary() and
 FreeLibrary() to increment and decrement the reference count.

 Note that local blocks are allocated from within a DS that is
 destroyed in its entirety when that application or DLL is terminated.
 The GMEM_* flags do not apply.

 To reiterate, an application cannot just pass a global memory handle
 around. It must do so using DDE, the clipboard, or a library DS.

 For more detailed information about EMS support, please refer to Paul
 Yao's EMS article in the January 1988, (Volume 3, No. 1), issue of the
 "Microsoft Systems Journal."


 437. SLAP.ARC: Copy Windows Screens to the Clipboard

 The Windows program SLAP.EXE allows screen images to be copied to the
 clipboard. The following table describes how to use this utility:

 Key Combination     Result

 CTRL+F9             Captures current window
 ALT+F9              Captures whole screen
 SHIFT+F9            Captures client area

 This file can be found in the Software Library by searching for the
 filename SLAP.ARC, the Q number of this article, or S10041. SLAP.ARC
 has been archived with PKARC so you will need to unarchive it with
 PKXARC. The PKXARC utility is located on your OnLine Utilities Disk 2.


 438. Windows RIPs 0x1f0 when Application WM_CLOSEd with Menu Active

    When an application with the focus has its System menu pulled down
 (i.e., becomes active) and it receives a WM_CLOSE message from
 another application running in the system, the focus application
 proceeds through the WM_DESTROY processing to the WM_NCDESTROY and
 then RIPs with a 01f0 code.
    This problem was tested using two simple applications (hello sample
 application), as follows:

    1. The target application needs nothing more than the System menu
       selected.
    2. The other application has a small timer delay set, enough to
       select the target application and bring down its System menu. It
       then posts a WM_CLOSE to the target.

    You do not want a background process to start closing applications
 if your workstation is in this situation when the end-of-day
 processing takes place.

    If DDE is not used, Windows does not guarantee behavior when
 messages are posted or sent from one application to another.
 Therefore, the above behavior is not a problem in Windows; it must be
 handled by the application. Apparently, DDE is not being used and
 these applications are non-cooperating (i.e., have no internal
 knowledge of one another).
    There are two problems, as follows:

    1. An application must be able to determine if another application
       has a menu active, and if so, how the menu can be closed without
       making a selection.
          Unfortunately, Windows Versions 2.03 and 2.10 provides no
       functions or messages that can be used to do either of these
       tasks directly. In fact, it is no easier for an application to
       know if it currently has a menu pulled down than it is to
       determine this for another application. Windows sends a
       WM_INITMENU message to the parent window when a pull-down menu
       appears, but there is no such corresponding message when the
       menu closes. The following function can be used to determine if
       any window in the system has a menu active:

 /* A function that will determine if a specific window has a menu visible */
 BOOL FAR PASCAL IsWindowMenuVisible( hWnd )
 HWND hWnd;
 {
     HWND hWndMenu;

     /* The menu classname is an atom of value 32768. The menu is a pop-up
        window that is always present but usually invisible */
     hWndMenu = FindWindow( "#32768", (LPSTR)NULL );

     /* If menu window is visible and its parent is the window specified, */
     /* then window has a currently visible window. */
     if ((IsWindowVisible( hWndMenu )) && (GetParent( hWndMenu ) == hWnd))
        return TRUE;
     else
        return FALSE;
 }

          A pop-up window that is used to display a pull-down menu is
       always present. Normally, this pop-up window is invisible. The
       classname for the menu window is an atom of value 32768.
       Therefore, the above code first gets the window handle for the
       menu pop-up window. The code then checks whether the window is
       visible. Finally, it checks to see if the parent of the menu is
       the same as the window that has been passed in.
    2. This menu should close without making a selection. Again, there
       is no direct menu message that can be used. Clicking the mouse
       outside of the menu both closes the menu and makes the menu
       disappear. This "trick" is accomplished by posting a left mouse
       button down message, as follows:

         /* A function to check if window has a menu present */
         if (IsWindowMenuVisible( hwndAPP )) {
            /* Make menu disappear without any selection occurring */
            PostMessage( hwndAPP, WM_LBUTTONDOWN, 0, 0xf000f000L );
         }

          The handle, hwndAPP, is the window handle of the application
       being queried. The code uses the IsWindowMenuVisible() function
       listed above. The mouse position specified in the lParam field
       of PostMessage() is an off-screen location that will not be
       located in any window. Therefore, this code tests whether a menu
       is present and closes it if it does exist. You now can post a
       WM_CLOSE message and avoid the fatal exit. This technique is
       sensitive to version changes. The menu classname will remain
       32768. However, the "trick" used to close the menu is not
       guaranteed to work in future versions. In future versions, there
       may be more direct ways of accomplishing this task.


 439. FAR Calls Must Be Made with DS Equal to Default DS

 Question:
    We have a medium-model program with assembler modules that have
 their own data segments. Under this situation, we occasionally get
 the fatal exit 0x00FF with the string "_gnotify can't discard
 segment." What is most likely wrong here?

 Response:
    Whenever a FAR call is made, DS must be set to the default data
 segment for the application or DLL making the call. When a code
 segment that is waiting to be returned to is eligible for discarding,
 the Windows memory manager wants the value of DS that was pushed on the
 stack at the time of the call to be equal to the value of the default
 data segment.
    The fatal exit "_gnotify can't discard segment" is caused because
 the code segment to be discarded is owned by one of your assembler
 modules with its own data segment that is waiting to be returned to.
 Its DS value (as well as other things) is pushed on the stack because
 the -Gsw compile switch was used. When that segment is to be
 discarded, the DS value on the stack points to your assembler module's
 DS and not the default data segment; hence, the segment cannot be
 discarded. Therefore, you must set DS equal to the DS value of your
 default data segment before making any FAR calls.


 440. CreateWindow(): hMenu Parameter and Fatal Exit 0x0180

 The hMenu parameter in the CreateWindow() call differs, depending on
 whether the window being created has WS_POPUP, WS_OVERLAPPED, or
 WS_CHILD style. For WS_POPUP or WS_OVERLAPPED windows, the hMenu
 parameter specifies either a handle to a menu or NULL. If a handle to
 a menu is specified, this menu is attached to the window. If a NULL is
 specified, the menu defined in the window's class (if any) is used for
 the window.

 If the window has WS_CHILD style, the hMenu parameter specifies the
 child window identifier. For each child of a single parent, these
 child identifiers must be unique. You will most often see this used in
 predefined window classes (edit, list box, etc.). When these controls
 send WM_COMMAND messages to their parent, they pass the unique child
 ID in the wParam of that message. This value is stored in a window
 word and can be retrieved by calling GetWindowWord(hChild, GWW_ID).
 This function is documented on Page 321 of the "Microsoft Windows
 Software Development Kit Programmer's Reference" for Versions 2.03 and
 2.10.

 If an identifier is used for a WS_POPUP or WS_OVERLAPPED window
 instead of NULL or a handle to a menu, a fatal exit 0x0180 (invalid
 local handle) will occur during the CreateWindow() call.


 441. SYMDEB Stopping on INT 3

 Question:
    While single stepping with the "p" SYMDEB instruction, SYMDEB
 occasionally stops after tracing over a subroutine call. The code at
 the return point is INT 3. The interrupt was not there prior to the
 call. What is causing this and what can be done to keep it from
 happening?

 Response:
    Microsoft has confirmed this to be a problem in Version 2.03. We
 are researching this problem and will post new information as it
 becomes available.
    When using the "p" instruction, SYMDEB sometimes forgets that it
 has changed the following instruction to an INTERRUPT 3. When the
 breakpoint interrupt occurs, SYMDEB does not restore the original
 instruction and control is lost. This problem has been known to occur
 only when the function call being made causes a code segment load.
    There are two workarounds to the problem. When a function call is
 known to cause this problem, write down the instruction following the
 function call. When SYMDEB returns from the call and stops at the INT
 3, use the "a" command to enter the instruction that was at the
 location prior to the call.
    The other workaround is to set a breakpoint using the bp
 instruction. In the case of a breakpoint, SYMDEB remembers that it has
 modified the instruction and properly restores the code.
    SYMDEB also restores breakpoints when a code segment is reloaded.
 This is by design, but occasionally gets in the way, as you are
 experiencing. The reason for this feature is to allow the following:

    1. Set a breakpoint a winmain().
    2. Do some debugging and perhaps clear the winmain breakpoint.

    If the application is closed, you can restart the application from
 the MS-DOS Executive and expect SYMDEB to stop at winmain. This way
 the session does not have to be restarted at the DOS prompt.


 442. Word Alignment in the Physical Font Structure

    The "Microsoft Windows Software Development Kit Programmer's
 Reference," the "Microsoft Windows 2.00 Software Development Kit
 Update," the "Microsoft Windows Adaptation Guide" and the DDK file
 GDIDEFS.INC incorrectly document the physical font structure variously
 known as a FONTINFO, DeviceFont, PHYSFONTTYPE, or the Font File. In
 all cases, the character offset table is shown immediately following
 the field dfBitsOffset. In Version 2.x fonts, the character offset
 table is word aligned. This requires that there must be an extra
 padding byte added to the structure or the file following
 dfBitsOffset.

    The corrected ASM and C structures follow.

   FONTINFO        Struc

   dfType            dw      0   ; Type field for the font.
   dfPoints          dw      0   ; Point size of font.
   dfVertRes         dw      0   ; Vertical digitization.
   dfHorizRes        dw      0   ; Horizontal digitization.
   dfAscent          dw      0   ; Baseline offset from char cell top.
   dfInternalLeading dw      0   ; Internal leading included in font
   dfExternalLeading dw      0   ; Preferred extra space between lines
   dfItalic          db      0   ; Flag specifying if italic.
   dfUnderline       db      0   ; Flag specifying if underlined.
   dfStrikeOut       db      0   ; Flag specifying if struck out.
   dfWeight          dw      0   ; Weight of font.
   dfCharSet         db      0   ; Character set of font.
   dfPixWidth        dw      0   ; Width field for the font.
   dfPixHeight       dw      0   ; Height field for the font.
   dfPitchAndFamily  db      0   ; Flag specifying variable pitch, family.
   dfAvgWidth        dw      0   ; Average character width.
   dfMaxWidth        dw      0   ; Maximum character width.
   dfFirstChar       db      0   ; First character in the font.
   dfLastChar        db      0   ; Last character in the font.
   dfDefaultChar     db      0   ; Default character for out of range.
   dfBreakChar       db      0   ; Character to define wordbreaks.
   dfWidthBytes      dw      0   ; Number of bytes in each row.
   dfDevice          dd      0   ; Offset to device name.
   dfFace            dd      0   ; Offset to face name.
   dfBitsPointer     dd      0   ; Bits pointer.
   dfBitsOffset      dd      0   ; Offset to the beginning of the bitmap.
                                 ; On the disk, this is relative to the
                                 ; beginning of the file. In memory this is
                                 ; relative to the beginning of this structure
   Padding           db      0   ; A padding byte to make dfCharOffset word
                                 ; aligned. This is only needed for padding
                                 ; alignment and may be removed later.
   dfCharOffset      dw      0   ; Area for storing the character offsets,
                                 ; facename, device name (opt), and bitmap.

 FONTINFO        ends

 typedef     struct  {

     short int   dfType;
     short int   dfPoints;
     short int   dfVertRes;
     short int   dfHorizRes;
     short int   dfAscent;
     short int   dfInternalLeading;
     short int   dfExternalLeading;
     BYTE        dfItalic;
     BYTE        dfUnderline;
     BYTE        dfStrikeOut;
     short int   dfWeight;
     BYTE        dfCharSet;
     short int   dfPixWidth;
     short int   dfPixHeight;
     BYTE        dfPitchAndFamily;
     short int   dfAvgWidth;
     short int   dfMaxWidth;
     BYTE        dfFirstChar;
     BYTE        dfLastChar;
     BYTE        dfDefaultChar;
     BYTE        dfBreakChar;
     short int   dfWidthBytes;
     unsigned long int   dfDevice;
     unsigned long int   dfFace;
     unsigned long int   dfBitsPointer;
     unsigned long int   dfBitsOffset;
     BYTE        Padding;
     unsigned short dfMaps[DF_MAPSIZE];
     } FONTINFO;


 443. Cannot Alter Messages with WH_KEYBOARD Hook

 Problem:
    I am writing a Windows hook that intercepts and translates the
 user's keystrokes so that the user can type characters contained in
 the upper half of our modified OEM character set. I have created a
 program that uses the WH_KEYBOARD hook function to intercept the
 user's keystrokes. I then translate the wParam parameter according to
 my look-up table and return the translated value via DefHookProc(). I
 am able to verify that my translation procedure works as expected;
 however, the application that has the focus is not receiving the
 modified character message.
    I have changed the wParam value and then passed the hook code via
 DefHookProc(), yet whoever has the focus still receives the original
 character and not the translated character.

 Response:
    Keyboard messages cannot be altered with this hook. All that can be
 done is to swallow the message (return TRUE) or have the message
 passed on (FALSE). In a keyboard hook function, when you return
 DefHookProc(), you are passing the event to the next hook procedure in
 the potential hook chain, and giving it a chance to look at the event
 to decide whether or not to discard it. You are not passing the
 message to the system as if you had called DefWindowProc() from a
 Window procedure.
    To change the value of wParam (and hence the character message that
 is received by the window with the focus), you must install the
 WM_GETMESSAGE and WH_CALLWNDPROC hooks. The WH_GETMESSAGE hook traps
 all messages retrieved via GetMessage() or PeekMessage(). This is the
 way actual keyboard events are received: the message is placed in the
 queue by Windows and the application retrieves it via GetMessage() or
 PeekMessage(). However, because applications can send keyboard
 messages with SendMessage(), it is best to also install the
 WH_CALLWNDPROC hook. This hook traps messages sent to a window via
 SendMessage().
    These hooks pass you the address of the message structure so you
 can change it. When you return DefHookProc() within a WH_GETMESSAGE or
 WH_CALLWNDPROC hook procedure, you are passing the address of the
 (potentially altered) contents of the message structure on to the next
 hook function in the chain. If you alter the wParam before passing on
 the message, this will change the character message eventually
 received by the application with the focus.
    Keep in mind that the hook callback procedure must be placed in a
 DLL with fixed code so that it will be below the EMS line and thus
 will always be present. If the hook callback procedure is not in a
 fixed code segment, it could be banked out when it is called, and this
 would crash.


 444. FatalExit after Using WM_CTLCOLOR

 Problem:
    When using the message WM_CTLCOLOR, I sometimes get a Fatal Exit
 and my program terminates. I am doing everything correctly according
 to the programmer's guide.

 Response:
    When using the message WM_CTLCOLOR, you must return a selected
 HBRUSH if you process this message. This information was not included
 on Page 358 of the "Microsoft Windows Software Development Kit
 Programmer's Reference Guide"; however, this process is documented in
 Version 2.00 of the "Microsoft Windows Software Development Kit
 Programmer's Reference Guide." See the sample Cardfile (INDEX.C)
 program included with the SDK for an example of how to return a
 selected HBRUSH.


 445. Expanded PASSTHROUGH/DEVICEDATA Documentation

 As of July 1988, the "Printer Escapes" appendix in the "Microsoft
 Windows Software Development Kit Programmer's Reference" and the
 "Printer Driver Escapes" appendix in the "Microsoft Windows Adaptation
 Guide" manuals incorrectly document the use and implementation of the
 PASSTHROUGH (formerly DEVICEDATA) printer escape.

 The documentation is incorrect in regard to the values returned from
 the escape and how the count of bytes to be written is passed. The
 count of the number of bytes to be written is passed as a short (two
 bytes) at the beginning of the buffer. The nCount parameter of
 Escape() is ignored. The return value gives the number of bytes
 written if the function succeeds, or an error code less than or equal
 to zero if the function fails.

 A typical driver would implement the function as follows:

         case PASSTHROUGH:

             /* Pass a block of data to the port. */

             /* Validate lpInData. */
             if (!lpInData) {
                 return FALSE;
                 }

             /* Check for previous errors. */
             if (lpDevice->Job == (HANDLE)SP_ERROR) {
                 return FALSE;
                 }

             /* Check if we got anything to write. */
             /* If we didn't we'll return a zero,
                but we don't want to kill the job. */
             if (*(WORD FAR *)lpInData) {
                 if ((Result = WriteSpool(lpDevice->Job,
                                          lpInData + sizeof(WORD),
                                          *(WORD FAR *)lpInData)) <= 0) {
                     /* Kill the job and note that it's dead. */
                     DeleteJob(lpDevice->Job, 0);
                     lpDevice->Job = (HANDLE)SP_ERROR;
                     return Result;
                     }
                 }

             /* We made it. Return the count. */
             return *(WORD FAR *)lpInData;

 This code fragment is derived from the sample RAW driver, which can be
 found in the Software/Data Library by searching on the keyword RAW,
 the Q number of this article, or S12035.


 446. BOXES.EXE: Windows Demo that Displays Boxes

 There is a file named BOXES.EXE in the Software Library that displays
 dithered and colored boxes at random.

 This file can be found in the Software Library by searching for the
 filename BOXES.ARC, the Q number of this article, or S10042.


 447. WinSDK Prog Ref 2.00 UPDATE.DOC: Printer Escape Functions

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 130 in the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 Printer-Escape Functions
    [DELETE: "EXTEXTOUT Provides a more efficient way for the
 application to call the GDI TextOut function."]


 448. WinSDK Prog Ref 2.00 UPDATE.DOC: Metafile Data Structure

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.2  Metafile Data Structure (new appendix)
    The metafile itself is organized into two parts, a header and a
 list of records. The header contains a description of the size (in
 words) of the metafile and the number of drawing objects it uses. The
 list of records contains the GDI functions. The drawing objects can be
 pens, brushes, or bitmaps.


 449. WinSDK Prog Ref 2.00 UPDATE.DOC: ATOM AddAtom

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 148 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 ATOM AddAtom(lpString)
 Comments
    The atom values returned by AddAtom range from C000 to FFFF
 (hexadecimal). Atoms are case insensitive.


 450. WinSDK Prog Ref 2.00 UPDATE.DOC: LPSTR AnsiLower

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 151 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 LPSTR AnsiLower(lpStr)
 Return Value
    The return value points to a converted character string if the
 function parameter is a character string. Otherwise, it is a 32-bit
 value that contains the converted character in the low-order byte of
 the low-order word [DELETE:", and the high-order word is zero"].


 451. WinSDK Prog Ref 2.00 UPDATE.DOC: LPSTR AnsiUpper

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 153 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 LPSTR AnsiUpper(lpStr)
 Return Value
    The return value points to a converted character string if the
 function parameter is a character string; otherwise, it is a 32-bit
 value that contains the converted character in the low-order byte of
 the low-order word [DELETE:", and the high-order word is zero"].


 452. BN_CLICKED Notification Code Documentation Error

 On Page 522 of the "Microsoft Windows Software Development Kit
 Programmer's Reference," the lParam description of the BN_CLICKED
 message is incorrect. It should state that the low-order word contains
 the button window handle and the high-order word contains the
 BN_CLICKED notification code.

 On Page 522, the "comments" about the BN_CLICKED message are also
 incorrect. The second sentence, which states, "This code applies only
 to buttons that have BS_USERBUTTON style," is incorrect. This code
 applies to all buttons (i.e., to all controls of "button" window
 class).


 453. WinSDK Prog Ref 2.00 UPDATE.DOC: HWND CreateDialogIndirect

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 182 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 HWND CreateDialogIndirect(hInstance, lpDialogTemplate, hWndParent,
 lpDialogFunc)

 Return Value
    The return value is the window handle of the dialog box. It is NULL
 if the function cannot create either the dialog box or any controls in
 the dialog box.


 454. WinSDK Prog Ref 2.00 UPDATE.DOC: HWND CreateWindow

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 195 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 HWND CreateWindow(rgbColor)

 Table 3.5
 Control Styles
 __________________________________________________________
 Style   Meaning
 __________________________________________________________
 STATIC Class______________________________________________

 SS_NOPREFIX     Unless this style is specified, windows will interpret
 any "&" characters in the control's text to be accelerator prefix
 characters. In this case, the "&" is removed and the next character
 in the string is underlined. If a static control is to contain text
 where this feature is not wanted, SS_NOPREFIX may be added. This
 static-control style may be included with any of the defined static
 controls.
    You can combine SS_NOPREFIX with other styles by using the bitwise
 OR operator. This is most often used when filenames or other strings
 that may contain an "&" need to be displayed in a static control in a
 dialog box.


 455. WinSDK Prog Ref 2.00 UPDATE.DOC: int DialogBox

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 211 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 int DialogBox(hInstance, lpTemplateName, hWndParent, lpDialogFunc)

 Return Value
    The return value specifies the value of the nResult parameter in
 the EndDialog function that is used to terminate the dialog box.
 Values returned by the application's dialog box are processed by
 Windows and are not returned to the application. The return value is
 -1 if the function could not create the dialog box.


 456. WinSDK Prog Ref 2.00 UPDATE.DOC: int DialogBoxIndirect

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 212 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 int DialogBoxIndirect(hInstance, hDTemplateName, hWndParent,
 lpDialogFunc)

 Return Value
    The return value specifies the value of the wResult parameter in
 the EndDialog function that is used to terminate the dialog box.
 Values returned by the application's dialog box are processed by
 Windows and are not returned to the application. The return value is
 -1 if the function could not create the function.


 457. WinSDK Prog Ref 2.00 UPDATE.DOC: WORD EnumClipboardFormats

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 228 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 WORD EnumClipboardFormats(wFormat)
 Return Value
    The return value specifies the next known clipboard data format. It
 is zero if wFormat specifies the last format in the list of available
 formats. It is zero if Clipboard is not open.

 Comments
    The order that an application uses for putting alternative formats
 for the same data into the Clipboard is the same order that the
 enumerator uses when returning them to the pasting application. The
 pasting application should use the first format enumerated that it can
 handle. This gives the donor a chance to recommend formats that
 involve the least loss of data.


 458. WinSDK Prog Ref 2.00 UPDATE.DOC: BOOL FillRgn

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 244 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 BOOL FillRgn(hDC, hRgn, hBrush)
 Parameter       Type/Description

 hRgn            HRGN    Identifies the region to be filled. The
                         coordinates for the given region are specified
                         in device units.


 459. WinSDK Prog Ref 2.00 UPDATE.DOC: BOOL FrameRgn

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 249 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 BOOL FrameRgn(hDC, hRgn, hBrush, nWidth, nHeight)
 Parameter       Type/Description

 hRgn            HRGN    Identifies the region to be enclosed in a
                         border. The coordinates for the given region
                         are specified in device units.


 460. WinSDK Prog Ref 2.00 UPDATE.DOC: long GetBitmapBits

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 254 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 long GetBitmapBits(hBitmap, dwCount, lpBits)
 Parameter       Type/Description

 lpBits          LPSTR    Long pointer to the buffer that is to receive
                          the bitmap. The bitmap is an array of bytes. The
                          bitmap byte array conforms to a structure
                          where horizontal scan lines are multiples of
                          16 bits.


 461. WinSDK Prog Ref 2.00 UPDATE.DOC: WORD GetWindowWord

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 321 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 WORD GetWindowWord(hWnd, nIndex)
 Parameter       Type/Description

         Value   Meaning

 GWW_HWNDTEXT    Handle to the window title, but this handle is not
                 accessible to the application.


 462. WinSDK Prog Ref 2.00 UPDATE.DOC: HANDLE GlobalDiscard

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 325 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 HANDLE GlobalDiscard(hMem)
 Comments
    The GlobalDiscard function discards only global objects that an
 application allocated with the [DELETE: "GMEM_DISCARDED"]
 GMEM_DISCARDABLE and GMEM_MOVEABLE flags set. The function fails if an
 application attempts to discard a fixed object.


 463. WinSDK Prog Ref 2.00 UPDATE.DOC: void GlobalNotify

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 329 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 void GlobalNotify(lpNotifyProc)
    This function installs a notification procedure for the current
 task.

 Parameter       Type/Description

 lpNotifyProc    FARPROC    This is the procedure instance address of
                            the current task's notification procedure.

 Return Value
    None.

 Comments
    The address passed as the lpNotifyProc parameter must be created by
 using the MakeProcInstance function. A notification procedure is
 called whenever a global memory block allocated with the GMEM_NOTIFY
 flag is about to be discarded.
    If the notification procedure returns a non-zero value, Windows
 discards the global memory block. If zero is returned, the block is
 not discarded.
    The notification procedure must use the Pascal calling convention
 and must be declared FAR. The notification procedure must have the
 following form:

 BOOL FAR PASCAL NotifyProc (hmem)
    NotifyProc is a placeholder for the application-supplied function
 name. Export the name by including it in an EXPORTS statement in the
 application's module-definition statement.

 Parameter       Type/Description

 hMem            HANDLE    Identifies the global memory block being
                           discarded.

 Return Value
    The function returns a non-zero value if Windows is to discard the
 memory block, and zero if it should not.


 464. WinSDK Prog Ref 2.00 UPDATE.DOC: BOOL InvertRgn

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 343 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 BOOL InvertRgn(hDC, hRgn)
 Parameter       Type/Description

 hRgn            HRGN    Identifies the region to be filled. The
                         coordinates for the region are specified in
                         device units.


 465. WinSDK Prog Ref 2.00 UPDATE.DOC: HBITMAP LoadBitmap

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 352 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 HBITMAP LoadBitmap(hInstance, lpBitmapName)
 Comments
    If a bitmap is device-independent, the LoadBitmap function
 stretches or compresses the bitmap to accommodate the system's aspect
 ratio and resolution. If a bitmap is device-dependent, it's loaded as
 is, without any transformation applied. Mark bitmaps as
 device-independent or device-dependent when you create them with
 Iconeditor.


 466. WinSDK Prog Ref 2.00 UPDATE.DOC: Metafile Headers

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.2.1  Metafile Headers (new appendix)
    The following structured list describes the fields found in a
 metafile header:

 struct{
  WORD mtType;         /* Metafile in memory = 0; on disk = 1
 */
  WORD mtHeaderSize;   /* Metafile size in words */
  WORD mtVersion;      /* Version number in words*/
  DWORD mtSize;        /* Size of file in words */
  WORD mtNoObjects;    /* Word indicating number of objects
 created in
                   metafile */
  DWORD mtMaxRecord;   /* Size of largest record in words */
  WORD mtNoParameters; /* This field is not used */
 }


 467. WinSDK Prog Ref 2.00 UPDATE.DOC: BOOL PaintRgn

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 386 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 BOOL PaintRgn(hDC, hRgn)

 Parameter       Type/Description

 hRgn            HRGN    Identifies the region to be filled. The
                         coordinates for the given region are specified
                         in device units.


 468. WinSDK Prog Ref 2.00 UPDATE.DOC: HCURSOR LoadCursor

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 353 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 HCURSOR LoadCursor(hInstance, lpCursorName)

 Comments
 [After "IDC_SIZE":]

 IDC_SIZENESW    Double-pointed cursor with arrows pointing northeast
                 and southwest
 IDC_SIZENS      Double-pointed cursor with arrows pointing north and
                 south
 IDC_SIZENWSE    Double-pointed cursor with arrows pointing northwest
                 and southeast
 IDC_SIZEWE      Double-pointed cursor with arrows pointing west and
                 east


 469. WinSDK Prog Ref 2.00 UPDATE.DOC: void PlayMetaFileRecord

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 390 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 void PlayMetaFileRecord(hDC, lpHandletable, lpMetaRecord, nHndl)

 Parameter       Type/Description

 lpHandletable   LPSTR    Points to the object handle table to
                          be used for the metafile playback.

 lpMetaRecord    LPSTR    Points to the metafile to be played.


 470. WinSDK Prog Ref 2.00 UPDATE.DOC: HANDLE SelectObject

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 412 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 HANDLE SelectObject(hDC, hObject)

 Return Value
    If a region is being selected, the return is the same as for
 SelectClipRgn.


 471. WinSDK Prog Ref 2.00 UPDATE.DOC: long SetBitmapBits

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 416 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 long SetBitmapBits(hBitmap, dwCount, lpBits)

 Parameter       Type/Description

 lpBits          LPSTR   Points to the bitmap bits that are stored as a
                         long pointer to a byte array.


 472. WinSDK Prog Ref 2.00 UPDATE.DOC: ExtTextOut Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 ExtTextOut Record
 struct{
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 0A32H */
  WORD rdParm;   /* Contains the following elements:
            y    Logical y-value of string's starting
                 point
            x    Logical x-value of string's starting
                 point
            count        String's length
            options      Rectangle type
            rectangle    RECT structure defining ExtTextOut
                 rectangle if option is non-zero; non-
                 existent if options equal zero
            string       Byte array (((count + 1) >>1) words
                 long) containing the string
            dxarray      Optional word array of intercharacter
                 distances
         */
 }


 473. WinSDK Prog Ref 2.00 UPDATE.DOC: FARPROC SetWindowsHook

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 466 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 FARPROC SetWindowsHook(nFilterType, lpFilterFunc)
 Return Value
    The return value points to the procedure-instance address of the
 previously installed filter (if any). It is NULL if there is no
 previous filter. The library that calls the SetWindowsHook function
 must save this return value in the library's data segment. The fourth
 argument of the DefHookProc function points to the location in memory
 where the library saves this return value.

 Comments
    The system hooks are a shared resource. Installing a hook affects
 all applications. Because of LIM 4.0 considerations, most hook
 functions must be in libraries. The only exception is WH_MSGFILTER,
 which is task-specific. System hooks should be restricted to
 special-purpose applications or as a development aid during debugging
 of an application. Libraries that no longer need the hook should
 remove the filter function.
    To install a filter function, the SetWindowsHook function must
 receive a procedure-instance address of the function, and the function
 must be exported in the library's module-definition file. Libraries
 can pass the procedure address directly. Tasks must use
 MakeProcInstance to get a procedure-instance address.

 WH_CALLWNDPROC
 [Fourth paragraph:]
    FilterFunc is a placeholder for the library-supplied function name.
 The actual name must be exported by including it in an EXPORTS
 statement in the library's module-definition file.

 WH_GETMESSAGE
 [Third paragraph:]
    FilterFunc is a placeholder for the library-supplied function name.
 The actual name must be exported by including it in an EXPORTS
 statement in the library's module-definition file.

 WH_JOURNALPLAYBACK
 [Fourth paragraph:]
    FilterFunc is a placeholder for the library-supplied function name.
 The actual name must be exported by including it in an EXPORTS
 statement in the library's module-definition file.

 WH_JOURNALRECORD
 [Fourth paragraph:]
    FilterFunc is a placeholder for the library-supplied function name.
 The actual name must be exported by including it in an EXPORTS
 statement in the library's module-definition file.

 WH_KEYBOARD
 [Fourth paragraph:]
    FilterFunc is a placeholder for the library-supplied function name.
 The actual name must be exported by including it in an EXPORTS
 statement in the library's module-definition file.

 WH_MSGFILTER
 [After first paragraph:]
 Note
    This is the only task-specific filter. A task may install this
 filter.

 [Fourth paragraph:]
    FilterFunc is a placeholder for the library- or application-
 supplied function name. The actual name must be exported by including
 it in an EXPORTS statement in the application's module-definition
 file.

 WH_SYSMSGFILTER
 [Fourth paragraph:]
    FilterFunc is a placeholder for the library-supplied function name.
 The actual name must be exported by including it in an EXPORTS
 statement in the library's module-definition file.


 474. WinSDK Prog Ref 2.00 UPDATE.DOC: BOOL StretchBlt

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 481 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 BOOL StretchBlt(hDestDC, X, Y, nWidth, nHeight, hSrcdc, XSrc, YSrc,
 nSrcWidth, nSrcHeight, dwRop)

 [DELETE: "StretchBlt cannot process bitmaps larger than 64K. If a
 bitmap is larger than 64K, it must be subdivided into smaller
 bitmaps."]


 475. WinSDK Prog Ref 2.00 UPDATE.DOC: BOOL WriteProfileString

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 497 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 BOOL WriteProfileString(lpApplicationName, lpKeyName, lpString)

    This function copies the character string pointed to by the
 lpString parameter into the Windows initialization file, win.ini.

 Parameter       Type/Description

 lpApplicationName:

 LPSTR           Points to an application heading in win.ini.

 lpKeyName       LPSTR    Points to a key name that appears under
                          the application heading in win.ini.

 lpString        LPSTR    Points to the string that should replace
                          the existing key name.

 Return Value
    The return value specifies the result of the function. It is
 nonzero if the function is successful. Otherwise, it is zero.

 Comments
    If there is no match for lpKeyName, the new string that lpString
 points to will be added to win.ini.
    If there is no match for lpApplicationName, this function creates a
 new application field and adds the string pointed to by lpString.
    A string entry in win.ini has the following form:

 [application name]
 keyname = value
                .
                .
                .


 476. WinSDK Prog Ref 2.00 UPDATE.DOC: EM_GETHANDLE

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 528 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 EM_GETHANDLE
 Comments
    You may use this message only if a DS_LOCALEDIT flag has been added
 to the STYLE command for the dialog box in the .rc file.


 477. WinSDK Prog Ref 2.00 UPDATE.DOC: LB_SETCURSEL

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 545 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 LB_SETCURSEL
 Parameter    Type/Description

 wParam       Contains the index of the string that is selected. If
              wParam is -1, the list box is set to have no selection.


 478. WinSDK Prog Ref 2.00 UPDATE.DOC: EM_SETHANDLE

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 534 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 EM_SETHANDLE
 Comments
    You may use this message only if a DS_LOCALEDIT flag has been added
 to the STYLE command for the dialog box in the .rc file.


 479. WinSDK Prog Ref 2.00 UPDATE.DOC: LB_SETSEL

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 546 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 LB_SETSEL
 Parameter   Type/Description

 lParam      The low-order word of the lParam parameter is an index
             that specifies which string to set. If lParam is -1, the
             selection is removed from or added to all strings based on
             the value of wParam.


 480. WinSDK Prog Ref 2.00 UPDATE.DOC: WM_SPOOLER STATUS

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 594 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 WM_SPOOLER STATUS
    This message is sent from the spooler whenever a job is removed
 from the spooler's queue.

 Parameter    Type/Description

 wParam       Is set to SP_JOBSTATUS.

 lparam       Specifies the number of jobs remaining in the spooler's
              queue.

 Comments
    This message is for informational purposes only.


 481. WinSDK Prog Ref 2.00 UPDATE.DOC: WM_SYSCHAR

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 595 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 WM_SYSCHAR
 Parameter     Type/Description

 wParam        Contains the ASCII-character keycode of a system-menu
               key.


 482. WinSDK Prog Ref 2.00 UPDATE.DOC: LOGFONT

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Page 620 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" manual.

 LOGFONT   Logical-Font Descriptor
 Field     Description

 lfHeight  Specifies the average height of the font (in user units).


 483. WinSDK Prog Ref 2.00 UPDATE.DOC: Font-File Format

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Pages 645-655 of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 Font-File Format
 Field      Description

 <bitmaps>  This field contains the character bitmap definitions. Each
            character is stored as a contiguous set of bytes. (In the
            previous font format, this was not the case.) The first
            byte contains the first eight bits of the first scan line
            (that is, the top line of the character). The second byte
            contains the first eight bits of the second scan line.
               This continues until what amounts to a first column is
            completely defined. The following byte contains the next
            eight bits of the first scan line, padded with zeroes on
            the right if necessary (and so on, down through the second
            column).
               If the font is quite narrow, each scan line is covered
            by one byte, with bits set to zero as necessary for
            padding, and if the font is very wide a third or even
            fourth set of bytes can be present.
               The character bitmaps must be stored contiguously and
            arranged in ascending order.

    Figure 7.1 gives the bytes for a 12x14 pixel character, shown
 schematically:

 ............
 .....**.....
 ....*..*....
 ...*....*...
 ..*......*..
 ..*......*..
 ..*......*..
 ..********..
 ..*......*..
 ..*......*..
 ..*......*..
 ............
 ............
 ............

 Figure 7.1  12x14 Pixel Character

    The bytes are given here in two sets, as the character is less than
 17 pixels wide:

    00 06 09 10 20 20 20 3F 20 20 20 00 00 00

    00 00 00 80 40 40 40 C0 40 40 40 00 00 00

    Note that in the second set of bytes, the second digit of each is
 always zero. It would correspond to the 13th through 16th pixels on
 the right side of the character, if they were present.


 484. Valid GDI Functions with Metafiles and Banding Printers


 Page 128 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" lists the valid GDI functions an application
 can use in a metafile. This list is inaccurate, and should be changed
 as listed below.

 Nonmetafile functions cannot be used successfully on some printers.
 For a complete description of the problems with using a nonmetafile
 function for printing on a banding device, please refer to the article
 titled "Some GDI Functions Fail on Banding Printers," Q35857.

 The corrected list follows:

 Arc
 BitBlt
 Chord
 CreateBrushIndirect
 CreateFontIndirect
 CreatePatternBrush
 CreatePenIndirect
 CreateRegion
 Ellipse
 Escape
 ExtTextOut
 ExcludeClipRect
 FloodFill
 IntersectClipRect
 LineTo
 MoveTo
 OffsetClipRgn
 OffsetViewportOrg
 OffsetWindowOrg
 PatBlt
 Pie
 Polygon
 Polyline
 Rectangle
 RestoreDC
 RoundRect
 SaveDC
 ScaleViewportExt
 ScaleWindowExt
 SelectClipRegion
 SelectObject
 SetBkColor
 SetBkMode
 SetMapMode
 SetMapperFlags
 SetPixel
 SetPolyFillMode
 SetRelAbs
 SetROP2
 SetStretchBltMode
 SetTextAlign
 SetTextCharExtra
 SetTextColor
 SetTextJustification
 SetViewportExt
 SetViewportOrg
 SetWindowExt
 SetWindowOrg
 StretchBlt
 TextOut


 485. WinSDK Prog Ref 2.00 UPDATE.DOC: Appendix D: Metafile Format

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 APPENDIX D  [New Appendix]
 Metafile Format
    A metafile consists of a collection of Graphic Device Interface
 (GDI) function calls that create specific images on a device.
 Metafiles provide convenient storage for images that appear repeatedly
 in applications, and also allow you to use Clipboard to cut and paste
 images from one application to another.
    Images are stored in metafiles as a series of GDI functions. After
 function calls are stored, applications play a metafile to generate an
 image on a device.

 Note
    Functions described in this section are discussed in greater detail
 in the Microsoft Windows Programmer's Reference.


 486. WinSDK 2.00 README.DOC: C Library Conversion

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 README.DOC file.

 C Library Conversion
    Standard C libraries--such as SLIBCE.LIB--don't contain some of the
 Windows specific routines that the SDK needs. Using the standard C
 libraries, the SDK Install program creates modified C libraries that
 correspond with each standard C library found in the library directory
 on your disk. A "W" is appended to the base name of these newly
 created libraries. For example, SLIBCE.LIB becomes SLIBCEW.LIB.
    The installation program can copy the new libraries to the standard
 C library names (e.g., SLIBCEW.LIB is copied to SLIBCE.LIB) so that
 LINK4 will automatically find these libraries. LINK4 looks for the
 standard C library names by default.
    If you allow Install to copy these new libraries to the standard C
 library names, Install preserves your original C libraries by
 appending each with the letter "C". For example, SLIBCE.LIB becomes
 SLIBCEC.LIB.
    The Install Utility requires at least 2 megabytes of available disk
 space for installation of one memory model library. For each additional
 memory model, approximately one additional megabyte of free disk space
 will be needed.

 If You're Mostly Writing Windows Applications

    You should go ahead and allow the SDK Install program to copy the
 Windows libraries to the standard C run-time library names.
    If you need to build a DOS application after installing the Windows
 libraries, be sure to specify the original library on the LINK command
 line with the /NOD flag (No Default Library). For example:

    LINK dosapp, , /map, SLIBCEC /NOD

 If You're Mostly Writing DOS Applications

    Don't allow the Install program to copy the Windows specific
 libraries to the standard C library names. When you want to build a
 Windows application, type the name of the Windows specific library on
 the LINK4 command line, and follow it with the /NOD flag. For example:

    LINK4 winapp, , /map, SLIBW SLIBCEW /NOD, winapp.def


 487. WinSDK Prog Ref 2.00 UPDATE.DOC: Using Metafiles

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.1  Using Metafiles (new appendix)
    These are the steps involved in using a metafile:

    1. Create the metafile.
    2. Enter the GDI functions in the metafile.
    3. Close the metafile.
    4. Retrieve the metafile.
    5. Play the metafile.

    There are also functions available that allow you to retrieve
 metafiles from disk, delete them, copy them from one application to
 another, and alter them while they're being played.


 488. WinSDK Prog Ref 2.00 UPDATE.DOC: Creating a Metafile

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.1.1  Creating a Metafile (new appendix)
    To create a metafile, use the CreateMetaFile function. This call
 returns a handle that is used like a regular Device Context handle
 (hDC).
    To create the image, make GDI calls as though to a regular DC. The
 only difference is that the hDC that is passed into all the calls
 should be the handle returned by the CreateMetaFile function.
    The series of calls is then stored in the metafile. If a file is
 associated with the metafile, the calls are stored on disk for later
 use. If a memory metafile is being used, the calls are stored in main
 memory.


 489. WinSDK Prog Ref 2.00 UPDATE.DOC: Closing a Metafile

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.1.2  Closing a Metafile (new appendix)
    When all of the needed calls are made, use the CloseMetaFile
 function to close the metafile. This tells the GDI that the metafile
 is complete and ready for playing. A metafile handle is returned by
 this function.


 490. WinSDK 2.00 README.DOC: Font Editor Formats

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 README.DOC file.

 Font Editor Formats

    The Windows Version 2.00 Font Editor now reads and writes .FNT
 files in both Windows Version 1.00 and 2.00 format. Use the radio
 buttons that have been added to the SAVE AS dialog box to convert
 Version 1.00 formatted files to 2.00 format.


 491. WinSDK Prog Ref 2.00 UPDATE.DOC: Playing a Metafile

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.1.4  Playing a Metafile (new appendix)
    To play a metafile, use the PlayMetaFile function call with the
 handle of the metafile that you want to play.


 492. WinSDK Prog Ref 2.00 UPDATE.DOC: Deleting a Metafile

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.1.5  Deleting a Metafile (new appendix)
    To delete access to a metafile, use the DeleteMetaFile function.
 DeleteMetaFile disables the handle so that the application cannot play
 the metafile. Deleting a metafile frees the system resources
 associated with that file.


 493. WinSDK Prog Ref 2.00 UPDATE.DOC: Copying a Metafile

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.1.6  Copying a Metafile (new appendix)

 Use the CopyMetaFile function to make copies of metafiles. To
 successfully copy metafiles between applications each application must
 contain a global definition of the metafile.


 494. WinSDK Prog Ref 2.00 UPDATE.DOC: Altering Metafiles/Playback

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.1.7  Altering Metafiles During Playback (new appendix)
    You can use the EnumMetaFile function to alter a metafile during
 playback. EnumMetaFile allows you to control how the metafile is
 played. Otherwise GDI uses the PlayMetaFile function to play it
 exactly as it was created.
    With EnumMetaFile, GDI will call the playback function with every
 record in the metafile. This record may now be altered, copied,
 queried, etc.
    Any record can be played by using the PlayMetaFileRecord function.
 This includes records stored in the metafile you've specified, ones
 that have been altered, and newly created metafile records.


 495. WinSDK Prog Ref 2.00 UPDATE.DOC: BOOL ExtTextOut

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This
 information is an update to Page 241 of the "Microsoft Windows
 Software Development Kit Programmer's Reference" manual.

 BOOL ExtTextOut(hDC, X, Y, wOptions, lpRect, lpString, nCount, lpDx)

 Parameter       Type/Description

 wOptions        WORD    Specifies the rectangle type. It can be
                         one or both of the following values, or
                         neither:

                         ETO_CLIPPED
                         ETO_OPAQUE

                            The ETO_CLIPPED value specifies that
                         Windows will clip text to the rectangle. The
                         ETO_OPAQUE value specifies that the current
                         background color fills the rectangle.


 496. WinSDK 2.00 README.DOC: The F10 Key

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 README.DOC file.

 The F10 Key

    Do not use the F10 key for special functions in your applications.
 It has been reserved by Windows.


 497. WinSDK Prog Ref 2.00 UPDATE.DOC: Object Manipulation

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.1.8  Object Manipulation (new appendix)
    Objects created during the playback of a metafile are identified by
 an index to an object table that contains the corresponding object
 handles.
    Object selection calls use these indexes to identify the object to
 be selected. The objects are added to the table in the order they are
 created. For example, if a brush is the first thing created in the
 metafile--by using one of the object-creation routines--the resulting
 object is given index 0. The next object created is given index 1, and
 so on.
    The handle table is declared follows:

    typedef struct HANDLETABLE {
        HANDLE  objectHandle[1];
    } HANDLETABLE;


 498. WinSDK Prog Ref 2.00 UPDATE.DOC: Metafile Records

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.2.2  Metafile Records
    Metafile headers are followed by a series of records. Metafile
 records describe GDI functions. Most of the GDI functions that can be
 used to create metafiles are stored in similar records. These function
 numbers are listed in Table D.1.
    The rest of the functions contain more complex structures in their
 records and are described in Section D.2.3.
    The following structured list describes the fields found in a
 metafile record:

 struct{
  DWORD rdSize;  /* Records size in words */
  WORD rdFunction;       /* Function number in words */
  WORD rdParm[1];        /* N words containing function
 parameters  ParmN, ...,
             Parm1 (with the function call defined as
             Function(hDC, Parm1, ..., ParmN)) */
 }

 Table D.1  GDI Functions and Values

 Function              Value

 Arc                   0817H
 Chord                 0830H
 Ellipse               0418H
 ExcludeClipRect       0415H
 FloodFill             0419H
 IntersectClipRect     0416H
 LineTo                0213H
 MoveTo                0214H
 OffsetClipRgn         0220H
 OffsetViewportOrg     0211H
 OffsetWindowOrg       020FH
 PatBlt                061DH
 Pie                   081AH
 Rectangle             041BH
 RestoreDC             0127H
 RoundRect             061CH
 SaveDC                001EH
 ScaleViewportExt      0412H
 ScaleWindowExt        0400H
 SetBkColor            0201H
 SetBkMode             0102H
 SetMapMode            0103H
 SetMapperFlags        0231H
 SetPixel              041FH
 SetPolyFillMode       0106H
 SetRelAbs             0105H
 SetROP2               0104H
 SetStretchBltMode     0107H
 SetTextAlign          012EH
 SetTextCharExtra      0108H
 SetTextColor          0209H
 SetTextJustification  020AH
 SetWindowExt          020CH
 SetWindowOrg          020BH
 SetViewportExt        020EH
 SetViewportOrg        020DH


 499. WinSDK Prog Ref 2.00 UPDATE.DOC: CreateFontIndirect Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 CreateFontIndirect Record
 struct {
  DWORD rdSize;  /* Record size is 28 words */
  WORD rdFunction;       /* Function number is 0x02FBH */
  LOGFONT rdParm;        /* Specifies the logical font */
 }


 500. WinSDK Prog Ref 2.00 UPDATE.DOC: CreatePatternBrush Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 CreatePatternBrush Record
 struct {
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 01F9H */
  WORD rdParm[1];        /* Field contains the following
 elements:
            bmType       Bitmap type
            bmWidth      Bitmap width
            bmHeight     Bitmap height
            bmWidthBytes Bytes per raster line
            bmPlanes     Number of color planes
            bmBitsPixel  Number of adjacent color bits that
                 define a pixel
            bmBits       Pointer to bit values
            bits Actual bits of pattern
         */
 }


 501. WinSDK Prog Ref 2.00 UPDATE.DOC: CreatePenIndirect Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 CreatePenIndirect Record
 struct {
  DWORD rdSize;  /* Record size is 8 words */
  WORD rdFunction;       /* Function number is 0x02FAH */
  LOGPEN rdParm; /* Specifies the logical pen */
 }


 502. WinSDK Prog Ref 2.00 UPDATE.DOC: CreateRegion Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 CreateRegion Record
 struct {
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 06FFH */
  WORD rdParm[1];        /* Contains the region to be created */
 }


 503. WinSDK Prog Ref 2.00 UPDATE.DOC: DrawText Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 DrawText Record
 struct{
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 062FH */
  WORD rdParm[1];        /* Contains the following text-drawing
 information:
            Format       Specifies method of formatting
            count        Number of bytes in the string
            rectangle    Rectangular structure defining area
                 where text is to be defined
            string       Byte array containing the string
                 (((count +1) >> 1) words long)
         */
 }


 504. WinSDK Prog Ref 2.00 UPDATE.DOC: Escape Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 Escape Record
 struct {
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 0626H */
  WORD rdParm[1];        /* Contains the following escape
 information:
            escape number        Number identifying individual
 escape
            count        Number of bytes of information
            input data   Variable length field
                 (((count+1)/2+1))
         */
 }


 505. WinSDK Prog Ref 2.00 UPDATE.DOC: Polygon and Polyline Records

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 Polygon Record
 struct {
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 0324H */
  WORD rdParm[1];        /* Contains the following elements:
            count        Number of points
            list of points       List of individual points */
 }

 Polyline Record
 struct {
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 0325H */
  WORD rdParm;   /* Contains the following elements:
            count        Number of points
            list of points       List of individual points
         */
 }


 506. WinSDK Prog Ref 2.00 UPDATE.DOC: SelectClipRegion

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 SelectClipRegion
 struct{
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 012CH */
  WORD rdParm;   /* Index into the handle table corresponding
 to the
            region being selected */
 }


 507. WinSDK Prog Ref 2.00 UPDATE.DOC: SelectObject Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 SelectObject Record
 struct{
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 012DH */
  WORD rdParm;   /* Index into the handle table corresponding
 to the
            object being selected */
 }


 508. WinSDK Prog Ref 2.00 UPDATE.DOC: StretchBlt Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 StretchBlt Record
 struct {
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 0x0B23H */
  WORD rdParm[1];        /* Contains the following elements:
            raster op    Low word of the raster operation
            raster op    High word of the raster operation
            SYE  Y-extent of the source
            SXE  X-extent of the source
            SY   Y-coordinate of the source origin
            SX   X-coordinate of the source origin
            DYE  Destination y-extent
            DXE  Destination x-extent
            DY   Y-coordinate of destination origin
            DX   X-coordinate of destination origin
            bmWidth      Width of the bitmap in pixels
            bmHeight     Height of the bitmap in raster lines
            bmWidthBytes Number of bytes in each raster line
            bmPlanes     Number of color planes in the bitmap
            bmBitsPixel  Number of adjacent color bits
            bits Actual bitmap bits
         */
 }


 509. WinSDK Prog Ref 2.00 UPDATE.DOC: TextOut Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 TextOut Record
 struct {
  DWORD rdSize;  /* Record size in words */
  WORD rdFunction;       /* Function number is 0521H */
  WORD rdParm;   /* Contains the following elements:
            count        The string's length
            string       The actual string
            y-value      Logical y-value of string's starting
                 point
            x-value      Logical x-value of string's starting
                 point
         */
 }


 510. WinSDK Prog Ref 2.00 UPDATE.DOC: Sample Metafile Program

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This is an
 update to Appendix D of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual.

 D.3  Sample Metafile Program
    This sample program will create a small metafile in which a purple
 rectangle with a green border is drawn, and the words "Hello People"
 are written in the rectangle.

 MakeAMetaFile(hDC)
 HDC hDC;
 {
     HPEN        hMetaGreenPen;
     HBRUSH      hMetaVioletBrush;
     HDC hDCMeta;
     HANDLE      hMeta;

     /* create the metafile with output going to the disk */
     hDCMeta = CreateMetaFile( (LPSTR) "sample.met");

     hMetaGreenPen = CreatePen(0, 0, (DWORD) 0x0000FF00);
     SelectObject(hDCMeta, hMetaGreenPen);

     hMetaVioletBrush = CreateSolidBrush( (DWORD) 0x00FF00FF);
     SelectObject(hDCMeta, hMetaVioletBrush);

     Rectangle(hDCMeta, 0, 0, 150, 70);

     TextOut(hDCMeta, 10, 10, (LPSTR) "Hello People", 12);

     /* we are done with the metafile */
     hMeta = CloseMetaFile(hDCMeta);

     /* play the metafile that we just created */
     PlayMetaFile(hDC, hMeta);
 }

    The resulting binary file sample.met  will look like this:

 0001    mtType...  disk metafile
 0009    mtSize...
 0100    mtVersion
 0000 0036       mtSize
 0002    mtNoObjects
 0000 000C       mtMaxRecord
 0000    mtNoParameters

 0000 0008       rdSize
 02FA    rdFunction (CreatePen function call)
 0000 0000 0000 0000 FF00  rdParm (LOGPEN structure defining pen)

 0000 0004       rdSize
 012D    rdFunction (SelectObject)
 0000    rdParm (index to object #0... the above pen)

 0000 0007       rdSize
 02FC    rdFunction (CreateBrush)
 0000 00FF 00FF 0000 rdParm (LOGBRUSH structure defining the brush)

 0000 0004       rdSize
 012D    rdFunction (SelectObject)
 0001    rdParm (index to object #1... the brush)

 0000 0007       rdSize
 041B    rdFunction (Rectangle)
 0046 0096 0000 0000 rdParm (parameters sent to Rectangle...
                     in reverse order)

 0000 000C       rdSize
 0521    rdFunction (TextOut)
         rdParm
 000C    count
         string
 48 65 6C 6C 6F 20 50 65 6F 70 6C 65     "Hello People"
 000A        y-value
 000A        x-value


 511. WinSDK 2.00 README.DOC: Math Routines File Support

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 README.DOC file.

 Math Routines File Support
    If you use the math routines of the WIN87EM library, it is
 essential that you distribute the file WIN87EM.EXE. This file is
 located on the Development Files disk in the Software Development Kit.
    You need no written agreement with Microsoft to distribute this
 file. If you distribute applications under the Single Application
 Environment (SAE) agreement, you will notice that WIN87EM.EXE is not
 listed as one of the files in the SAE "product." WIN87EM.EXE is not a
 part of the SAE product; therefore, distribution of this file is not
 covered under the SAE agreement. Distribute this file as you see fit.
    If you convert your standard C libraries using the SDK Install
 utility, you do not have to specify WIN87EM.LIB on your link line.
    Dynamic Link Libraries that rely on WIN87EM.EXE can experience the
 following initialization problem: WIN87EM.EXE may not be present
 before a floating point call occurs within the dynamic link library.
 This can be resolved by placing a floating point variable in the
 calling application. This will insure that WIN87EM.EXE is loaded at
 the time that the application is executed, so that WIN87EM is already
 present before the dynamic link library is called.


 512. WinSDK 2.00 README.DOC: HEAP SIZE

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 README.DOC file.

 HEAP SIZE
    The Windows Version 2.00 loader requires a minimum heap size, as
 specified in the definition file, of 2048 bytes when developing using
 the medium memory model.


 513. WinSDK Prog Ref 2.00 UPDATE.DOC: CreateBrushIndirect Record

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 UPDATE.DOC file. This
 information is an update to Appendix D of the "Microsoft Windows
 Software Development Kit Programmer's Reference" manual.

 CreateBrushIndirect Record
 struct {
  DWORD rdSize;  /* Record size is seven words */
  WORD rdFunction;       /* Function number is 02FCH */
  LOGBRUSH rdParm;       /* Specifies the logical brush */
 }


 514. WinSDK 2.00 README.DOC: GDI Function GetTextAlign

    The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.00 README.DOC file.

 GDI Function GetTextAlign
    This function is documented but not available in this release.
 References to this function should be ignored.


 515. Private Data Exchange Sample Application

 EXCHANGE.ARC, a private data exchange application using libraries, is
 available in the Software Library. This application demonstrates the
 use of libraries to implement data exchange. The method does not
 provide mutual exclusion. It is an OnLine/DIAL customer-donated
 application built with assistance from Microsoft. We thank the
 customer for donating this application.

 This sample program can be found in the Software Library by searching
 on the filename EXCHANGE.ARC, the Q number of this article, or S12018.

 Processor: IBM AT 640K EGA with Enhanced Color Display


 516. WinSDK 2.10 READCVW.TXT: Using the Proper Version of Windows

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Using the Proper Version of Windows

 CVW does not work with earlier versions of Windows, and should be used
 only with the debugging version of the Windows Software Development
 Kit, Version 2.1 or later.


 517. C Compiler Version 5.00 Compile Error 2152

 Problem:

 When I attempted to recompile a Windows application using the
 Microsoft C Compiler Version 5.00, I received the following error:

    error C2152 '=': pointers to functions with different attributes

 Response:

 The error you are receiving is due to the stricter type checking of
 the Microsoft C Version 5.00 compiler. In WINDOWS.H, window procedures
 are currently defined to be of the following type:

    long (FAR * lpfnWndProc)();

 whereas in your programs, you are defining your window procedures as
 being of the following type:

    long (FAR PASCAL * lpfnWndProc)();

 The difference is the keyword PASCAL. This problem can be eliminated
 by changing the lpfnWndProc definition in the WNDCLASS structure in
 WINDOWS.H.

 Unfortunately, once this problem is eliminated, you will encounter
 problems linking when using the C Version 5.00 libraries with the
 Windows Version 1.0x SDK LINK4. This occurs because C Version 5.00
 produces object code not recognized by the old linker. You will
 encounter further problems involving a run-time initialization code
 conflict between the C Version 5.00 and Windows libraries.

 Therefore, as a solution to these problems, we have placed in the
 Software Library new Windows libraries, a new LINK4, and a WINDOWS.H
 that has been modified to include the new lpfnWndProc definition.
 These files are compressed in archive format in the file C5FIX.ARC.
 This file can be found in the Software Library by searching for the
 filename, the Q number of this article, or S12010.


 518. Variables in ES: Not Referenced by CodeView for Windows

 When programming in Windows with multiple data segments using the
 large model, CodeView for Windows (CVW) will not recognize variables
 in the extra segment. Watchpoints at any of those variables and any
 sort of direct access to these variables results in a "variable
 undefined" error message.

 CVW can only reference variables contained in the first data segment
 (DGROUP). Only static initialized variables will be guaranteed to
 reside there in the large model. Variables in other segments cannot be
 referenced by CodeView. If you want to be able to set watchpoints and
 do watches on a variable, you should initialize the variable and make
 it static in order for CVW to recognize it.

 Also, by using the recommended medium model under Microsoft Windows
 Version 2.10, a pointer can be set to a global memory location and can
 be referenced by CodeView. This global pointer will be residing in the
 DGROUP even though the global memory is not part of the DGROUP.


 519. Fatal Exit 280: Invalid Global Handle

 Question:

 I'm getting Fatal Exit 280. When I run SHAKER, the problem occurs
 sooner. What can cause this?

 Response:

 Fatal Exit 280 is caused by having an invalid global handle. You can
 get this error whenever you use a global handle or when the system
 uses a global handle. One reason for getting this fatal exit is that
 you may not have exported a window procedure or a call-back function
 in your .DEF file, or you did not do a MakeProcInstance() for a
 call-back function or a dialog procedure. This fatal exit can also be
 caused by a wild write.


 520. Incorrect CreateWindow Documentation in Programmer's Reference

 There is an error in the documentation for CreateWindow in the
 "Microsoft Windows Software Development Kit Programmer's Reference"
 manuals for Windows Versions 1.x and Versions 2.03 and 2.10. It says
 that the lpParam is passed as lParam in the WM_CREATE message. This is
 true since the lpParam value can be found through the lParam of the
 WM_CREATE message, but it is confusing since the lpParam value is NOT
 the value of lParam in the WM_CREATE message. The lParam value found
 in the WM_CREATE message is a pointer to a CREATESTRUCT; the
 CREATESTRUCT contains a copy of each parameter from the CreateWindow
 call (which includes the lpParam value). See the definition of
 CREATESTRUCT in WINDOWS.H for more details.

 To find the pages that are affected by this error, see the
 documentation of CreateWindow, WM_CREATE, and CREATESTRUCT, since the
 pages are different depending on the version of the Windows SDK that
 you are using.


 521. Windows SDK: Undocumented File I/O and String Routines

 The Windows KERNEL exports a number of undocumented file I/O and
 string routines.

 By including the definitions below in the application's include file,
 the application can access any of these functions.

 The string functions work as documented for their counterparts in the
 C run time. The file I/O functions all go directly to the DOS int 21
 handler. They don't do ANY processing of the data. In most cases, it's
 preferable to use OpenFile() rather than _lopen() because OpenFile()
 searches the path and can put up a message box if needed. The _lopen()
 function can be used if path searching is not desired.

 The definitions are as follows:

 /*
  * _lopen flags.
  */

 #define READ        0
 #define WRITE       1
 #define READ_WRITE  2

 /*
  * _llseek flags.
  */

 #define SEEK_SET    0
 #define SEEK_CUR    1
 #define SEEK_END    2

 /*
  * The file I/O routines.
  */

 extern  int     FAR PASCAL _lopen( LPSTR, int ); /* OpenFile is preferred. */
 extern  int     FAR PASCAL _lcreat( LPSTR, int );
 extern  int     FAR PASCAL _lclose( int );
 extern  long    FAR PASCAL _llseek( int, long, int );
 extern  int     FAR PASCAL _lread( int, LPSTR, int );
 extern  int     FAR PASCAL _lwrite( int, LPSTR, int );

 /*
  * The string routines.
  */

 extern  int     FAR PASCAL lstrcmp( LPSTR, LPSTR );
 extern  LPSTR   FAR PASCAL lstrcpy( LPSTR, LPSTR );
 extern  LPSTR   FAR PASCAL lstrcat( LPSTR, LPSTR );
 extern  int     FAR PASCAL lstrlen( LPSTR );


 522. hInstance in GetModuleHandle() and GetModuleUsage()

 Both GetModuleHandle() and GetModuleUsage() will accept an instance
 handle (hInstance) in place of their regular arguments.

 The following are examples of these usages:

     hModule = GetModuleHandle(MAKEINTRESOURCE(hInstance);

     Count = GetModuleUsage(hInstance);


 523. Finding the Number of Instances of an Application

 To find out how many instances of an application are running, you can
 use either one of the following:

     Count = GetModuleUsage(hModule);

     Count = GetModuleUsage(hInstance);


 524. The Four Classes of Device Banding

 There are four classes of printer device banding that an application
 must consider when printing. They are nonbanding devices, simple
 banding devices, "Full Page Banding for Text" devices, and BANDINFO
 devices.

 The classes are as follows:

 1. Nonbanding Devices

    These devices don't do any banding at all. They are generally
    nonraster devices that just send primitive output directly to the
    device without any need for intermediate rasterization. These
    devices don't set the RC_BANDING bit in GetDeviceCaps(hDC,
    RASTERCAPS). The PLOTTERS, HPPLOT, and PSCRIPT drivers are examples
    of this type.

 2. Simple Banding Devices

    These devices band both text and graphics into a series of bands
    starting at the top of the page and running to the bottom. They
    don't use any full-page bands. These devices set the RC_BANDING
    bit. The EPSON driver is an example of this type.

 3. "Full Page Banding for Text" Devices

    These devices use a full-page band for text before the rest of
    their regular bands for graphics. These devices set the RC_BANDING
    bit, but return FALSE to an Escape(..., QUERYESCSUPPORT, ...) for
    BANDINFO. They behave the same way the BANDINFO devices described
    below will when the application doesn't call the BANDINFO escape.

 4. BANDINFO Devices

    These devices use bands according to information passed by the
    application in the BANDINFO escape. These devices set the
    RC_BANDING bit and also return TRUE to an Escape(...,
    QUERYESCSUPPORT, ...) for BANDINFO. The HPPCL driver is an
    example of this type.

 For more information on full-page banding for text, please refer to
 the article titled, "Expanded BANDINFO Documentation," Q33252.

 For more information on banding, query the OnLine KnowledgeBase for
 NEXTBAND, NEWFRAME, BANDINFO, banding, and printing.


 525. CombineRgn Problem

 CombineRgn with a type of RGN_COPY returns an incorrect value.

 CombineRgn(..., RGN_COPY) should return the type of the region. Instead
 it returns a boolean which is non-zero if the function succeeds.

 The type of the region can be determined by calling the following:

     RgnType = OffsetRgn(hRgn, 0, 0);

 Microsoft has confirmed this to be a problem in Versions 2.x. We are
 researching this problem and will post new information as it becomes
 available.


 526. PSCRIPT.DRV Pen Width Problem

 There is a problem with the postscript driver PSCRIPT.DRV. Drawing
 with a pen, outputting underlined text, and drawing again with a pen
 of the same width as the first will result in drawing with the wrong
 pen width.

 The workaround is to select another pen of a different width and draw
 outside the device limits or with R2_NOP, then select a pen of the
 desired width.

 Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10. We are researching this problem and will post new information as
 it becomes available.


 527. Forcing an Application's DS to Move for Debugging

 When writing a Windows application, the developer must be very careful
 about having outstanding far pointers into DS. To thoroughly test an
 application, it must be run with its DS being frequently moved so that
 any outstanding pointers will be detected early. There is sample code
 in the Software Library showing how to force an application's DS to
 move. This file can be found in the Software Library by searching for
 RATTLEDS.ARC, the Q number of this article, or S12025.

 RattleDS is a sample application that moves its DS every two seconds
 based on a Windows timer. It does this by changing the size of its DS
 and forcing a discard and compact of global memory. When the size of
 the DS increases and there is no room for it to grow, it will be
 moved. Also, when memory is compacted, the DS is unlocked so that it
 will be moved downward in memory.

 To find the affected areas of the code, search on "RATTLEDS". Cut and
 paste those sections into the appropriate places of the application to
 be tested.



 528. Windows Versions 2.x Supports Bitmaps Greater Than 64K

 Windows Versions 2.03 and 2.10 will support bitmaps greater than 64K.
 However, to use this feature, the devices that use the bitmap must
 also support bitmaps of greater than 64K.

 To find out the capabilities of a given device, use the function
 GetDeviceCaps as follows:

    GetDeviceCaps (hIC, nIndex);

 The hIC parameter is a handle to the information context for the
 device in question. The nIndex parameter specifies the type of
 information desired. In this case, nIndex should contain the value
 RASTERCAPS defined in WINDOWS.H. To find out if bitmaps greater than
 64K are supported by the specified device, perform a bitwise AND
 operation on the return value from GetDeviceCaps with nIndex
 "RASTERCAPS" as follows:

    nflag = GetDeviceCaps (hIC, RASTERCAPS) & RC_BITMAP64;

 If the result, nflag, is nonzero, bitmaps greater than 64K are
 supported on the specified device.


 529. Limits on Polygons

 The Polygon function is capable of drawing a polygon that contains no
 less than two and no more than 8,189 vertices.

 The absolute distance between two connected points in a polygon must
 be in the range 0 to 32,767.



 530. Sample Source Code for TIFF

 The "Microsoft Windows Software Development Kit Windows Extensions"
 manual for Version 2.03 incorrectly states on Page XI of the TIFF
 section that source code is included within the kit for reading,
 writing, decompressing, and compressing TIFF files. There is no such
 source code included in the kit.

 The TIFF specification revision #4 with source code can be found in
 the Software Library by searching for the filename TIFF4.ARC, the Q
 number of this article, or S10066. Once you have found the file you
 can download it and unarchive it.



 531. wParam Values for the WM_HSCROLL Message

 The possible values for the WM_HSCROLL message are the same as those
 for the WM_VSCROLL message, as shown in the documentation. This is NOT
 a documentation error; although the values are the same, they are
 processed differently (although in analogous ways). When processing
 WM_HSCROLL messages, SB_TOP is effectively SB_LEFT; SB_BOTTOM acts as
 if it were SB_RIGHT; SB_LINEDOWN acts as SB_CHARRIGHT; and so on.

 If desired, a new set of parameter names can be defined in a header
 file so that WM_HSCROLL messages can be processed using wParam names,
 which makes it a little easier to follow the process.

 The following is a table of existing parameter names that suggest a
 specifically vertical orientation, their values, and possible names
 for horizontal equivalents:

 Defined Parameter     Value       Possible Equivalent

 SB_LINEDOWN            0             SB_CHARRIGHT
 SB_LINEUP              1             SB_CHARLEFT
 SB_PAGEDOWN            2             SB_PAGERIGHT
 SB_PAGEUP              3             SB_PAGELEFT
 SB_TOP                 6             SB_LEFT
 SB_BOTTOM              7             SB_LEFT


 532. GlobalWire() Banking Status with Large- and Small-Frame EMS

 Question:

 The GlobalWire() function moves a global object to low memory and
 places a lock count on it. Windows Version 2.x can bank application
 code into LIM 4.0 EMS. Will GlobalWire() move the object above or
 below the bank line?

 Response:

 There are two types of EMS to consider: Intel above-board type EMS,
 which only banks above the 640K line, and Rampage-board type EMS,
 which can bank above and below the 640K line. The first type of EMS is
 known as small frame, the second as large frame.

 Under small-frame EMS, the object will be wired below the bank line
 and locked. Under large frame, if you want it below the line, then you
 have to specify the GMEM_NOT_BANKED flag with the GlobalAlloc function.


 533. Manually Displaying Pop-Up Menus

 Problem:

 I was wondering what routine actually displays a pop-up menu from a
 menu bar? I would like to add a new menu resource that will emulate a
 Macintosh feature in which a menu bar's submenu can have a submenu of
 its own.

 Response:

 Windows uses a "simple" approach for implementing menus that does not
 extend well to supporting multilevel menus. To add this ability will
 require that you write a menu manager for your own application. This
 ability is planned for Presentation Manager and is being considered
 for a future release of Windows.

 The current technique Windows uses for implementing menus is that
 there is one pop-up window. This window is normally invisible. The
 window has register class name of "#32768". You will find this window
 if you enumerate all windows or call FindWindow with the above class
 name. The window procedure of this pop-up manages the menu items
 through messages and is responsible for proper menu display. When a
 menu is selected, the pop-up window is initialized through several
 menu messages, then sized and made visible. The internal routines
 currently assume one pop-up menu window.


 534. Windows UnionRect() Behavior

 Problem:

 There is a problem with the description of the UnionRect() function in
 the Windows SDK Version 2.03 (Page 490 of the "Microsoft Windows
 Software Development Kit Programmer's Reference"). Specifically, if
 either of the source rectangles is considered to be NULL or EMPTY by
 Windows (top==bottom or right==left), the destination rectangle is NOT
 the smallest rectangle that contains both source rectangles. This
 behavior should be clarified in the documentation because it is
 different from the behavior of the identically named Macintosh
 function.

 Response:

 Windows's GDI in general does not recognize rectangles or regions with
 NULL areas. For example, the Rectangle() function will not draw a line
 for a rectangle with either X1 == X2 or Y1 == Y2.


 535. Dialog Box Limited to 255 Controls

 Problem:

 In creating a dialog box that emulates a keyboard, I discovered that a
 dialog box is limited to 255 child controls. This should be
 documented.

 Response:

 The problem in the dialog box manager is due to one of the internal
 dialog-control template structures. A field that indexes the dialog
 box controls is defined as an unsigned char. Therefore, index wrap
 occurs at the 255 line. Since the dialog template is a documented structure,
 it is unlikely that this can be changed without affecting applications. A
 documentation and implementation request has been made.


 536. GlobalWire() and GlobalUnWire() Parameters

 Problem:

 I am confused about the proper use of GlobalWire() and GlobalUnWire().
 I have seen samples that pass global memory handles to these functions
 as well as examples that use absolute segment values. The
 documentation about these functions on Page 332 in the "Microsoft
 Windows Software Development Kit Programmer's Reference" does not
 explain the difference between them.

 Response:

 Either can be used.  GlobalWire() and GlobalUnwire() can refer
 to either handles or segments. There is no collision in the set of
 segment values and handle values that would create an ambiguity when
 wiring. It is even possible to use a segment when wiring and a handle
 when unwiring or vice versa.


 537. LocalAlloc() Fails when It Should Succeed

 When allocating memory, LocalAlloc() looks in the current heap first.
 If the memory cannot be found there, it will look in the "to be
 allocated" portion of the local heap. But, when allocating a block of
 memory, the "to be allocated" fragment of memory and the current heap
 are not considered contiguous. Therefore, if you ask for a memory
 block that is large enough to span both the current heap and the "to
 be allocated" portion of the heap, the memory allocation will always
 fail.

 The following is a diagram outlining the configuration of the data
 segment:

     -------------------------  64K
    |                         |
    |                         |
    |                         |
    | "to be allocated" heap  |
    |                         |
    |                         |
     -------------------------  size of automatic data + stack + current heap
    |                         |
    |                         |
    |                         |
    |      current heap       |
    |                         |
    |                         |
     -------------------------  size of automatic data + stack
    |                         |
    |                         |
    |                         |
    |       stack (fixed)     |
    |                         |
    |                         |
     -------------------------  size of automatic data
    |                         |
    |                         |
    |                         |
    | automatic data (fixed)  |
    |                         |
    |                         |
     -------------------------  0K

 The following is a procedure that will work around this problem. It
 has the same arguments as LocalAlloc() with one exception, fShrink.
 This flag should be set if you want the local heap to shrink if the
 memory allocation request fails. This procedure first it checks if the
 memory is already available, and if so, it returns a standard
 LocalAlloc call. Otherwise, it obtains the memory currently available
 in the local heap. Requesting the remaining amount through a call to
 LocalAlloc() causes the local heap to expand if the memory can be
 obtained. Freeing whatever local memory you have then allocated, call
 LocalAlloc() again to see if the large piece can be made contiguous in
 the (now larger) local heap.

 If this call fails, check the fShrink handle to determine if the
 caller wants the heap to be reduced from the size to which you have
 expanded it. Returning h1 works regardless of success or failure,
 since you are returning the same values as LocalAlloc(). Note that
 using the LMEM_NOCOMPACT flag will not prevent the LocalAllocAgain()
 routine from initially compacting your memory (this seems contrary to
 the objective of the routine).

 HANDLE LocalAllocAgain(WORD flags,WORD size,BOOL fShrink)
 {
 HANDLE h1,h2;
 WORD AvailHeap;

 AvailHeap = LocalCompact((WORD)-1);

 if (size <= AvailHeap)                    /* enough room in current heap */
   h1 = LocalAlloc(flags,size);

 else if (h1 = LocalAlloc(flags,AvailHeap)) { /* alloc AvailHeap memory   */

   if (h2 = LocalAlloc(flags,size-AvailHeap)) /* can remaining memory can be *
     LocalFree(h2);                           /* found in tba heap?          *

   LocalFree(h1);

   if ((!(h1 = LocalAlloc(flags,size))) && fShrink)  /* memory in tba and    *
     LocalShrink(0,0);                               /* AvailHeap contiguous?*

   }

 return(h1);
 }


 538. Hatched Brush Background Color

 Question:

 What affects the background color of a hatched brush?

 Response:

 As for all BitBlt() operations, hatched brushes depend on the ternary
 raster operator passed to BitBlt() by the application or another
 routine such as FillRect(). They don't reference the TRANSPARENT or
 OPAQUE mode set by SetBkMode().

 The algorithm used is as follows:

 1. The hatched portion of the brush is filled with the color specified
    in CreateHatchBrush() or CreateBrushIndirect().

 2. The remaining area of the brush is filled with the background color
    (as set by SetBkColor()) at the time the brush is used by BitBlt(),
    FillRect(), etc. This may be different than the background color at
    the time the brush was created.)

 3. BitBlt() uses the resulting brush with the ternary raster operation
    given. (FillRect() uses PATCOPY.)


 539. Nonstandard Format for Octal Integers in the RCDATA Statement

 Page 33 of the "Microsoft Windows Software Development Kit Programming
 Tools" manual incorrectly describes the RCDATA statement. The text
 states that "the raw-data field specifies one or more integers and
 strings stated in standard C-language format." It also states that
 "integers are given in decimal, octal, or hexadecimal format."
 However, this is not true of octal integers. Octal integers are
 currently not given in C-language format. The example in the manual
 and the example below work with Version 2.03 and Version 2.03em of the
 RC compiler. However, the example is not given in the correct
 C-language format.

 A subset of the above-mentioned example is as follows:

    resname RCDATA
    BEGIN
       0o733       /* Octal integer */
    END

 If the octal integer matched the standard C-language format, it would
 be specified as 0733 rather than as 0o733. If you try to specify the
 octal integer in correct C-language format, the RC compiler interprets
 the value as a decimal rather than octal.


 540. Octal-Byte Value of Zero Not Used in RCDATA Statement

 The RC compiler doesn't take a zero octal byte value. The RC compiler
 is documented as being able to accept octal bytes within the RCDATA
 statement. This works correctly except when the octal byte is a zero.

 Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.03em of the RC compiler. We are researching this problem and will
 post new information as it becomes available.

 The following is a sample .RC file that demonstrates the above problem
 with the RC compiler:

 /********************************************************************/
 /* SAMPLE.RC */

 resname RCDATA
 BEGIN
    "a"
    "\3"   /* This octal byte appears in the .RES file */
    "a"
    "b"
    "\0"  /* This octal byte does not appear in the .RES file */
    "b"
 END
 /********************************************************************/

 Note that the characters "a" and "b" are present to delimit the octal
 bytes. This will make them easier to find below. Now, copy the above
 code to a file called SAMPLE.RC and execute the following command to
 create a .RES file:

 RC -R SAMPLE.RC

 A hex dump of the .RES file will show that the first octal byte, "\3",
 appears in the .RES file as 03 hex. The second octal byte, "\0", does
 not appear in the .RES file at all.


 541. RC Compiler Version 2.03 Corrupting Bitmaps

 The RC compiler Version 2.03 supplied with the Windows Software
 Development Kit Version 2.00 corrupts the lower portion of bitmap
 resources. This problem may or may not be noticed when compiling a
 Windows program that uses bitmaps.

 The problem has been corrected in a new release of the RC compiler
 (Version 2.03em). This program is available in the Software Library.
 This file can be found by searching on RC.ARC, the Q number of this
 article, or S10065.


 The two RC compilers mentioned above can be identified as follows.
 Each RC compiler will display a message, which includes a distinct
 version number, when the command "RC" is executed from the DOS command
 line. For instance, the RC compiler supplied with the Windows Version
 2.00 SDK displays the following message:

    Microsoft (R) Windows Resource Compiler  Version 2.03
    Copyright (C) Microsoft Corp. 1985-1988.  All rights reserved.

    Invalid usage. Use rc -? for Help

 On the other hand, the newer maintenance release of the RC compiler
 displays a slightly different message as follows:

    Microsoft (R) Windows Resource Compiler  PSS Version 2.03em
    Copyright (C) Microsoft Corp. 1985-1988.  All rights reserved.

    Invalid usage.  Use rc -? for Help

 Also, note that the older, problematic, RC compiler has a file date of
 January 29, 1988, whereas the newer RC compiler has a file date of
 April 4, 1988.


 542. Expanding the Size of a Dialog Box

 Question:

 How can I make a dialog box expand in the same way Excel uses for its
 fonts dialog box?

 Response:

 To make a dialog box expand, do the following:

 1. After designing the complete dialog box, lessen the height and/or
    width so that the other controls are not visible.

 2. Disable the "hidden" controls in the RC file (by giving them the
    WS_DISABLED style) so that using the TAB key or other neumonic keys
    will not give them the focus.

 When the person presses the desired "expand" key, do the following:

 1. Disable the "expand" key.

 2. Enable the other controls with EnableWindow(...,TRUE).

 3. Set the focus on the control you want to now have the focus.

 4. Use the following function to get the current origin of the dialog:

    GetWindowRect(hWndDlg,(LPRECT)&r);

 5. Use the following function to then expand the dialog to its new
    size:

    MoveWindow(hWndDlg, r.left,r.top, r.right-r.left,
                        (r.bottom-r.top)+14, TRUE );

 6. Replace the "+14" with the desired depth.

 Note that you can also expand in any direction that you want with the
 MoveWindow() function. There is also a sample application called
 EXPAND.ARC in the Software Library that demonstrates how this is done.
 This file can be found in the Software Library by searching for the
 filename, the Q number of this article, or S12027.


 543. CreateDialog() Failure Returns NULL, Not -1

 The CreateDialog() function is incorrectly documented as returning a
 -1 if it cannot create the dialog box. The correct return value is
 NULL.

 CreateDialog() is documented on Pages 180-181 of the "Microsoft
 Windows Software Development Kit Programmer's Reference" Version 2.00.
 The text under the heading "Return Value" should read as follows:

 "The return value is the window handle of the dialog box. It is NULL
 if the function cannot create the dialog box."

 The following are some reasons why CreateDialog() might fail:

 1. Unable to find the resource

 2. Unable to lock the resource

 3. Unable to create the window (probably not enough memory)


 544. EM_SCROLL Message Is Not Supported

 Question:

 I can't get my window to respond to an EM_SCROLL message. It collects
 lines of text sent by other applications and displays them in an edit
 window. After receiving each line, it appends the line to its buffer
 and displays the new buffer. Since later lines are more interesting, I
 use the following two scrolling calls to position the buffer at the
 top of the last page:

 SendMessage( hMsgWnd, EM_LINESCROLL, 0, MAKELONG( numLines, 0 ));
 SendMessage( hMsgWnd, EM_SCROLL, SB_PAGEUP, 0L );

 The first message works (changing numLines affects the display), but
 the second has no effect. The top of the window is always positioned
 at the bottom of the last page, displaying no lines. What is wrong?

 Response:

 The EM_SCROLL message does not work correctly. The effect of scrolling
 up a page can be accomplished by figuring out the number of lines that
 will fit in the edit-control client area and then sending the
 EM_LINESCROLL message with the low-order word equal to that value. The
 following procedure will scroll an edit control's contents up or down
 one page. The first parameter, "hEdit", is a handle to the edit
 control and the second, "fPageUp", is a boolean that specifies if the
 edit control is to be scrolled upward or downward.

 Note: The documentation of the lParam of the EM_LINESCROLL message on
 Page 533 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" is incorrect. The high-order word of the
 lParam contains the number of lines to scroll HORIZONTALLY and the
 low-order word of the lParam contains the number of lines to scroll
 VERTICALLY.

     void PASCAL EditScroll(hEdit,fPageUp)
     HWND hEdit;      /* handle to edit control to scroll */
     BOOL fPageUp;    /* scroll up? if not, scroll down */
        {

         int  nPage,            /* number of rows in a "page" */
              nRowHeight;       /* row spacing between characters */
         HDC  hDC;              /* display context for edit control */
         RECT edRect;           /* client coordinates of edit control */
         TEXTMETRIC tmMetrics;  /* font information */

         GetClientRect(hEdit,(LPRECT)&edRect);
         hDC = GetDC(hEdit);
         GetTextMetrics(hDC,(LPTEXTMETRIC)&tmMetrics);
         ReleaseDC(hEdit,hDC);
         nRowHeight = tmMetrics.tmHeight + tmMetrics.tmExternalLeading;
         nPage = edRect.bottom/nRowHeight -1;
         SendMessage(hEdit,
                     EM_LINESCROLL,
                     0,
                     MAKELONG(fPageUp ? -nPage : nPage,0));
         return;
         }


 545. lParam of EM_LINESCROLL Message Is Documented Incorrectly

 The documentation of the lParam of the EM_LINESCROLL message on Page
 533 of the "Microsoft Windows Software Development Kit Programmer's
 Reference" Versions 2.03 and 2.10 is incorrect. It incorrectly states
 that "The high-order word of the lParam parameter contains the number
 of lines to scroll vertically; the low-order word contains the number
 of character positions to scroll horizontally."

 However, the high-order word of the lParam specifies the number of
 lines to scroll HORIZONTALLY. The low-order word of the lParam
 specifies the number of lines to scroll VERTICALLY.


 546. EM_GETLINE Assuming Locally Allocated Text Buffer

 Question:

 It appears the message EM_GETLINE requires a far pointer to a buffer
 in the local data segment as the destination for the text. Is this
 correct?

 Response:

 The length of the buffer stored in the first word of the buffer is
 referenced as an (int *) rather than an (int far *).

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.

 The only workaround is to use a far pointer to a local buffer.


 547. Message and Task Priorities

 Question:

 What are the differences between tasks and windows? Do different
 messages have different priorities?

 Response:

 A task will get scheduled to run. Each instance of an application is a
 task. Although a task can have more than one window, a window can be
 owned by only one task.

 The message priority is determined by the queue (if any) in which the
 message is placed or where it starts out from. There are two message
 queues: the system queue and the application queue. Messages get
 emptied out of the application queue first, and the application queue
 gets filled from the system queue.

 Keyboard and mouse messages are placed by Windows into the system
 queue. Messages that are posted with PostMessage() go to the
 application queue. Messages that are sent with SendMessage() do not go
 to either queue but are sent directly to the window procedure. It is
 possible that messages placed in the application queue (with
 PostMessage()) after keyboard or mouse messages in the system queue
 will reach the window procedure first. It is also possible that
 messages that are sent with SendMessage() will reach the window
 procedure before messages that were placed in the system or
 application queue before the SendMessage() was executed.

 In addition to the above scenarios, there are two messages that are
 handled specially. These are WM_TIMER and WM_PAINT messages. These
 messages are never retrieved by GetMessage() or PeekMessage() until
 all other messages for the application have been cleared from the
 application and system queues.


 548. Yield() Function in Windows Requires Queue to Be Empty

 Question:

 Are there any code examples that use the Yield() function? I can't get
 it to work. What am I doing wrong?

 Response:

 The problem you are having is that the Yield() function will only
 yield if the application queue is empty. PeekMessage() has the same
 restriction: it will only yield if a message in the specified range is
 not found AND there are no messages left in the application queue.
 Purposely yielding is most easily done with PeekMessage(); however, to
 yield with the Yield() function, the following code can be used:

 while (PeekMessage((LPMSG)&msg,msg.hwnd,0,0,PM_REMOVE | PM_NOYIELD))
     {
     /* translate and dispatch the messages if you wish */
     }
 Yield();

 Note that the PM_NOYIELD flag is used so that PeekMessage() does not
 do the yielding when the queue is empty. This is so that the yielding
 can be done by Yield() instead. A more straightforward way of
 yielding, in which less code is executed, is as follows:

 while (PeekMessage((LPMSG)&msg,msg.hwnd,0,0,PM_REMOVE))
     {
     /* translate and dispatch the messages if you wish */
     }

 When the queue is empty, PeekMessage() will yield.


 549. Dialog Box Placement

 Question:

 I have a small dialog box that I want to appear at a definite location
 on the screen. In this case, I want the left side of the dialog box to
 be flush with the left side of the screen. How do I do this?

 Response:

 Add the following code to your dialog-box routine to move the dialog
 box before it gets displayed:

     RECT r;

     case WM_INITDIALOG:
         GetWindowRect(hDlg,(LPRECT)&r);
         MoveWindow (hDlg, 0,0,r.right-r.left,r.bottom-r.top , TRUE);
         break;

 GetWindowRect() will get the current size of the window and the
 MoveWindow() call will move the window to the left of the screen so
 that it is at the same Y position at which it was originally located.


 550. WinSDK 2.10 READCVW.TXT: Terminating the Debugging Session

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Terminating the Debugging Session

 You must terminate Windows before terminating CVW or using the CVW
 Quit command. If you attempt to terminate CVW before terminating
 Windows, the system will fail to run properly.


 551. WinSDK 2.10 READCVW.TXT: The Stack Trace Command

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 The Stack Trace Command

 In order for the Stack Trace command (or the Calls menu) to work
 reliably, you need to 1) execute at least to the beginning of the main
 function or procedure and 2) make sure that the current module has
 full CodeView information (make sure module was compiled or assembled
 with /Zi).


 552. WinSDK 2.10 READCVW.TXT: Using the Enhanced Keyboard

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Using the Enhanced Keyboard

 An enhanced keyboard has function keys along the top and an extra set
 of direction keys (in addition to the ones on the numeric keypad). If
 you use one of these keyboards, you may experience the following
 behavior: you are executing Windows, you press the CTRL+ALT+SYSREQ
 sequence to move to CVW, you return to Windows, and then the system
 acts as if the CTRL key were depressed. If you experience this
 condition, you can turn off the CTRL key by doing the following two
 steps: 1) turn the NUM LOCK condition off if it is on; and 2) press
 the CTRL key, and then press the ALT key twice. (Do not hold the CTRL
 key down when you press the ALT key.)

 With an enhanced keyboard, CTRL+ALT+DEL is disabled in Windows/386
 when running under CVW.


 553. WinSDK 2.10 READCVW.TXT: Shell/Restart Commands Not Supported

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 The Shell Command

 The DOS Shell command (!) is not supported.

 The Restart Command

 The Restart command (L) is not supported.


 554. WinSDK 2.10 READCVW.TXT: Debugging Dynamic-Link Libraries

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Debugging Dynamic-Link Libraries

 You can use the /L option to load symbolic information for
 dynamic-link libraries as well as for applications. For example, to
 debug both the application GRAPHAPP and the dynamic-link library
 GRAPHDLL, enter the following command at the DOS prompt:

    CVW /2 /L GRAPHAPP.EXE /L GRAPHDLL.EXE WIN.COM

 Remember that you use the /2 option only with a secondary monitor.

 Using the /L option with GRAPHDLL.EXE provides CodeView with symbolic
 information for line numbers, variables, and functions within the
 GRAPHDLL dynamic-link library. To make use of this symbolic
 information, you would also need to start an application from within
 Windows (in this case, GRAPHAPP) that calls functions stored in
 GRAPHDLL.


 555. WinSDK 2.10 READCVW.TXT: Debugging Multiple Applications

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Debugging Multiple Applications

 You can easily debug multiple applications. When you enter the
 CodeView command line, use the /L option with each distinct
 application you wish to debug. For example:

    CVW /2 /L PROG1.EXE /L PROG2.EXE /L PROG3.EXE WIN.COM

 The example above loads symbolic information for three applications.
 However, to debug an application, you must execute it as well as load
 symbolic information. To execute an application, select it from within
 Windows. In the example above, you could debug PROG1, PROG2, or PROG3
 by itself, or you could run all three concurrently and have symbolic
 information available for all of these applications.

 To debug multiple instances of the same application, do not list the
 application twice on the CodeView command line. Instead, you can debug
 up to eight instances of the same application simply by starting new
 instances. CodeView automatically keeps track of separate stack and
 variable information for each instance. In verbose mode (which is
 enabled when you start CodeView with the /V option) memory allocation
 messages indicate a number next to the word "Instance." The first
 instance has the number 0, the second has the number 1, and so forth
 up to the number 7.


 556. WinSDK 2.10 READCVW.TXT: Setting Breakpoints

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Setting Breakpoints

 When you debug multiple applications or dynamic-link libraries,
 special rules apply for setting breakpoints.

 If you set a breakpoint at a line number, then CodeView sets the
 breakpoint in the module that is currently displayed. If this module
 is not currently residing in real memory, then CodeView sets a virtual
 breakpoint. CodeView handles all management of virtual breakpoints,
 and activates them when the corresponding segment is loaded into real
 memory.

 If you set a breakpoint at a label, then CodeView first looks for this
 label in the module that is currently displayed. If the label is not
 found, CodeView then searches through the list of modules with
 symbolic information. Modules are searched in the order that you
 specified them on the command line (by using the /L option). As soon
 as CodeView finds a module that contains the given label, it sets the
 breakpoint.


 557. WinSDK 2.10 READCVW.TXT: Setting Flags in the WIN.INI File

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Setting Flags in the WIN.INI File

 Do not set the EnableEMSDebug flag in your WIN.INI file. This flag is
 useful with SYMDEB, but it prevents CodeView from running reliably.


 558. WinSDK 2.10 READCVW.TXT: CVPACK, Debug Information Compactor

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 After compiling and linking a program with CodeView debugging
 information, you can use CVPACK, the Microsoft Debug Information
 Compactor, to reduce the size of the executable file. CVPACK
 compresses the debugging information in the file and allows CodeView
 to load larger programs without running out of memory.

 The CVPACK utility has the following command line

    CVPACK [/p] <exefile>

 in which /p is a command-line option. You should replace <exefile>
 with the name of your executable file.

 The /p option results in the most effective packing possible, but
 causes CVPACK to take longer to execute. When you use the /p option,
 CodeView discards unused debugging information and sorts the packed
 information. When you do not use the /p option, CodeView simply
 appends packed information to the end of the file.

 To debug a file altered with CVPACK, you must use Version 2.10 or
 later of the CodeView debugger.


 559. WinSDK 2.10 READCVW.TXT: Symbolic Information, Windows Files

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains to
 CodeView for Windows (CVW).

 Symbolic Information for Windows Files

 You can get symbolic information for Windows files whenever the
 following conditions are true: 1) the .SYM files for Windows are
 present in the current directory, and 2) the Windows FatalExit routine
 is called. The debug version of the FatalExit routine searches the
 current directory for .SYM files corresponding to any .EXE files that
 it finds executing in memory. The FatalExit routine then prints out a
 stack trace in the CVW dialog window. If FatalExit can find the
 appropriate .SYM files, then it uses symbolic information from these
 files to print symbolic names in the stack trace display.

 Therefore, you can benefit from having up-to-date .SYM files residing
 in the directory from which Windows is executing. The .SYM files let
 Windows display meaningful information when FatalExit is called.


 560. WinSDK 2.10 READCVW.TXT: Error Messages

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Error Messages

 The error message "? cannot display" indicates that the Display
 Expression command (?) has been passed a valid symbol it cannot
 display. In previous versions of CodeView, structures could not be
 displayed. However, the current version displays all data types except
 for the enums type.

 The error message "Expression not a memory address" occurs when the
 Tracepoint command is given without a symbol that evaluates to a
 single memory address. For example, the commands TP?1 and TP?a+b each
 produce this error message. The proper way to put a tracepoint on the
 word at address 1 is with the command TPW 1.

 The error message "Function call before 'main'" occurs when you
 attempt to evaluate a program-defined function before entering the
 main function. Execute at least to the beginning of the main function
 before attempting to evaluate program-defined functions.

 The error message "Bad emulator info" occurs when CodeView cannot read
 data from the floating-point emulator.


 561. WinSDK 2.10 READCVW.TXT: Microsoft Pascal Programs

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Microsoft Pascal Programs

 In this release, Microsoft Pascal programs cannot be debugged with the
 CodeView debugger.


 562. WinSDK 2.10 READCVW.TXT: The Segment/Task Message

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 The Segment/Task Message

 A code segment starts at a specific address, and it corresponds to a
 currently existing task. When CodeView loads a code segment into
 memory, it identifies both the segment address and the number of the
 corresponding task. For example:

    segment: D005 - task: 0003

 The example above indicates that a code segment of task 0003 has been
 loaded, and that the segment address of this segment is D005.


 563. WinSDK 2.10 READCVW.TXT: Additional Messages in Verbose Mode

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains
 to CodeView for Windows (CVW).

 Additional Messages in Verbose Mode

 When you run CodeView in verbose mode (by using the /V option),
 CodeView displays messages about expanded-memory swapping, in addition
 to the segment-movement messages presented in the documentation.

 This section briefly discusses two kinds of messages that CodeView
 displays in verbose mode: newtask and flushtask and in and out.

    The Newtask and Flushtask Messages

    The "newtask" message indicates that Windows has loaded a new task.
    (The term "task" is interchangeable with the word "application.")
    The "flushtask" message indicates that a task is being terminated.
    Each "newtask" and "flushtask" message indicates the relevant task
    number. For example:

    newtask: 0003
    flushtask: 0003

    The In and Out Messages

    Windows can place code segments in the expanded-memory area, and
    then swap them in and out. (In this context, "swapping" refers to
    the process of selecting an active page of memory from the
    expanded-memory area.) However, Windows must store data at static
    addresses below 640K. Each time Windows swaps a code segment,
    CodeView displays an "in" or "out" message that gives two pieces of
    information: 1) the task number of the code segments swapped in or
    out, and 2) the current instance of this task, as indicated by
    where the data of the instance is stored. For example:

    in: 0003 - DS: 6003
    out: 0003 - DS: 6003

    In the example above, all the code segments for task 0003 are
    swapped in, and then they are swapped out. Data for the current
    instance of task 0003 is stored at the address 6003.


 564. WinSDK 2.10 READCVW.TXT: Display Global Heap Command (DGH)

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains to
 CodeView for Windows (CVW).

 Display Global Heap Command (DGH)

 The DGH command displays a list of the global memory objects in the
 global heap. The list has the following form:

 segment-address: size segment-type owner

 The segment-address field specifies the segment address of the first
 byte of the memory object. The size field specifies the size in
 paragraphs (multiples of 16 bytes) of the object. The segment-type
 field specifies the type of object. The type can be any one of the
 following:

     Segment Type        Meaning

     CODE                Segment contains program code.

     DATA                Segment contains program data and possible
                         stack and local heap.

     PRIV                Segment contains private data.

     FREE                Segment belongs to pool of free memory objects
                         ready for allocation by an application.

     SENTINEL            Segment marks the beginning or end of the
                         global heap.

 The owner field specifies the module name of the application or
 library that allocated the memory object. The name PDB is used for
 memory objects that represent program descriptor blocks. These blocks
 contain execution information about applications.


 565. WinSDK 2.10 READCVW.TXT: Using CodeView with Windows/386

 The following information was taken from the Microsoft Windows
 Software Development Kit Version 2.10 CVWREAD.TXT file and pertains to
 CodeView for Windows (CVW).

 Overview

 To use CVW, you must have installed expanded memory that conforms to
 the Lotus/Intel/Microsoft (LIM) memory specification, Version 4.0.
 (You should have a two megabyte system in order to run CodeView with
 Windows/386.) However, the retail version of Windows/386 has a LIM 4.0
 emulator built in. You do not need to have LIM 4.0 memory if you use
 retail Windows/386. If you do not already have a LIM 4.0 driver
 installed, then follow the instructions below to run CVW.

 System Requirements

 To prepare to use CVW with Windows/386, you need to have the
 following:

 1. A retail version of Windows/386, Version 2.1.

 2. The Windows Software Development Kit (SDK), Version 2.1.

 3. At least two megabytes of extended memory hardware.

 Procedure

 To run CVW with Windows/386, complete the following steps:

 1. Run the Windows SDK MKDEBUG.BAT file as follows:

       MKDEBUG WIN386

 2. Execute the following commands from DOS:

       COPY WIN86.COM WIN.COM
       COPY COMMAND.COM WIN86.COM

 3. Issue the following command from DOS:

       WIN386

    This command invokes COMMAND.COM and a standard DOS shell appears.
    Although nothing appears to have changed, you are now inside a
    Windows 386 virtual machine running DOS.

 6. Start the CodeView debugger with a command line similar to the
    following:

       CVW /2 /L <yourfile.exe> WIN.COM

 7. After you exit from the debugger, type "exit" to exit the Windows
    virtual machine.

 Note on Expanded-Memory Error Message

 Depending on the configuration of your 80386 computer, you may get one
 of the following messages from CodeView:

    "Need at least four 16K pages in EMS"
    "EMS Internal Error"

 These messages indicate that a 64K page frame is not available for the
 CodeView debugger. A page frame is an area of real-mode memory (that
 is, memory in the one-megabyte memory address space of the 8086) to
 which EMS memory is mapped. If you receive one of these messages, you
 can force Windows/386 to give CodeView the page frame it needs by
 placing the following statements in your WIN.INI file:

    [Win386]
    EMMPageFrame=0C000

 Be warned that these statements take away from memory reserved for
 hardware devices. Therefore, they can have side effects such as
 disabling the network. If the statements above cause the system to
 fail, then enter a page-frame setting other than 0C000, or start
 Windows without using the network. You may have to try a number of
 different page-frame settings before finding the one that gives you
 the best results. Page frame settings are rounded down by 0400
 hexadecimal. Therefore, if a setting of 0C000 does not work, try new
 settings in increments of 0400: 0C400, 0C800, 0CB00, and so forth.

 Note that the hexadecimal number in the EMMPageFrame statement must
 include a leading zero.


 566. Sending Raw Data to the Printer: RAW.DRV

 Question:

 How can an application send raw, unformatted data to a port using the
 spooler so that multiple applications can send data to the port without
 conflict?

 Response:

 The best method is to use a GDI device driver that simply passes raw
 data. RAW.DRV is a simple raw driver that allows an application to
 pass data through Escape(..., PASSTHROUGH, ...). RAW.ARC is an archive
 containing the sources and a sample usage for RAW.DRV.

 RAW.ARC can be found in the Software Library by searching for the
 filename RAW.ARC, the Q number of this article, or S12035.


 567. PostAppMessage() Versus PostMessage()

 Question:

 What is the difference between PostAppMessage() and PostMessage(), and
 when should I use one or the other?

 Response:

 In most cases, you will want to use PostMessage(). Essentially, they
 both do the same thing, except that PostMessage() uses a window
 handle, and PostAppMessage() uses a task handle.

 Since it is possible to have a "task" that does not have a "window"
 associated with it (but not vice versa), there will be situations in
 which you will want to be able to send a message, but there is no
 window handle to use as a parameter to PostMessage(). In this
 situation, you will want to use PostAppMessage() to send the message
 by way of the task handle (you can use GetCurrentTask() to get a task
 handle).

 However, if your application uses PostAppMessage(), your message loop
 will need to be modified to account for this. When GetMessage()
 retrieves a message off the queue that was posted with
 PostAppMessage(), the hwnd value of the message structure is NULL
 because no window was specified as the target (and a task can have
 many windows). Thus, you will want to watch for this special case and
 do whatever processing is appropriate. Some possibilities for doing
 this are as follows:

 1. You could handle the message within your message loop.

 2. You could pass the message to another procedure that was set up to
    handle special cases.

 3. You could set the hwnd value of the MSG structure to the window
    handle of the window that you want to get the message.

 4. You could change any of the other values in the MSG structure prior
    to passing the message along.

 The following C source code shows an example of processing a
 PostAppMessage() call. Once the window is drawn, pressing the left
 mouse button in the window will generate a PostMessage() call, and
 pressing the right mouse button will generate an identical
 PostAppMessage() call. The message loop will beep the speaker on
 receipt of a PostAppMessage() call, then pass the message to the main
 window.

 /**********************************************************************
  * MinWin - PostMessage versus PostAppMessage example.
  */
 #include <windows.h>

 char szAppName[] = "MinWin";

 HWND    hMainWnd;

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

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

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

         if (!RegisterClass (&wndclass))
             return FALSE;
     }

     hMainWnd = CreateWindow (szAppName, szAppName,
         WS_OVERLAPPEDWINDOW,
         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
         NULL, NULL, hInstance, NULL);

     ShowWindow (hMainWnd, nCmdShow);
     UpdateWindow (hMainWnd);

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

         if (msg.hwnd == NULL) {     /* For now, lets just */
             MessageBeep(0);         /* pass this message  */
             msg.hwnd = hMainWnd;    /* to our only Window */
         }

         TranslateMessage (&msg);
         DispatchMessage (&msg);
     }
     return msg.wParam;
 }

 long FAR PASCAL WndProc (hWnd, iMessage, wParam, lParam)
      HWND     hWnd;
      unsigned iMessage;
      WORD     wParam;
      LONG     lParam;
 {
     HANDLE hInst;
     HANDLE hTask;

     hInst = GetWindowWord (hWnd, GWW_HINSTANCE);

     switch (iMessage) {

         case WM_LBUTTONDOWN:
             PostMessage (hWnd, WM_USER, 0, 0L);
             break;

         case WM_RBUTTONDOWN:
             PostAppMessage (GetWindowTask(hWnd), WM_USER, 0, 0L);
             break;

         case WM_USER:
             MessageBox (GetFocus(), "Message Recieved", "WM_USER", MB_OK);
             break;

         case WM_DESTROY:
             if (hWnd == hMainWnd)
                 PostQuitMessage (0);
             break;

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


 568. Detecting ANSI Fonts

 Question:

 How can an application tell which fonts support the full ANSI
 character set?

 Response:

 For any given font, look at the tmCharSet field in the TEXTMETRIC
 structure, which you get by enumerating the fonts or selecting a
 particular font. If the tmCharSet field is not ANSI_CHARSET, the font
 is definitely not ANSI. If it is ANSI_CHARSET, you need to know if the
 font is generic or device dependent. To tell if a font is generic or
 device specific, enumerate the fonts and check the DEVICE_FONTTYPE bit
 in the fonttype parameter of the enumeration callback function. If
 the font is generic and ANSI_CHARSET, it's ANSI. If the font is device
 specific, you can't tell if it is ANSI because some driver
 manufacturers have not accurately filled in this field.


 569. Some GDI Functions Fail on Banding Printers


 Some GDI functions that work on the display fail on banding printers.

 When an application prints using the NEWFRAME escape on a banding
 device, GDI diverts each page to an intermediate metafile. This
 metafile is then played back to each band on the device. A problem
 arises when the application tries to use a GDI function that isn't
 supported in metafiles. The result is that the function produces no
 output.

 The workaround is to use NEXTBAND instead of NEWFRAME on banding
 devices so that no metafile is used. Note that not all nonbanding
 devices support NEXTBAND, so the application must check if the device
 is a banding one to determine which escape to use. Banding devices can
 be detected by checking the RC_BANDING bit in the GetDeviceCaps()
 RASTERCAPS field.

 For a list of valid GDI functions with metafiles and banding printers,
 please refer to the article titled, "Valid GDI Functions With
 Metafiles and Banding Printers," Q34585.


 570. Scrolling Quickly in Windows

 Question:

 I have used both the ScrollWindow() function and the ScrollDC()
 function. They are both efficient and fast functions; however, I
 scroll a large amount of data and need something faster if it is
 possible. Is there another function you can suggest?

 Response:

 The ScrollWindow() and ScrollDC() functions use BitBlt() to move the
 image that is being scrolled. Windows doesn't know what type of data
 is being scrolled so it has to scroll the image as a bitmap.
 Therefore, if the image is easy for you to redraw (text or simple
 graphics), you will probably be able to get faster scrolling by
 invalidating the entire area that is scrolled and then redrawing the
 image in your own WM_PAINT message-handling routine.


 571. DialogBox() Being Delayed by Pending Messages

 Question:

 After a DialogBox() function has been issued, and many messages are
 pending for the current applications queue, it appears that the
 messages are done first. This has the undesirable effect of locking
 out input (both keyboard and mouse) to the application until the
 dialog box appears. In some situations, this may be minutes because
 data is potentially coming down the communication lines every six
 seconds, causing several plots/charts to be updated. How can I force
 the dialog box to appear first?

 Response:

 To get the dialog box to come up faster when other messages are
 waiting to be processed, use the CreateDialog() or the
 CreateDialogIndirect() function to create the dialog box. To then make
 the dialog box look modal, use the EnableWindow(hWnd, FALSE) call to
 disable the parent of the dialog box before calling CreateDialog().


 572. CodeView Memory Requirements

 Question:

 What are the memory requirements for CodeView for Windows (CVW)?

 Response:

 To use this version of the CodeView debugger, you must have expanded
 memory installed in your computer. This expanded memory must conform
 to the Lotus/Intel/Microsoft expanded-memory specification, Version
 4.00. The CodeView debugger places its symbol table in the
 expanded-memory area. By doing so, the debugger frees up memory for
 both Windows and applications. Microsoft recommends a (minimum)
 2-megabyte system (motherboard RAM and expanded memory combined) to run
 CodeView for Windows (CVW).


 573. Using EM_SETSEL to Set Caret Position

 Question:

 How can I position the caret in an edit control to a specific
 character position?

 Response:

 You can use EM_SETSEL to position your caret. To do so, pass the
 low-order word and high-order word of the lParam as the same position.
 This will position the caret immediatly before the position requested.
 For example, if you pass 3 as the low-order word and high-order word,
 the caret will be positioned immediately before the fourth character
 (the character at position 3 in a 0-based scheme).


 574. Detecting Keystrokes when Menu Is Pulled Down

 Question:

 I need to know how to recognize when a key is pressed when a menu or a
 menu item is selected through the keyboard interface so that I can
 implement a help system to get context-sensitive help on a menu or a
 menu item using the keyboard interface. How is this done?

 Response:

 You can accomplish this with a message filter hook (WH_MSGFILTER).
 Your application sets the hook with the SetWindowsHook() function.
 This hook allows you to process messages bound for dialog boxes,
 message boxes, and menus before they are received. Therefore, if a
 menu is pulled down, your application's hook function will still
 receive the key-up and key-down messages. The hook functions are
 documented starting on Page 466 of the "Microsoft Windows Software
 Development Kit Programmer's Reference."


 575. Using EM_GETSEL to Get Caret Position

 Question:

 From experimenting with the caret position, I come to the conclusion
 that the caret position that I get when using the GetCarPos() function
 is relative to the edit-control window. How can I get the position of
 the caret relative to the beginning of my text buffer?

 Response:

 You can obtain the character position of the caret by sending the
 EM_GETSEL message to the edit control. The SendMessage() function will
 return the starting position in the low-order word and the position of
 the first unselected character in the high-order word. If there is no
 text selected, the low-order word and the high-order word will be the
 same. This value will be the character immediately preceding the
 caret.


 576. CodeView: Unexpected Behavior Setting Breakpoints/Watchpoints

 Problem:

 When I set a watchpoint or a tracepoint in CodeView for Windows (CVW),
 then enter the "go" command, the system hangs or the mouse becomes
 erratic and the keyboard does not respond.

 Response:

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.

 You can work around the problem by deleting all of your watchpoints
 before entering the "go" command. Another workaround you can try is
 tracing instead of executing through this area of code.

 This is a problem inherent in the CV line and is emphasized in the
 Windows environment. When you set a watchpoint, the watched area in
 memory is checked each time a line of code is executed. Under
 low-memory conditions this can have the effect of almost stopping
 your system.


 577. RC.EXE Switch Definitions

 The switches to the Windows Version 2.03 resource compiler are as
 follows:

     -?
     -H
     -h
         Help

         Prints a help message and quits.

     -N
     -n
         NewExeFormat

         Internal, not for application use.

     -R
     -r
         OnlyMakeBinFile

         Makes only the binary .RES file as documented in the
         "Microsoft Windows Software Development Kit Programmer's
         Reference" manual.

     -V
     -v
         Not currently supported. Ignored.

     -S
     -s
         SetSwapAreaSize

         Sets the swap area size similar to the way it's set by
         SetSwapAreaSize(). It must be followed by an additional
         argument of the following form to set the size in kilobytes
         or paragraphs:

             ddddK

             or

             ddddP

     -L
     -l
     -LIM32

         LIM32

         Enables the application to make LIM 3.2 calls by allocating a
         page frame as documented in the "Microsoft Windows Software
         Development Kit Programming Tools" manual in Section 3.3 on
         Page 29.

     -M
     -m
         MultInst

         Puts multiple instances of an application in separate banks.
         This is documented in the "Microsoft Windows Software
         Development Kit Programming Tools" manual in Section 3.3 on
         Page 29. This effect is achieved by loading each new instance
         as if the existing instance were not there so that a new
         module is created. This causes each instance to behave as if
         it were a different application.

     -E
     -e
         EmsLibrary

         Enables DLLs loaded through LoadLibrary() (generally GDI device
         drivers) to use memory above the bank line. By default all
         such library's GlobalAlloc()s will go below the bank line,
         regardless of the flags used. The -e switch reenables the
         GMEM_NOT_BANKED flag.

         Please refer to the KnowledgeBase articles Q26456 and Q26434
         for a more complete description of this switch.



 578. Vector Font File Format Omitted from Version 2.x Manual

 The section titled "Vector Font File Format" was omitted from Chapter
 7, Section 7.2 of the Version 2.00 "Microsoft Windows Software
 Development Kit Programmer's Reference." The material is included in
 the Versions 1.03 and 1.04 "Microsoft Windows Software Development Kit
 Programmer's Reference" in Appendix C, Section C.2.2.

 The original section is as follows:

     Vector Font File Format

     The header information for a vector font file is described in the
     section "Font File Formats". This section describes some additional
     information for vector font files.

     The dfCharOffset field is used to specify the location and usage
     of the character strokes in the bitmap area. For fixed-pitch fonts,
     each two-byte entry is an offset from the start of the bitmap area
     to the beginning of the strokes for the character. For variable-
     pitch fonts, each four-byte entry consists of two bytes giving the
     offset (as for fixed-pitch) and two bytes giving the width of the
     character.

     For both fixed- and variable- pitch fonts, the bitmap area is the
     same. Each character is composed of a series of vectors consisting
     of a pair of signed relative coordinates starting from the character
     cell origin. Each pair may be preceded by a special value indicating
     that the next coordinate is to be a pen-up move. The special pen-up
     value depends on how the coordinates are stored. For one-byte
     quantities, it is -128 (080H) and for two-byte quantities, it is
     -32768 (08000H).

     The character cell origin must be at the upper left corner of the
     cell so that the character hangs down and to the right of where it
     is placed.

     The storage format for the coordinates depends on the size of the
     font. If either dfPixHeight or dfMaxWidth is greater than 128, the
     coordinates are stored as two-byte quantities; otherwise, they are
     stored as one-byte quantities.


 579. CodeView: Fatal Exit 0x0280

 Problem:

 I receive "Fatal Exit 0x0280" when entering CodeView for Windows
 (CVW).

 Response:

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.

 You can work around the problem in the following manner:

 1. If you are running Windows/286 and have told Setup that you have
    extended memory or are running Windows/386 (e.g. you have installed
    USERF.EXE), try installing with USERS.EXE. This is done by running
    Windows/286 Setup again and telling Setup that you do not have
    extended memory.

 2. Do not load network software.

 3. Do not load TSRs.


 580. Definitions: Active Application, Active Window, Input Focus

 Although the concepts of "input focus," "active application," and
 "active window" are very closely related, there are differences
 among them.

 1. The "input focus" determines which window receives keyboard input.

 2. The "active window" is the window that receives the user's
    attention. If it's an overlapped window or a pop-up window with a
    caption bar, the caption bar is highlighted. If it's a dialog
    window, the frame is highlighted. Either the active window or one
    of its child windows has the focus.

 3. The "active application" is the application that created the window
    that has the input focus.

 The following discussion contains detailed definitions of the terms
 "input focus," "active window," and "active application," as well as a
 demonstration of those differences that you can perform using Windows
 Write.

 Definition of Terms

 The following is from the glossary of "Programmer's Guide to Windows,
 Second Edition," by David Durant, Geta Carlson, and Paul Yao,
 published by Sybex, Page 647:

     Active Application

     The active application is the application that created the window
     that currently has the keyboard input focus. Applications do not
     need to be the active application in order to receive and process
     messages. Applications are notified by message whenever they are
     gaining or losing the status of "the active application." The user
     normally determines the active application, but applications can
     override this decision.

 For more information on the active application, refer to the message
 WM_ACTIVATEAPP in the "Microsoft Windows Software Development Kit
 Programmer's Reference."

 The following is also from "Programmer's Guide to Windows, Second
 Edition," Page 655:

     Focus

     Keyboard input is transferred to the application as messages.
     Since several windows may be visible on the screen simultaneously,
     there must be a method for determining which of these windows
     should receive the keyboard input messages. In Windows, the window
     that has the focus is the window that will receive the keyboard
     messages. Normally, the user controls which window has the focus
     by use of the mouse, but applications can transfer the focus from
     window to window themselves.

 For more information on focus, refer to the messages WM_SETFOCUS and
 WM_KILLFOCUS and the API calls GetFocus(), SetFocus(), and
 EnableWindow() in the Windows SDK programmer's reference.

 The following is from the book "Programming Windows" by Charles
 Petzold, published by Microsoft Press, Page 127:

     Focus, Focus, Who's Got the Focus?

     The concept of input focus is closely related to the concept of
     "active window." The window with the input focus is either the
     active window or a child window of the active window. The active
     window is usually easy to identify. If the active window is an
     overlapped or popup window with a caption bar, Windows highlights
     the caption bar. If the active window has a dialog frame instead
     of a caption bar (a form most commonly seen in dialog boxes),
     Windows highlights the frame. If the active window is an icon,
     Windows displays the window's caption bar text below the icon.

     The most common child windows are controls such as push buttons,
     check boxes, scroll bars, edit boxes, and list boxes that usually
     appear in a dialog box. Child windows are never themselves active
     windows. If a child window has the input focus, then the active
     window is its parent. Child window controls indicate that they
     have the input focus generally by using a flashing cursor or
     caret.

     If the active window is an icon, then no window has the input
     focus. Windows continues to send keyboard messages to the icon,
     but these messages are in a different form from keyboard messages
     sent to active windows that are not icons. A window function can
     determine when it has the input focus by trapping WM_SETFOCUS and
     WM_KILLFOCUS messages. WM_SETFOCUS indicates that the window is
     receiving the input focus, and WM_KILLFOCUS signals that the
     window is losing the input focus.

 Charles Petzold indicated that Windows sends keyboard messages to
 icons differently than it does to windows with the focus. This
 difference is noted in the following excerpt from "Microsoft Windows
 Software Development Kit Programmer's Reference," Page 430:

     If a window is active but doesn't have the focus (that is, no
     window has the focus), any key pressed will produce the
     WM_SYSCHAR, WM_SYSKEYDOWN, or WM_SYSKEYUP message.

 For more information on the active window, refer to the message
 WM_ACTIVATE and the API calls GetActiveWindow() and SetActiveWindow()
 in the Windows SDK programmer's reference.

 Demonstration of Differences between Focus, Active Application, and
 Active Window

 The following is an experiment you can do that will clarify the
 differences among active application, active window, and input
 focus, and show you how the user can specify which attribute is
 assigned to which window:

 Go into Windows Write and select Find from the Search menu. You'll now
 have a pop-up window that is a child of Write. Arrange your screen so
 that the Write, Find, and MS-DOS Executive windows are all on the
 screen together.

 If you click on the MS-DOS Executive window, both the Write and Find
 windows' caption bars will be set to the inactive color, and the
 MS-DOS Executive window's caption bar will be set to the active color.
 At this point, MS-DOS Executive is the active application; it is also
 the active window and has the input focus.

 Click on the Write window; it will become the active application, as
 well as the active window, as well as getting the input focus.

 Click on the Find window; although it is now the active window, it is
 not the active application (Write is) nor does it have the input
 focus: the focus is with the "Find What" window, which is an edit
 control window that is a child of the Find window, which is itself a
 pop-up-style window that is a child of Write. (Note that in this
 discussion the term "child windows" is used in terms of parent-child
 relationships rather than the child-style window, WS_CHILD.)

 At this point, you can move the input focus from window to window
 within the Find window by using the TAB key (or by clicking with the
 mouse). There are three or four windows you can move between: "Find
 What," "Whole Word," "Match Upper/Lowercase," and "Find Next" (only
 available if there is some text in the "Find What" window). The TAB
 key moves the focus between controls.

 Now press ALT+F6. This will make Write the active window instead of
 Find, and the focus will be set to the Write window as well; this is
 indicated by the flashing vertical caret. Press ALT+F6 a second time.
 Find will be the active window again, and the input focus will be set
 to whichever control it was last set to (Find What, Whole Word, Match
 Upper/Lowercase, or Find Next).

 Finally, you can use ALT+TAB to return to the MS-DOS Executive, making
 it the active application, the active window, and the one with the
 input focus. If you press ALT+TAB again, you will make Write the
 active application, Find the active window, and set the input focus to
 one of Find's controls (whichever one last had the focus).


 581. Using SetPriority() to Lower Task Priority

 Using the SetPriority() function to lower the priority of a task can
 result in the task never gaining control of the CPU. Consider the
 following scenario:

 Three tasks are running. Tasks 1 and 2 are running at priority 0. Task
 3 is running at priority -8. When Windows looks for tasks to give
 control to, it first tries to give control to those with highest
 priority, in this case, tasks 1 and 2. When a task has control of the
 processor, it will retain control as long as it has messages to
 process. In this example, as long as tasks 1 or 2 have messages to
 process, Windows will continue to give control to those two processes,
 and task three will starve.

 If you are going to lower the priority of a task, you must be careful
 that all other tasks with higher priority will eventually yield;
 otherwise, the lower priority task will never gain control of the
 processor. In general, it is recommended that you leave the priority
 settings at the default value of zero.


 582. Windows Spawning Application

 The following application note can be obtained either by calling
 Microsoft Product Support Services at (206) 454-2030, or by searching
 for the appropriate files in the Software Library (see below).

 Spawning Applications
 ---------------------

 Spawning a Windows or MS-DOS application from within a Windows
 application is done using Interrupt 21 function 4B. The WSPAWN.ASM
 file contains the source for Int21Function4B, which is an example of
 the use of this function. The object module obtained by compiling this
 file can be linked with a Windows application and used to spawn both
 MS-DOS and Windows applications.

 Special Notes
 -------------

 When spawning old MS-DOS applications it is best to spawn the PIF file
 for the respective application. This way you can run the application
 in a window.

 Some modifications must be made to the various files of your
 application. In your Definition file (FILENAME.DEF), you must add a
 SEGMENTS line as follows:

       SEGMENTS
          ASMCODE   CLASS'  ASMCODE'  FIXED LOADONCALL READWRITE

 You must then prototype this function in your include file
 (FILENAME.H), as follows:

       extern int far pascal Int21Function4B (BYTE, LPSTR, LPSTR);

 Also, remember to make the needed modifications to your make file
 (FILENAME.), as follows:

       WSPAWN.OBJ   : WSPAWN.ASM
            masm WSPAWN;

       FILENAME.EXE : FILENAME.OBJ FILENAME.DEF FILENAME.RES WSPAWN.OBJ
            link4 FILENAME WSPAWN, /align:16, /map, slibw/NOE, FILENAME

 The following are the definitions to be added to your source file
 (FILENAME.C):

       char     szSpawnAppName  [40];
       char     szCommandLine     [40];

       typedef struct {
                       WORD   environment;
                       LPSTR  commandline;
                       LPSTR  FCB1;
                       LPSTR  FCB2;
                      } EXECBLOCK;
       EXECBLOCK      exec;
       WORD           wFCB1Contents[2];

 Once these modifications have been made, the Int21Function4B()
 function can be used to spawn applications. The following is an
 example of the use of this function:

           GlobalCompact(-1L);
           LockData(0);

           exec.environment = 0;
           exec.commandline = szCommandLine;

           wFCB1Contents[0] = 2;
           wFCB1Contents[1] = SW_SHOWNORMAL;
           exec.FCB1 = (LPSTR)wFCB1Contents;
           exec.FCB2 = (LPSTR)NULL;

           Int21Function4B(0, (LPSTR)szSpawnAppName,
                           (LPSTR)&exec);

           UnlockData(0);

 Before spawning an application, it is best to perform a
 GlobalCompact() with a -1L as a parameter. This GlobalCompact() is
 optional. You must make sure to execute a LockData() call to lock the
 current data segment in memory. After the function is used, you can
 make an UnlockData() call to free the current data segment. The
 wFCB1Contents structure contains the attribute used in determining the
 way the spawned program will be opened. The first word in
 wFCB1Contents contains the size in bytes of the second entry in
 wFCB1Contents. The value of the second entry in wFCB1Contents,
 SW_SHOWNORMAL, will tell Windows to open this window as a normal
 window. This value could also be set to SW_SHOWMINIMIZED or one of the
 many other values listed in the "Microsoft Windows Software
 Development Kit Programmer's Reference" under the ShowWindow()
 function.

 As you may have noticed, there are two strings that must be filled
 before this section of the code is processed. These strings are
 szCommandLine, and szSpawnAppName. The szSpawnAppName string should be
 filled with the name of the application you wish to spawn, as in the
 following example:

      sprintf (szSpawnAppName,"CLOCK.EXE");
 or
      sprintf (szSpawnAppName,"COMMAND.COM");

 The szCommandLine string can be filled differently according to the
 type of application you are trying to spawn. When you are spawning an
 MS-DOS application, the first character of the string must contain the
 number of characters in the command line. The following is an example
 of a command line sent to COMMAND.COM, to type a sorted directory
 listing:

     sprintf (szCommandLine, "%c /c dir | sort", 14);

 The number 14, which represents the length of this particular command
 line string, is placed in the first character of the string. This
 length is determined by starting at the first blank (the second
 character of the string) and counting each character.

 There is more flexibility when spawning Windows applications. You can
 fill the first character of the command-line string with the length of
 the string, a blank, or the first character of the string. For
 example, when spawning the Notepad Windows function, all of the
 following command lines are valid:

       sprintf (szCommandLine, "%c  WINSPAWN.C", 11);

       sprintf (szCommandLine, "  WINSPAWN.C");
 or
       sprintf (szCommandLine, "WINSPAWN.C");

 As you have probably gathered from these examples, it is possible to
 combine these two types of spawning into a generic spawning routine
 that will spawn both MS-DOS applications as well as Windows
 applications. Obtaining the application name, extension, and command
 line from a dialog box, these three strings can be inserted into the
 exec structure in such a way as to handle both types of applications.
 Given a dialog box named SpawnDlgBox and a procedure that modifies
 three strings, szDlgSpawnAppName, szDlgExtension and szDlgCommandLine,
 the following is one way to accomplish this task:

       DialogBox (hInstMain, (LPSTR) "SpawnDlgBox", hWnd, lpProcSpawnDlgBox);
       sprintf (szSpawnAppName, "%s.%s", szDlgSpawnAppName, szDlgExtension);
       sprintf (szCommandLine, "%c %s",
                   strlen (szDlgCommandLine) + 1, szDlgCommandLine);

 If you do not send a command line to the spawned application, you
 should clear the szCommandLine string.

 For more detail on these examples, please consult the source code in
 WINSPAWN.C in the Software Library. This file can be found by
 searching on the keyword WINSPAWN, the Q number of this article, or
 S12011.


 583. PeekMessage() Documentation Is Incorrect

 The description of hWnd is incomplete in the second paragraph of the
 description of PeekMessage() on Page 388 of the "Microsoft Windows
 Software Development Kit Programmer's Reference" for Version 2.00.

 The hWnd description should be changed as follows:

    PeekMessage() retrieves only messages associated with the window
    specified by the hWnd parameter, or any of its children as
    specified by IsChild(), and within the range of message values
    given by the wMsgFilterMin and wMsgFilterMax parameters. If hWnd is
    NULL, PeekMessage() retrieves messages for any window that belongs
    to the application making the call. (The PeekMessage() function
    does not retrieve messages for windows that belong to other
    applications.) If hWnd is -1, PeekMessage() returns only messages
    with a hWnd of NULL as posted by PostAppMessage(). If wMsgFilterMin
    and wMsgFilterMax are both zero, PeekMessage() returns all
    available messages (no range filtering is performed).


 584. Turning off Windows' Use of Expanded Memory

 Question:

 Is there a way to tell Windows NOT to use a part or ALL of expanded
 memory?

 Response:

 Yes. To tell Windows not to use ANY expanded memory, use the /n switch
 at the MS-DOS command line.

 If you have a network or some other type of hardware board that needs
 a special section mapped for itself, you can tell the LIM 4.0 driver
 not to use a range of memory for expanded memory by setting your
 REMM.SYS line in your CONFIG.SYS file to exclude the desired range.
 The line will look something like the following:

    DEVICE=REMM.SYS /X=A000-CFFF

 In Windows/386 Versions 2.10 and later, you can put the following in
 the [win386] section of your WIN.INI file:

    emsexclude=D000-E000

 The following are some common hardware devices to exclude:

 1. IRMA card at CE00-CFFF (fixed)

 2. Token Ring ROM at DC00 (mandatory)

 3. Token Ring RAM at C800 (through drivers)

 Windows/386 should properly detect the Token Ring ROM segment and
 exclude it from page-frame mapping.


 585. Windows SDK: DDEPOKE Structure Incorrect in DDE.H

 Below is the correct definition of the DDEPOKE structure for the DDE.H
 file that is shipped with the Windows Versions 2.x SDK. The "unused"
 field should be 13 bits wide, not 12. The corrected structure is as
 follows:

 typedef struct {
         unsigned unused:13,
                  fRelease:1,
                  fReserved:2;
         int      cfFormat;
         BYTE     rgb[1];
 } DDEPOKE;

 You do not need to fill in the 13th unused bit. If you want, you
 can set it to FALSE. For an example of how to use this, see the
 DDE.ARC file in the Software Library. This file can be found in the
 Software Library by searching for the filename DDE.ARC, the Q number
 of this article, or S10036.


 586. Windows Documentation of WIN.INI Definitions

 Question:

 Is there any documentation that defines the various items in WIN.INI?

 Response:

 This information can be found in the Version 2.00 "Microsoft Windows
 Software Development Kit Programmer's Reference" in Chapter 7, Section
 7.4, "Windows Initialization File," starting on Page 653.

 You can find additional information on WIN.INI on Page 271 of the
 "Microsoft Windows User's Guide" that is shipped with Version 2.00
 of the retail Windows product.


 587. Sound Sample Code for Windows

 There are two code examples in the Software Library that demonstrate
 the use of the Windows sound functions. The first one is called
 MARY.ARC and plays "Mary had a little lamb." The second one, called
 SOUND.ARC, shows how to use some of the other sound functions. These
 files can be found in the Software Library by searching on the
 filenames, the Q number of this article, or S12051 (MARY.ARC) or
 S12060 (SOUND.ARC).


 588. Selecting Angled Fonts and Vector Fonts in Windows

 Question:

 How can I select vector fonts so that I can display rotated text?

 Response:

 To select vector fonts, an application must pass a logical font filled
 in as it came from EnumFonts(). This is demonstrated in the sample code
 in FONTANGL.ARC. This file can be found in the Software Library by
 searching for the filename FONTANGL.ARC, the Q number of this article,
 or S10038.


 589. Sample for Building .FON Font Resources in Windows

 To use a .FNT file created by FONTEDIT or any other means, it must be
 converted, and possibly combined with other fonts, into a .FON font
 resource, which can then be added with AddFontResource().

 There is a sample of this conversion process in the archive
 FONTRES.ARC. This file can be found in the Software Library by
 searching for the filename FONTRES.ARC, the Q number of this article,
 or S10038.


 590. Metafile Size Limitations in Windows

 Question:

 What are the size limitations on both memory and disk metafiles?
 Also, does playing back a disk metafile load the entire metafile into
 memory at once?

 Response:

 The limits are as follows:

 1. Both memory and disk metafiles are restricted to 32K handles
    (selected objects).

 2. Disk metafiles are restricted to 32K records (primitives).

 3. Memory metafiles' primitive-record lists are limited to 64K.

 4. The total size of disk metafiles is limited to the size of the
    disk.

 Note that all of the above limits must be met simultaneously.

 Playing back a disk metafile using GetMetaFile() and PlayMetaFile()
 loads the header and the selected object table, then reads the
 primitive records one at a time so that the entire metafile need not
 be memory resident at once.


 591. CodeView for Windows: Tracing into Procedures

 Problem:

 I am having problems tracing from one procedure to another. When I try
 to trace into a procedure that resides in a source file other than the
 file I am currently in, CodeView traces over the instruction instead of
 bringing up the source code for this procedure.

 Response:

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.

 You can work around the problem in one of the following two ways:

 1. Move all the source that you need to debug into one source file and
    recompile.

 2. Set a breakpoint in the procedure you want to trace into, then
    enter the "go" command. The application will process until it
    reaches the breakpoint in the code.


 592. CodeView: DB Causes Syntax Error

 Issuing the following command in CodeView for Windows (CVW) causes a
 syntax error:

    DB 0:0

 The lowest memory location you can access is 100:0 (hex). To work
 around this problem, you should use SYMDEB to dump low-memory bytes.

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.


 593. DrawBitmap() Function from "Progamming Windows"

 Problem:

 I believe I have found a problem in the DPtoLP() function. I am using
 the DrawBitmap() function from Page 615 of the "Programming Windows"
 book by Charles Petzold. When I have a 16-by-16 bitmap, the DPtoLP()
 call in the DrawBitmap() function returns a positive value (16), but
 when I have a 32-by-32 bitmap, it returns -16. This seems to be a
 problem in DPtoLP() because a bitmap cannot have a negative width.

 Response:

 This is not a problem in DPtoLP(). DPtoLP() converts a device
 coordinate to a logical coordinate in a given DC. It cannot be used to
 convert a width in device coordinates to a width in logical
 coordinates if the window origin is nonzero or if the window has
 negative extents.

 To obtain the width of a bitmap in logical coordinates, the logical
 equivalent of the device coordinate of the lower right-hand bit of the
 bitmap should be subtracted from the logical equivalent of the device
 coordinate (0,0), then the absolute value should be taken. The
 following code fragment does this. The width and height will be
 contained in the POINT structure.

      POINT pt, zero;

      pt.x = bm.bmWidth;
      pt.y = bm.bmHeight;
      LPtoDP(hDC,&pt,1);
      zero.x = 0;
      zero.y = 0;
      LPtoDP(hDC,&zero,1);
      pt.x = abs(pt.x - zero.x);
      pt.y = abs(pt.y - zero.y);


 594. Using argv, argc, and environ in Windows

 Problem:

 The "DOS Tools" section of the "Microsoft Windows Software Development
 Kit Programming Tools" manual describes on Pages 16-17 how to get the
 standard C argv, argc, and environ values. I tried to use these values
 the way the manual describes, but all I get is garbage.

 Response:

 The manual incorrectly describes how to access these variables. The
 code fragment below properly references and displays the argv and
 environ variables.

 You should first look at the lpszCmdLine variable that is passed to
 your WinMain() function to see if it provides the functionality that
 you want.

 Note: The first string pointed to by __argv will be something similar
 to "C:\WINDOWS\WIN200.BIN", and the next strings pointed to will be
 the command-line arguments that were listed AFTER the application
 name. You will not be told the name of the application your customer
 started.

 These environment variables are not properly initialized for DLL
 usage; therefore, if your DLL needs access to them, it will need to
 be passed them by the application in question.

     {
         extern int __argc;
         extern char **environ;
         extern char **__argv;

         while (*environ)
           MessageBox (GetFocus(), *environ++, "Environ", MB_OK);

         while (*__argv)
             MessageBox (GetFocus(), *__argv++, "ArgV", MB_OK);
     }


 595. Manual Menu Manipulation under Windows

 Question:

 I want to manually bring down menus and highlight menu items in my
 code as part of an instruction mode. How can I do this?

 Response:

 One method of bringing down menus and highlighting items is to emulate
 the keypresses that your customer would make from the keyboard to
 bring the menus down. The following are some hints and examples:

 1. To bring up the System menu, use the following line:

    PostMessage (hWnd, WM_SYSCOMMAND, SC_KEYMENU, DWORD(' '));

 2. To emulate the press/release of the ALT key (this would highlight
    the first menu title, but not bring down the menu), use the
    following lines:

    PostMessage (hWnd, WM_SYSKEYDOWN, VK_MENU, 0x20000001);
    PostMessage (hWnd, WM_SYSKEYUP,   VK_MENU, 0x20000001);

    The breakdown of the last parameter is described on Page 599 of the
    Windows SDK reference manual; essentially, "2" means the ALT key is
    down, "0" is unused, "00" is the OEM-dependent scan code (unused in
    this case), and "0001" is the repeat count.

 3. You could emulate the ALT+F that would bring down a menu defined as
    &File (F is the accelerator key) by using the following:

    PostMessage (hWnd, WM_SYSCHAR, 'f', 0x20000001);

    The breakdown of the last parameter is essentially the same as
    listed above; however, you may want to refer to the reference
    manual and note the differences.

 4. Once you have a menu up, you could then use the above method to
    hotkey the item you want to demonstrate; however, since that would
    immediately toss away the menu before your customer could see what
    you were selecting, it is probably not what you want to do.
    Instead, you would want to emulate the arrow keys to move the
    highlighted selection down to the item you want to illustrate. This
    could be done with one of the following lines:

     PostMessage (hWnd, WM_KEYDOWN, VK_UP,    1L);
     PostMessage (hWnd, WM_KEYDOWN, VK_DOWN,  1L);
     PostMessage (hWnd, WM_KEYDOWN, VK_LEFT,  1L);
     PostMessage (hWnd, WM_KEYDOWN, VK_RIGHT, 1L);

     The last parameter has essentially the same breakdown as
     previously described; however, since the only value that is
     significant in this case is the repeat count, the value is a
     little simpler than in the previous examples.


 596. Algorithm for Full-Sized Windows

 Problem:

 I want my application to size its window the same size that the MS-DOS
 Executive does when it comes up. I can figure out the correct sizes to
 do this on my system, but I'd also like my application to work on VGA,
 Genius, and other available display resolutions.

 Response:

 The following algorithm will allow you to create a window that is the
 "standard" full-screen window (i.e., the same size that the MS-DOS
 Executive comes up in):

     int winBorder, cxBorder, cyBorder,
         cxMargin, cxBAMargin, cyBAMargin,
         x, y, cx, cy;

     /* Get the user defined border width */
     winBorder = GetProfileInt("windows", "BorderWidth", 5);
     /* It must be in the range of 0 < winBorder < 51 */
     if (winBorder < 1) {
         winBorder = 1;
     } else if (winBorder > 50) {
         winBorder = 50;
     }

     /* Get some internal system metrics to determine extra scaling */
     cxBorder = GetSystemMetrics (SM_CXBORDER);
     cyBorder = GetSystemMetrics (SM_CYBORDER);
     cxMargin = (cxBorder * winBorder) + cxBorder;

     /* Byte Align the border */
     cxBAMargin = (((cxMargin + 7) & 0xFFF8) - cxMargin);
     cyBAMargin = cxBAMargin * cyBorder / cxBorder;

     x  = cxBAMargin;
     y  = cyBAMargin;
     cx = CW_USEDEFAULT;
     cy = 0;

     hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW,
                         x, y, cx, cy,
                         NULL, NULL, hInstance, NULL);


 597. Sizes in CreateWindow() Not Working Correctly

 Problem:

 I am specifying the window size I desire in my CreateWindow() call,
 but no matter what values I use, my window comes up as though I had
 used the CW_USEDEFAULT values.

 Response:

 The problem is that you are not attaching any resources to your
 application. It is the resource section of your application that
 Windows uses to determine which version of Windows your application
 was written for. If it was written for Versions 1.x, (which used
 tiled, rather than overlapping windows), it automatically gives your
 windows the CW_USEDEFAULT sizes.

 If your application does not need to use any resources, you can just
 add a dummy resource stub to your program by adding the following two
 lines to your MAKE file right after your link statement:

     ECHO /* No Resources */ > MyApp.RC
     RC MyApp

 The first line will create a .RC file that contains only a comment,
 and the second line will process this file and attach it to your .EXE
 file. This method is demonstrated and described on Page 68 of the book
 "Programming Windows" by Charles Petzold.


 598. Segmentation Test Versus GlobalCompact(-1)

 Although a Segmentation Test within Heapwalk does a GlobalCompact(),
 it is not the same as performing a GlobalCompact() from within the
 application's code. Performing a GlobalCompact(-1) from within the
 application's code will remove all discardable segments except the
 currently executing code segment. Your code segment is locked in
 memory, and even if it is a discardable segment, it will not be
 discarded. When the Segmentation Test performs a GC(-1), it has
 control of the processor. Your code segment is no longer locked in
 memory, and if it is a discardable segment, it will be discarded.


 599. Background Processing without Using Timers

 Question:

 How can I write an application so that it does background processing
 whenever the load is low? I don't want to use timers since they are
 based on fixed time intervals rather than on system load.

 Response:

 An application can't detect when overall system load is low, but it
 can detect when there are no more messages for it and do background
 processing then. This is done using PeekMessage() instead of
 GetMessage() so that it can do some background processing whenever
 there are no pending messages for this application. The background
 processing must be done in small pieces so that the application
 frequently looks for new messages and relinquishes control so that
 other applications can also run.

 The following code skeleton illustrates the ideas involved:

 int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
 HANDLE hInstance, hPrevInstance;
 LPSTR  lpszCmdLine;
 int    cmdShow;
     {
     extern HANDLE hAccel;

     MSG   msg;

     /* Initialize. */
     if (!AppInit(hInstance, hPrevInstance, lpszCmdLine, cmdShow))
         {
         return(1);
         }

     while (1)
         {

         /* Go look for any messages for us. If there aren't any,
            PeekMessage() will relinquish control before returning. */
         if (PeekMessage((LPMSG) &msg, NULL, 0, 0, (char)TRUE))
             {

             /* There is a message for us. */

             /* Quit if we get a WM_QUIT. Normally GetMessage()
                does this by returning NULL. */
             if (msg.message == WM_QUIT) break;

             /* Handle the message. This is where IsDialogMessage()
                would go if it were needed. */
             if (hWndTM != NULL &&
                 TranslateAccelerator(hWndTM, hAccel, (LPMSG)&msg) == 0)
                 {
                 TranslateMessage((LPMSG)&msg);
                 DispatchMessage((LPMSG)&msg);
                 }
             }

         else
             {

             /* There isn't a message for us. */

             /* Here's where we do the background processing. The idea is
                to do just a little bit of work, remember where we were,
                and then run around the loop again to look for messages
                and let other apps have a shot at the processor. */

             Do_a_bit_of_background();
             }
         }

     /* Finish and clean up. */
     if (!AppFin())
         {
         return(1);
         }

     return(0);
 }


 600. Windows SDK: Using a User-Defined Control

 Question:

 There was an article in the July 1988 issue of the "Microsoft Systems
 Journal" about creating your own user control. I am unable to get the
 user control to work in my application. What am I doing wrong?

 Response:

 You must export the user-defined controls window procedure in the DEF
 file. There is a modified version of HELLO in the Software Library
 that has the SPECTRUM user-defined control in the ABOUT box. The file
 is called USERCTL. You can check the source code and DEF file so that
 you can see what is necessary to implement the user-defined control.

 This file can be found in the Software Library by searching on the
 keyword USERCTL, the Q number of this article, or S12257. USERCTL was
 archived using the PKware file-compression utility.


 601. Corrected Version of EDITFILE.C for Windows SDK

 The EDITFILE sample program found on the sample source code disk in
 the \LEARNING\EDIFILE directory does not work correctly. The program
 uses strlen on the edit-control buffer to determine its length. This
 is incorrect. It should send the message WM_GETTEXTLENGTH to obtain
 the text length. The strlen function assumes the buffer is NULL
 terminated. However, the edit-control buffer is not NULL terminated.

 Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10. We have made a corrected version of the EDITFILE program
 available in the Software Library. It is contained in a file called
 EDITFILE.ARC. This file can be found in the Software Library by
 searching on the filename EDITFILE.ARC, the Q number of this article,
 or S12047.


 602. Making .FNT and .FON Files in Windows

 Question:

 I see .FON and .FNT files on my disk. Where do these .FON files come
 from?

 Response:

 To explain where these files come from, suppose that you want to
 develop a set of font files. To do so, you would do the following:

 1. Use the FONTEDIT.EXE program that is provided in the Software
    Developers Toolkit and is explained on Page 121 of the "Microsoft
    Windows Software Development Kit Programmer's Reference."

 2. Read in the sample font file ATRM1111.FNT that is supplied on one
    of the disks in the Software Developers Toolkit.

 3. Edit the file as you like.

 4. Make a few copies of this file in different point sizes so that
    they look good.

 5. Create a DEF file that contains these FNT files. It is now up to
    the RC compiler to take these DATA files and make them executables.

 At this point, these executables are PURE DATA and can therefore be
 discarded by the memory manager.

 In other words, you edit a group of FNT files with different point
 sizes and run them through the RC compiler, which puts an EXE header
 on the files, thus making them LOFONTS.EXE, as follows:

    SYS12EGA.FNT  ----- \
    SYS08CGA.FNT  -----  ---->  RC.EXE -----> LOFONTS.EXE
    OEM08CGA.FNT  ----- /
    OEM12EGA.FNT  -----/

 The MAKE file then renames or copies the EXE file to have a FON
 extension, as in the following example:

    LOFONTS.EXE --> MS DOS rename --> LOFONTS.FON

 This is done so that people won't click on the FONTS.EXE file and
 expect an application to start running.

 For more information on fonts, refer to Q35992, "Sample for Building
 .FON Font Resources in Windows," in the KnowledgeBase.


 603. Speed Trade Off when Using CS_OWNDC

 Question:

 We have been experimenting with the class type CS_OWNDC, and would
 like very much to use it to avoid remapping each time we want to
 paint.

 However, when you use CS_OWNDC you get a paint message to all child
 windows when you put up a pop-up window and then remove it. If you use
 GetDC() and ReleaseDC() this behavior does not occur. Instead, it
 appears that shadow bitmaps are saved and BitBlted back to the screen.
 How is this done?

 Response:

 When you do a GetDC() and a ReleaseDC(), the USER interface code can
 keep track of the area of the screen you are painting onto and will
 set a "dirty" bit only in those windows touched by the pop-up. If you
 use CS_OWNDC, Windows doesn't keep track of this. Using your own DC
 means you can paint into areas of the screen underneath the pop-up and
 not set the dirty bit. The solution is to send paint messages to all
 the child windows because you may have painted underneath your pop-up.


 604. Windows: When PostMessage() Will Return 0, Indicating Failure

 PostMessage(hWnd,wMsg,wParam,lParam) returns 0, indicating failure,
 for one of the following two reasons:

 1. The window handle (hWnd parameter to PostMessage()) receiving the
    message is -1. If this is the case, the message (wMsg) is broadcast
    to all windows.

 2. The window to receive the message has a full application queue.

 Note that PostMessage() does not return NULL if the hWnd parameter in
 the PostMessage() call is an invalid window handle. Also note that
 IsWindow() can be called to verify if a given handle is indeed a valid
 window handle.


 605. Changing Interior Color of Scroll Bars, COLORSCR Program

 Question:

 There seems to be a problem in Charles Petzold's COLORSCR program from
 Chapter 7 of "Programming Windows." His code is correct, but the
 scroll bars do not always repaint the frame if the thumb is dragged.

 Response:

 This problem occurs if there is a color in the interior of a scroll
 bar. Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10. We are researching this problem and will post new information as
 it becomes available.


 606. Using EmptyClipboard() and EMS under Windows

 When large-frame EMS is being used by Windows Version 2.x, the memory
 allocated for an application may not be freed if the application
 empties the Clipboard (with EmptyClipboard()) without placing more
 data in the Clipboard (with SetClipboardData()). To workaround this
 problem, do not empty the clipboard unless you plan to put something
 into it, even if it is one byte of text.


 607. Corrected .SYM Files for Windows SDK Version 2.10

 The KERNEL, USER?, and GDI symbol files included with the Windows SDK
 Version 2.10 do not coincide with the EXE files on these disks.

 You can download the corrected version of the SYM files from the Software
 Library. This file can be found in the Software Library by searching on
 the filename DEBUG.ARC, the Q number of this article, or S12046.

 Use the PKXARC utility to unarchive the file DEBUG.ARC, which contains
 KERNEL.SYM, USERF.SYM, USERS.SYM, and GDI.SYM.


 608. Modifying LRU Chain with GlobalLRUOldest()

 The Windows function GlobalLRUOldest() can be used to set a code
 segment at the head of the LRU list so it will be the first to be
 discarded.

 There is a modified "Hello Windows" application in the Software
 Library that demonstrates how to use the GlobalLRUOldest() function.
 This file can be found in the Software Library by searching on the
 filename LRUOLD.ARC, the Q number of this article, or S12050.


 609. Definition of the CATCHBUF Structure

 The CATCHBUF structure used by the Windows Catch() and Throw()
 functions is not defined in WINDOWS.H, but is defined only in internal
 Windows KERNEL files. To use Catch() and Throw(), you must define the
 CATCHBUF structure yourself, using the following definition:

 typedef int CATCHBUF[9];



 610. Compatibility between SDK Version 2.10 and Windows 2.03

 A Windows application created using the Microsoft Windows Software
 Development Kit Version 2.10 will not run under Microsoft Windows
 2.03. Instead, a message will be displayed stating that you must have
 a newer version of Microsoft Windows.

 If an application created using the Version 2.10 Software Development
 Kit is desired to run under Windows Version 2.03, this can be achieved
 by using the resource compiler (RC.EXE) from the Version 2.00 Software
 Development Kit. The resource compiler from Version 2.10 of the
 development kit sets a version number in the EXE header that causes
 the Windows Version 2.03 loader not to load the executable.

 Note that there was a problem with RC.EXE shipped with the Version
 2.00 development kit. It corrupted bitmaps (an article in the
 KnowledgeBase describes this problem). There is a newer version of
 RC.EXE Version 2.00 in the Software Library. It can be found in the
 Software Library by searching on the filename RC.ARC, the Q number of
 this article, or S10065.


 611. Sample Application TAB.ALL: Modal and Modeless Dialog Boxes

 TAB.ALL is a sample application that demonstrates the differences
 between modal and modeless dialog boxes. The application later
 demonstrates how to put or use a dialog within a dialog and how to
 change the text inside a push button and how to disable one of the
 buttons. It also shows how to tab between push buttons and how to
 process a message after you have selected one of the buttons.

 TAB.ALL can be found in the Software Library by searching on the
 filename TAB.ARC, the Q number of this article, or S12056.


 612. Meaning of RIP_DCBUSY Error Message

 The RIP_DCBUSY(8) error message means you have exceeded your limit of
 display contexts. Windows maintains a five-item cache for display
 contexts retrieved by the GetDC() function.

 This error often occurs when your code calls BeginPaint() (or
 GetDC()), but doesn't make the corresponding call to EndPaint() (or
 ReleaseDC()).


 613. PostAppMessage(): Getting a Valid TASK Handle

 Question:

 Does PostAppMessage() not check to verify that the TASK still exists?
 If so, is there a way to determine if a TASK handle is still valid?

 Response:

 PostAppMessage() does not check to verify that the TASK still exists
 because it was not intended to be used to call another application
 without each application having prior knowledge of the other's
 existence.

 Currently, there is no easy way to determine if the TASK is still
 valid. If a function did exist, it would work under most conditions
 since the hWnd that you pass to GetWindowTask() may have been reused.
 Therefore, unless you enumerate the window handles each time to verify
 that the hWnd you get belongs to the proper window, you may receive
 unexpected results.


 614. Finding Code Segments in Heapwalk

 Question:

 I have been trying to see the memory allocated by my application using
 heapwalk; however, I can't seem to find any segments with its name.
 How I can be sure my application is loaded and then examine its global
 allocs with heapwalk?

 Response:

 The problem is that you are more than likely running with expanded
 memory, in which case, when you look at heapwalk, your application is
 now banked out. For your purposes, start Windows up by adding a "/n"
 to the command line, which tells Windows not to use expanded memory.
 Your start-up command would look like the following:

    C:>win /n


 615. Modal Dialog Boxes and WM_TIMER Messages

 Problem:

 When an application creates a modal dialog box, WM_TIMER messages are
 not sent to the main window procedure. The WM_TIMER messages are not
 received when the focus of the modal dialog box is set to an edit
 field or a scroll bar.

 Response:

 Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10. We are researching this problem and will post new information as
 it becomes available.

 You can work around the problem by changing from a modal dialog box to
 a modeless dialog box, then disabling the parent. MODALTM.ARC is an
 example program that demonstrates this workaround. This file can be
 found in the Software Library by searching for the filename
 MODALTM.ARC, the Q number of this article, or S12082.


 616. CALPOPUP.EXE: Windows Demo Pop-up Calendar

 There is a sample executable Windows application named CALPOPUP.EXE in
 the Software Library that is a pop-up calendar. It runs in overlapping
 instead of tiled mode.

 CALPOPUP.EXE can be found in the Software Library by searching for the
 filename CALPOPUP.ARC, the Q number of this article, or S10043.


 617. CRUMBLE.EXE: Windows Demo That Crumbles Your Screen

 There is a sample executable Windows application named CRUMBLE.EXE in
 the Software Library that does not write to the screen. It causes the
 screen to crumble before your eyes. You must reboot to clean up the
 screen.

 CRUMBLE.EXE can be found in the Software Library by searching for the
 filename CRUMBLE.ARC, the Q number of this article, or S10044.


 618. CodeView for Windows: Segment Motion/Loading Notification

 Question:

 In CodeView for Windows (CVW), is it possible to turn off the segment
 motion/loading notification completely when running under Windows?
 This must slow CodeView down somewhat, and most of the time I am not
 interested in the information.

 Response:

 Currently, there is no flag to turn off this output. This feature is
 under review and will be considered for inclusion in a future release.


 619. CUBE4.EXE: Rotating Cube Windows Program

 There is a file named CUBE4.EXE in the Software Library that lets you
 choose between tiled and overlapping windows. It also draws the cube
 into an off-screen bitmap and then blts it onto the screen.

 CUBE4.EXE can be found in the Software Library by searching for the
 filename CUBE4.ARC, the Q number of this article, or S10046.


 620. DISKLOOK.EXE: Windows Demo Disk/File Utility

 There is a sample executable Windows application named DISKLOOK.EXE in
 the Software Library that allows close examination of disks and files.

 DISKLOOK.EXE can be found in the Software Library by searching for the
 filename DISKLOOK.ARC, the Q number of this article, or S10047.


 621. Changing Cursor to IDC_SIZE Documentation Error

 Page 353 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" incorrectly states that the LoadCursor()
 function can be used to access the predefined cursors. Specifically,
 the manual states that using IDC_SIZE as the second parameter to
 LoadCursor() will load a four-pointed arrow cursor. This is a
 documentation error; IDC_SIZE is still valid as the second parameter,
 but rather than loading the four-pointed arrow cursor, it will load a
 square with a smaller square inserted in the right bottom corner.


 622. FISH.EXE: Windows Demo Application Electronic Aquarium

 There is a new and improved version of the FISH.EXE Windows
 application in the Software Library that allows you to choose between
 tiled and overlapping windows, number and types of fish, and bubbles.

 FISH.EXE can be found in the Software Library by searching for the
 filename FISH.ARC, the Q number of this article, or S10048.


 623. FUSE.EXE: Windows Demo Visual Display Program

 There is a sample Windows application named FUSE.EXE in the Software
 Library that is a self-running visual display demo.

 FUSE.EXE can be found in the Software Library by searching for the
 filename FUSE.ARC, the Q number of this article, or S10049.


 624. GLOBE.EXE: Windows Demo French View of Earth

 There is a sample executable Windows application named GLOBE.EXE in
 the Software Library that displays a rotating view of Earth. It has
 been translated into French.

 GLOBE.EXE can be found in the Software Library by searching for the
 filename GLOBE.ARC, the Q number of this article, or S10050.


 625. KILLER.EXE: Windows Demo GLOBE.EXE Killer

 There is a sample executable Windows application named KILLER.EXE in
 the Software Library that destroys the GLOBE.EXE sample executable
 Windows application. If you run the KILLER program while GLOBE.EXE is
 running, it will remove GLOBE.EXE in an unusual way.

 KILLER.EXE can be found in the Software Library by searching for the
 filename KILLER.ARC, the Q number of this article, or S10051.


 626. MOLE.EXE: Moles Appear at Random Windows Demo

 There is a sample executable Windows application named MOLE.EXE in the
 Software Library that doesn't write to the screen. Anywhere the moles
 appear, they will leave their burrows behind. The only way to stop them
 is to exit Windows.

 MOLE.EXE can be found in the Software Library by searching for the
 filename MOLE.ARC, the Q number of this article, or S10052.


 627. NAMI.EXE: Windows Demo Visual Display Program

 There is a sample executable Windows application named NAMI.EXE in the
 Software Library. NAMI.EXE is a visual display demonstration.

 NAMI.EXE can be found in the Software Library by searching for the
 filename NAMI.ARC, the Q number of this article, or S10053.


 628. PALETTE.EXE: Screen Driver Color Range Windows Demo

 There is a sample executable Windows application named PALETTE.EXE in
 the Software Library that shows the range of colors supported by a
 screen driver.

 PALETTE.EXE can be found in the Software Library by searching for the
 filename PALETTE.ARC, the Q number of this article, or S10054.


 629. SLUG.EXE: Slugs and Slime Trails Windows Demo

 There is a sample executable Windows application named SLUG.EXE in the
 Software Library that doesn't write to the screen. It creates slugs
 that leave slime trails wherever they go. To kill them, you must exit
 Windows.

 SLUG.EXE can be found in the Software Library by searching for the
 filename SLUG.ARC, the Q number of this article, or S10055.


 630. WEB.EXE: Interactive Windows Display Demo

 There is a sample executable Windows application named WEB.EXE in the
 Software Library that is an interactive version of the sample Windows
 application FUSE.EXE. This is a visual display demonstration.

 WEB.EXE can be found in the Software Library by searching for the
 filename WEB.ARC, the Q number of this article, or S10056.


 631. FONT2.ARC: Windows Font File Format Documentation

 In the Software Library is a copy of the "Font File Format" document
 that describes the Windows Font File format using the TEXTMETRIC
 structure that is returned by the GetTextMetrics routine.

 This file can be found in the Software Library by searching for the
 filename FONT2.ARC, the Q number of this article, or S10061. FONT2.ARC
 has been archived with PKARC so you will need to unarchive it with
 PKXARC. The PKXARC utility is located on your OnLine Utilities Disk 2.


 632. JOURNAL.ARC: Windows Journal Hooks Sample Source Code

 There is a file named JOURNAL.ARC in the Software Library that
 contains sample code that demonstrates the use of the journal hooks
 WH_JOURNALRECORD and WH_JOURNALPLAYBACK. The application, APP.EXE
 serves only one purpose; it illustrates how to call the dynamic link
 library (DLL) that does all of the journaling work. The DLL calls to
 record and playback messages are made from the application module,
 WNDPROC.C. The DLL file, JOURNAL.C, contains all the code necessary
 for creating a file of event messages for later playback. The code
 should allow quick creation of "hands off" demos for any application
 simply by having the application call the DLL high-level functions.

 The file DIARY.BIN has been included in JOURNAL.ARC to demonstrate the
 use of journal hooks. After running APP.EXE, select Play from the
 menu. Each time Record On is selected from the menu, DIARY.BIN is
 overwritten. Save copies of DIARY.BIN that you do not want to be
 destroyed.

 These files can be found in the Software Library by searching for the
 filename JOURNAL.ARC, the Q number of this article, or S10062.
 JOURNAL.ARC has been archived with PKARC so you will need to unarchive
 it with PKXARC. The PKXARC utility is located on your OnLine Utilities
 Disk 2.


 633. LOADFONT.ARC: Font Load Patch for Windows 1.x, 2.00 USER.EXE

 In the Software Library is a file named LOADFONT.ARC that corrects a
 problem in Windows Versions 1.x and 2.00 with USER.EXE not loading the
 fonts in the [fonts] section of WIN.INI.

 The problem is in how the USER init code calls GetProfileString() to
 read the [fonts] section of WIN.INI and call AddFontResource(). The
 critical factor is the total number of characters on the left of the
 "=" in the [fonts] section. The first 300 bytes (less one byte for
 each line since they are null terminated) are guaranteed to be read
 correctly. Beyond that, the procedure may or may not work.

 LOADFONT is a shadow application (i.e., you can't see it) that
 guarantees that large [fonts] sections are properly loaded by reading
 WIN.INI and calling AddfontResource(). All you have to do is put the
 following into your WIN.INI file:

    [windows]
    load=c:\win\loadfont (or whatever path it is on)

 The only disadvantage is that this will reload the fonts that USER.EXE
 did load. If you try to do a FreeFontResource(), it may not actually
 work unless you do a second FreeFontResource(). You could solve this
 problem by putting these fonts into a new section and having LOADFONT
 read that; however, the Control Panel wouldn't know about them. For
 most people it won't be a problem.

 LOADFONT.ARC can be found in the Software Library by searching for the
 filename LOADFONT.ARC, the Q number of this article, or S10063.
 LOADFONT.ARC has been archived with PKARC so you will need to
 unarchive it with PKXARC. The PKXARC utility is located on your OnLine
 Utilities Disk 2.


 634. TIFFSRC.ARC: TIFF Open, Read, Write, Close Sample Source Code

 In the Software Library is a file named TIFFSRC.ARC that contains
 sample source code that demonstrates how to open, read, write and
 close Tag Image File Format (TIFF) files. It also includes CCITT
 decompression routines and a menu to examine and create tags.

 TIFFSRC.ARC can be found in the Software Library by searching for the
 filename TIFFSRC.ARC, the Q number of this article, or S10067.
 TIFFSRC.ARC has been archived with PKARC so you will need to unarchive
 it with PKXARC. The PKXARC utility is located on your OnLine Utilities
 Disk 2.


 635. XLDDEKIT.ARC: Dynamic Data Exchange (DDE) Development Kit

 In the Software Library is a file named XLDDEKIT.ARC that contains
 sample source code and executable programs that demonstrate the use of
 Dynamic Data Exchange (DDE). When running any of the sample programs,
 you will need to use Windows Version 2.00 because the sample
 applications take advantage of the new global atom routines
 (GlobalAddAtom, GlobalDeleteAtom, and GlobalGetAtomName) available in
 Windows Version 2.00. We also suggest you obtain a current version of
 the DDE specification and the newest copy of Excel.

 XLDDEKIT.ARC can be found in the Software Library by searching for the
 filename XLDDEKIT.ARC, the Q number of this article, or S10068.
 XLDDEKIT.ARC has been archived with PKARC so you will need to
 unarchive it with PKXARC. The PKXARC utility is located on your OnLine
 Utilities Disk 2.


 636. Windows SDK: Creating an SAE and Relocating Files

 When creating a Single Application Environment (SAE), it is best to
 start with a copy of the original Windows retail product disks in the
 same disk format in which you desire your SAE. With the copy of the
 disks, you must delete the files specified in the SAE agreement and
 make the corresponding modifications to the SETUP.INF file. When the
 proper files have been removed from the copies of the disks, space can
 be condensed by rearranging files on the disks to minimize wasted
 floppy disk space. When this is done, the SETUP.INF file must again be
 modified to reflect the new locations of the files.

 For example, in the [apps] section of the SETUP.INF file, CLIPBRD.EXE
 might be moved from the original disk to Disk 1, filling the space
 previously occupied by the MSDOS.EXE file. Once it is moved, the
 format for the new entry would be (1:clipbrd.exe). The number
 preceding the colon indicates the disk in the installation set on
 which the file is located.


 637. Windows SAE Clarification on .COM File in Data Field

 In the Single Application Environment instructions (SAE) for Windows,
 it is mentioned that the data field must be modified in the
 information file. The first line calls for a .COM file named for the
 application being installed. Despite the fact that the Windows program
 will be an .EXE file and that no .COM file exists, it is still
 necessary to specify the file as a .COM because one will be created
 during the setup procedure. The second line contains the .EXE that is
 the actual program name.

 The following is an example data field:

 [data]
 ("YOURAPP.COM")
 ("YOURAPP.EXE")
 ("C:\SUBDIR")
 ("YOURAPP")
 ("YOURAPP and pertinent information")
 ("YOURAPP startup name")



 638. RC Compiler Bitmap Limit 32K; How to Make 64K Bitmap Resource

 The Microsoft Windows Resource Compiler (RC.EXE) Versions 2.00 and
 2.10 cannot handle bitmaps that are greater than or equal to 32K. This
 article shows a way the bits for a bitmap can be included in the
 resource file as a user-defined resource. This resource can then be
 loaded and locked and a bitmap created using those bits. Using this
 technique, bitmaps of greater than 64K can be stored as resources.

 In addition to this method, the 32K limit can also be avoided by
 splitting the bitmap into two bitmaps, or by creating the bitmap at
 run time and writing to it with GDI calls as you would to a display or
 printer DC. This second method has the advantage of being a
 device-independent solution.

 The resource file can contain a user-defined resource description, as
 in the following:

    LOADONCALL MYBITS BITMAPBITS mybitmap.bmp

 (For more information on user-defined resources, look in Section 3.5
 of the "Microsoft Windows Software Development Kit Programming Tools"
 manual.)

 This resource can then be loaded at run time and a bitmap created
 using the bits in the resource. The following function loads the
 above-described resource, creates a bitmap with the bits in the
 resource, and returns a handle to the bitmap. The bitmap represented
 in the above resource is 640 bits wide and 285 lines high. It is a
 device-dependent bitmap created for the EGA.

 HBITMAP MyLoadBitmap()
 {

 HBITMAP  hBitmap;
 HANDLE   hResInfo;
 HANDLE   hResData;
 LPSTR    lpResInfo;

 if ((hResInfo = FindResource( hInst,(LPSTR)"MYBITS",(LPSTR)"BITMAPBITS"))
             == NULL)
     return FALSE;

 if ((hResData = LoadResource( hInst, hResInfo )) == NULL)
     return FALSE;

 if ((lpResInfo = LockResource( hResData )) == NULL)
     {
     FreeResource(hResData);
     return FALSE;
     }

 hBitmap = CreateBitmap(640,285,3,1,lpResInfo+16);

 if (!UnlockResource(hResData))
         FreeResource( hResData );

 return hBitmap;

 }


 639. Meanings of Resource Compiler Messages

 Question:

 What do the different numbers mean that are displayed when the
 resource is being attached to the EXE file? The following is a sample
 of the output:

    Reading shapes.res

    Resources will be aligned on 16 byte boundaries
    Writing resource SHAPES.3 (1038 bytes)
    Writing resource SHAPES.4 (25 bytes)
    Writing resource 1.5 (222 bytes)
    Writing resource 13.6 (24 bytes)
    Writing resource SHAPES.9 (25 bytes)

 Response:

 The following is the syntax of what the RC compiler is telling you
 when it is writing resources

    <id>.<Predefined resource type> <bytes>

 where <id> is the #define that you give a resource or the string in
 uppercase; <predefined resource type> is one of the following

 #define CURSOR       1
 #define BITMAP       2
 #define ICON         3
 #define MENU         4
 #define DIALOG       5
 #define STRING       6
 #define FONTDIR      7
 #define FONT         8
 #define ACCELERATORS 9
 #define RCDATA       10

 and <bytes> is the size of the resource.


 640. Documentation Error on SetVoiceSound()

 The "Microsoft Windows Software Development Kit Programmer's
 Reference" manual for Versions 2.03 and 2.10 states on Page 461 that
 the second parameter of the SetVoiceSound() function is an int. This
 is incorrect; the proper type for this parameter is an unsigned long
 (or a LONG).

 The documentation also states that the high-order word is the
 frequency in kilohertz. This is also incorrect; the high-order word is
 actually in hertz. The symptom of this error is a barely audible
 sound.



 641. Using Minimize and Maximize Arrows in Pop-Ups and Dialogs

 Question:

 Is it possible to enable the minimize and maximize arrows in a pop-up,
 child or dialog box? When a dialog or pop-up window is created with
 the WS_MINIMIZEBOX or WS_MAXIMIZEBOX style bits, the resulting window
 is displayed with the appropriate arrows in the title bar, but the
 arrows don't work properly.

 Response:

 Windows was not designed to have a pop-up window or a dialog box use
 the minimize or maximize styles. The styles work partially; however,
 you cannot take advantage of them because there are too many side
 effects. There are two alternatives to getting some of the
 functionality that you want; they are as follows:

 1. Make the window an OVERLAPPED window.

 2. Follow the Multiple Document Interface, MDI, as described in the
    "Microsoft Windows Software Development Kit Application Style
    Guide" manual. There is also a sample application called MDI.ARC in
    the Software Library that has a lot of the code necessary to
    implement the MDI interface. This file can be found in the
    Software Library by searching for the filename MDI.ARC, the Q
    number of this article, or S10064.

 Also, child windows were not made to have the MINIMIZE style since
 they are made to stay within the parents client area.


 642. Using GetInstanceData()

 Question:

 I am having a problem getting GetInstanceData() to work. How does
 GetInstanceData() know which data instance to copy from?

 Response:

 GetInstanceData() uses the data from the instance that you provide as
 the first parameter. If you have four instances running and you want
 to copy data from the first instance, you will have to store the
 variable hPrevInstance in a static area. This should be done as one of
 the first statements in your WinMain procedure. By storing the
 variable in a static area, you have in essence created a linked list
 that is NULL terminated to the first instance. However, if one of the
 instances is closed down, the link list will break unless you maintain
 it yourself.


 643. Windows SDK: Reasons for Dialog Box Failure

 Problem:

 Sometimes DialogBox() returns -1. This is an intermittent problem. We
 know we are low on memory; Heapwalk reports 70-85K free when it does a
 GC(-1). However, when we invoke our application, one of the following
 scenarios occurs at random:

 1. The problem never occurs.

 2. The problem always occurs.

 3. The problem occurs at first, then after repeated attempts it
    disappears.

 Response:

 The following is a list of things to do and check:

 1. If you are displaying a large dialog, the default behavior is for
    the dialog box to save the bits behind the dialog. This can create
    a bitmap that can be larger than 64K for some large dialogs. In
    this situation, change the CS_SAVEBITS style bits of the dialog.
    There is a sample application in the Software Library called
    SAVEBITS.ARC that shows you how this can be done. This file can be
    found in the Software Library by searching for the filename, the Q
    number of this article, or S12111. For more information on this
    topic, search on the words "dialog" and "VGA" in the KnowledgeBase.

 2. If you receive a -1 answer, put a check in your code that you
    should do a GlobalCompact() and try the DialogBox() call again.

 3. Make sure that you have MOVEABLE, DISCARDABLE code.

 4. If you are using a lot of strings, you might want to break up your
    STRINGTABLE into smaller tables. Basically, just use the
    LOADONCALL statement on STRINGTABLES that you do not need to be
    loaded at start-up time. For more information on this topic, search
    on the word "STRINGTABLE" in the KnowledgeBase.

 5. Edit controls use the application's local heap for room, so low
    local heap space could be a problem.

 6. List boxes use global memory to store the strings. If your
    application is too large to have parts discarded, this might be
    another problem.


 644. Binary Data in Resource File

 Question:

 I have a user-defined resource that is included in the resource
 compiler through the following statement:

    TEST TESTDATA "test.vob"

 I load the resource in my program with the following:

    HANDLE  hand, FoundData;

    FoundData = FindResource(hInst,"TEST","TESTDATA");
    hand = LoadResource(hInst,FoundData);

 When reading from the resource, it seems that not all of the resource
 has been loaded. At any point in the resource after a chr(26), the
 data appears to be truncated. Is there a way to ensure that the entire
 resource (all binary data) will be loaded?

 Response:

 We were unable to reproduce the problem. There is a sample application
 called RCDATA.ARC in the Software Library that verifies the use of the
 resource. This file can be found in the Software Library by searching
 for the filename, the Q number of this article, or S12110.


 645. Windows SDK: Running CVW with Excel

 Problem:

 We would like to test our application's interaction with Excel.
 However, we can't get Excel to load when CodeView is running and the
 network is loaded.

 Response:

 Try to reduce the swapsize that Excel uses by adding the following to
 the [Microsoft Excel] section of the WIN.INI file:

    swapsize=128

 Start decreasing the swapsize by 20K increments. You will notice that
 Excel's performance starts to slow. If you set the swapsize too low,
 Excel may not run.


 646. Switching between Windows without Mouse

 Question:

 I have an application that uses a modeless dialog box to inform the
 customer about the status of a process running in the background.
 Using only the keyboard interface, how can the customer switch between
 my modeless dialog box and the parent program's main window? Using the
 ALT+TAB key combination only switches me between the child and the
 MS-DOS Executive.

 Response:

 In the absence of a mouse, use the ALT+F6 key combination to switch
 between a modeless dialog box and the main application window. This is
 described in the "Microsoft Windows Software Development Kit
 Application Style Guide," Section 5.4.1, Page 46.


 647. Windows SDK: Meaning of Undocumented WM_ENTERIDLE Message

 Question:

 What does the WM_ENTERIDLE message mean? I could not find any
 documentation on it in the SDK manuals.

 Response:

 The WM_ENTERIDLE message is not documented. This message is sent to
 the main window procedure during the display of modal dialog boxes and
 pop-up menus. It is intended to notify the application that the system
 is in an "idle" state, waiting for the customer to make a selection
 (which can take a long time compared to the system's
 message-processing speed).

 This is a good time for your application to do low-priority,
 background-processing tasks such as the recalculation of a spreadsheet
 or reformatting of a document. Because the customer has not (yet)
 asked your application to perform any tasks, it is free to catch up on
 lower priority tasks until the customer responds to the menu or dialog
 box.


 648. Using Libraries to Share Icons, Menus, and Other Resources

 It is possible to create a library of shared resources for use in
 multiple applications. An example of this can be found in the Software
 Library by searching on the filename ICONLIB.ARC, the Q number of this
 article, or S12119.

 The source code in this example is provided for informational purposes
 only. Microsoft does not claim that the code is error free or
 functional. Furthermore, the code provided is not supported by
 Microsoft OEM Customer Support.

 Resources may be loaded from Windows libraries as well as from Windows
 applications. The module handle returned by GetModuleHandle() may be
 used as an "instance handle" for finding, loading, and locking
 resources.

 The technique illustrated in this example is to use a library as the
 repository of all shared icons, menus, dialog boxes, etc.

 There is only one catch. Some resources are machine dependent and need
 to be transformed before being passed to an application. Normally, an
 application gets a default procedure during task start-up that
 transforms any machine-dependent resources. Libraries are not tasks
 and thus do not get a default procedure. The application must install
 a transformation procedure for a library if it wants to access
 machine-dependent resources in the library.


 649. Windows SDK: Fatal Exit Code 0x000B, DC Cache Full Error

 Question:

 An application I am creating aborts with fatal exit 0x000B. This fatal
 exit is not listed in Appendix A of the "Microsoft Windows Software
 Development Kit Programming Tools" manual. What is fatal exit 0x000B?

 Response:

 Fatal exit 0x000B is caused by a DC cache full error. There are five
 system-wide display contexts available. When an attempt is made to
 acquire a sixth, this fatal exit will occur. This usually happens when
 BeginPaint() calls are made without corresponding EndPaint() calls, or
 when GetDC() calls are made without corresponding ReleaseDC() calls.


 650. Determining Free Memory: Conventional and Expanded

 Question:

 How can my application determine how much memory is available on the
 system, including expanded memory? I tried using GlobalCompact(0), but
 it doesn't report on expanded memory.

 Response:

 You should continue to use GlobalCompact(0) to find out how much
 conventional memory is available. The return value from
 GlobalCompact(0) is the most accurate statement of how much memory is
 available to your application. However, there is no Windows API
 routine to tell you how much expanded memory exists (or is free).

 Instead, you need to make direct calls to the Expanded Memory
 Manager using the Lotus/Intel/Microsoft Expanded Memory Specification
 (LIM EMS) Version 3.2.

 You can read about EMS in Chapter 9, Pages 182-186, of Ray Duncan's
 book, "Advanced MS-DOS" (Microsoft Press, 1986). Section IV at the
 back of this book is a reference manual to the various INT 67H calls
 to the Expanded Memory Manager.

 You can also get a copy of the Lotus/Intel/Microsoft (LIM) Expanded
 Memory Specification (part number 300275-003) by writing to Intel at
 the following address:

    Intel Corporation
    3065 Bowers Avenue
    Santa Clara, CA  95051

 Although Windows itself uses LIM EMS 4.0, your application can only
 make calls to LIM EMS 3.2. To do this, you need to compile your
 application with the -l switch to the resource compiler:

    rc -l -r ......

 For more information on the -l switch, query in the KnowledgeBase for
 "ems and rc". For an in-depth discussion of Windows and EMS, please
 see Paul Yao's article "EMS Support Improves Microsoft Windows 2.0
 Application Performance" in the January 1988 issue of the "Microsoft
 Systems Journal." This article is also available in the Software
 Library by searching on the filename WINEMS.ARC, the Q number of this
 article, or S12242.

 To find out how much expanded memory you have on your system, you need
 to do the following:

 1. Ensure that the Expanded Memory Manager is present on your system.
    Ray Duncan's book gives two examples of how to detect it.

 2. Test to see if the expanded memory hardware is functional. This is
    INT 67H function 01H.

 3. Get the number of expanded memory pages. This is INT 67H function
    03H. This interrupt tells you the total number of expanded memory
    pages present in the system, and the number of those pages that are
    not already allocated.


 651. Porting an Application to Europe and Japan (Kanji)

 Question:

 I want to port my Windows application to run on European machines and
 Japanese (Kanji) machines. What do I need to do this?

 Response:

 The only part that changes in your application is the RC file that
 contains the strings. Your application should also be aware of the
 OemToAnsi and AnsiToOem functions. There is no European version of the
 Windows SDK.

 To change your RC file, you can use either Notepad or Write. To get
 a particular European character, you must use an ALT key sequence to
 generate the desired character. You can find the ANSI table on Page
 121 of the "Microsoft Windows Software Development Kit Programmer's
 Reference" manual. Refer also to the "Using Special Characters"
 section in Appendix D of the "Microsoft Windows User's Guide," which
 comes with the retail product. Other references are the "International
 Concerns" section of the "Microsoft Windows Software Development Kit
 Application Style Guide" and the March 1988 issue of the "Microsoft
 Systems Journal," which contains an article about Kanji information.

 Please note that if you use Write, you do not want to convert your
 file or include the formatting information when you save it. If you
 use Write, you should select the ANSI font or System font. Notepad
 uses only the System font.

 If you are going to port your application to European machines, you do
 not need to load the European keyboard driver. You also do not need a
 European keyboard, the European version of the retail product, or a
 special version of the Windows SDK.

 To port your application to Kanji, you will need the following:

 1. A Japanese machine, such as the NEC PC9801, the Fujitsu FMR, or the
    Panacom CVM.

 2. A Japanese editor to write the software. Notepad or Write will work
    for doing the conversion.

 3. Japanese MS-DOS and Microsoft Windows released by the OEM for that
    particular machine. We do not retail Kanji Windows.

 4. The Kanji Windows SDK available from the Windows OEMs. Again, we do
    not retail the SDK ourselves.

 Note: your application will also need to have its RC file modified and
 it will also need to support double-byte characters.


 652. MAPSYM Error: "Unexpected eof"; Lines More Than 80 Characters

 If MAPSYM issues the error "Unexpected eof", check your .MAP file for
 lines with more than 80 characters in them. MAPSYM has an 80-character
 limit for lines, and will generate an incorrect .SYM file (or not
 generate one at all) if the lines are too long. Lines can get too long
 because the names of the functions are extremely long.

 Look in the PUBLICS section of the .MAP file for your function names,
 checking to see if any of the lines are greater than 80 characters.

 For example, the following symbol will result in a line greater than
 80 columns:

    SUPERDUPERMONDOLIBRARYROUTINE

 However, it will fit in less than 80 columns if it is shortened as
 follows:

    SUPERDUPERMONDOROUTINE


 653. RC_GDI20_OUTPUT Flag Is Missing from WINDOWS.H File

 The definition of the flag RC_GDI20_OUTPUT is missing from WINDOWS.H.
 The correct definition is as follows:

    #define RC_GDI20_OUTPUT 0000020     /* or 0x10 in hex. */


 654. Using ExtTextOut() with No Rectangle Given

 Page 241 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" Version 2.00 incorrectly implies that a
 rectangle must be given to ExtTextOut() for either clipping or
 opaquing. This is not true. If neither is desired, both wOptions and
 lpRect may be passed NULL.


 655. Windows Crashes When an Application Uses Many DLLs

 There is a problem in Windows Version 2.03 and 2.10 that will cause
 the system to hang when your application exits. This may occur if your
 application contains more than 15 DLLs that have links between each
 other.

 This problem is caused by an overflow condition within Windows that
 results from having many DLLs linked to other DLLs. A link between two
 DLLs exists if there is one routine in one DLL that is referenced by a
 routine in another DLL. Having many routines in one DLL, or having
 many calls between routines in two DLLs does not increase the link
 count and will not worsen this problem. Microsoft has confirmed this
 to be a problem in Versions 2.03 and 2.10. We are researching this
 problem and will post new information as it becomes available.



 656. Exiting Windows in the Stand-Alone Environment

 If you spawn another application (such as Control Panel), when you are
 running a Windows application in the stand-alone environment, the
 system will hang instead of exiting back to DOS when your application
 closes down and you then close the application you spawned. This
 problem occurs because the application that did the spawning MUST
 outlive the application it spawned [this is true even outside the
 single application environment (SAE) world]. To be sure that all
 applications you have spawned are completed before the main
 application exits, the primary application should call ExitWindows()
 to close the other applications before exiting. The correct way of
 handling this problem is described below.

 Add the following in your application's window procedure:

 WM_CLOSE:
     /* If running in SAE mode */
     if ( GetModuleHandle((char far *)"MSDOS.EXE") ==0)
      {
         exec.commandline= "Suicide command line";

         /* fill in other fields of exec structure as necessary according to
            the spawn article in the KnowledgeBase Q35961 */

         Int21Function4B(0, "app.exe", &exec);
      }
     else

      Do your usual processing of a WM_CLOSE message

     break;

 WM_ENDSESSION:      /* standard processing of this message for all apps */
     if (wParam!=0)  /* The session is being ended */
     {
     Do clean up processing.
     }
     else
     {
     The session is not really ending since some other app voted no to the
     WM_QUERYENDSESSION message
     }

     break;

 Add the following in the very beginning of your WinMain() routine:

 extern  int     FAR PASCAL lstrcmp( LPSTR, LPSTR );
 void far PASCAL ExitWindows(LPSTR);

 int PASCAL WinMain (hInstance,
                     hPrevInstance,
                     lpszCmdLine,
                     cmdShow)

 HANDLE hInstance, hPrevInstance;
 LPSTR lpszCmdLine;                      /* Length of command line */
 int cmdShow;                            /* Iconic or tiled at start. */
 {
     if (lstrcmp(lpszCmdLine, "Suicide command line")==0) /* they're equal */
     {
     ExitWindows(0L);
     }
     else

     /* proceed with normal beginning of your application */

 }

 Description

 Calling the ExitWindows() routine will result in WM_QUERYENDSESSION
 and WM_ENDSESSION messages being sent to every application in the
 system except the one calling ExitWindows(). If all the applications
 return "YES" to the WM_QUERYENDSESSION message, the WM_ENDSESSION
 message will be the LAST message each application receives and the
 ExitWindows() function will NOT return before the Windows session is
 terminated. If some application returns "NO" to the WM_QUERYENDSESSION
 message, the ExitWindows() function will send the appropriate
 WM_ENDSESSION message to every other application and then it will
 return FALSE to its caller.

 In the above situation, the application that wishes to shut down all
 other applications and then end must spawn a dummy application (which
 happens to be another instance of itself) that then calls
 ExitWindows(). This is necessary so that it will have a chance to get
 the WM_QUERYENDSESSION and WM_ENDSESSION messages and do its own
 clean-up processing before the session is terminated. If one of the
 applications returns "NO", the ExitWindows() call will return FALSE
 and the dummy application will be completed.

 Note

 If you use this method to terminate, be aware that if someone runs
 your application with the "Suicide command line" in either SAE or the
 standard version of Windows, ExitWindows() will be called and the
 system will be terminated if no application returns "NO" to the
 QUERYENDSESSION message. To avoid this possibility, make the special
 command line unique so that it won't accidentally be typed when
 someone starts your application.


 657. CVW: "No Symbolic Information" Is Not "Normal Behavior"

 If CodeView issues the message "No symbolic information," this means
 that your application has no symbolic information for it, not that
 Windows lacks symbolic information.

 To remedy this, make sure you are compiling and linking correctly, as
 follows:

     cl /Zi  ....        (or /Zd for line numbers only)
     masm /Zi ....
     link4 /CO .....

 There is a documentation error on Page Update-7 of the "Microsoft
 CodeView for Windows" manual, in the section titled "Beginning the
 Session."

 The manual states the following:

    The message 'No symbolic information' merely indicates that
    you have no symbolic information for the file WIN.COM. Since
    the debugger cannot load symbolic information for .COM files,
    this behavior is normal. However, you still have information
    available for the application, assuming you have used the /L
    option as described above.

 This is incorrect. CodeView only issues this error message when your
 application lacks symbolic information; therefore, it is not "normal
 behavior" to receive this error during debugging.


 658. Use of Parent's Edit Menu in Dialog Box Edit Controls

 Question:

 I am attempting to create a dialog box that contains edit controls
 implemented in such a way that the following items occur:

 1. I enter data into any of the edit controls and then select some of
    the text.

 2. I click the parent window's Edit menu to Cut, Paste, etc. This
    should move text to or from the Clipboard.

 The problem is that the focus is removed from the edit box and the
 selected text can't be cut or copied. Is it possible to use the parent
 window's edit menu to edit the contents of a dialog box's edit
 control?

 Response:

 Yes, this is possible under Windows. To create a dialog box containing
 edit controls that can be modified by your parent window's edit menu,
 you must add code to your Edit menu to send a message to the dialog
 box's edit-control window, asking it to perform the Cut, Copy, Paste,
 etc. operation.

 To do this, use GetDlgItem() to get the window handle of the dialog
 box's edit control, then use SendMessage() to tell that window to
 perform a WM_CUT, WM_COPY, etc. operation on the selected text.

 There is some additional logic you will need to handle, such as the
 following:

 1. Where should the focus go after the operation is performed?

 2. What if no text was selected?

 3. How do I know if the dialog box exists?

 A program demonstrating how to do this can be downloaded from the
 OnLine Software Library. It is called CREATDLG.EXE, and the source for
 it is in CREATDLG.ARC. The code is commented, and there is a
 README.TXT file discussing the coding issues.

 This file can be found in the Software Library by searching for the
 filename CREATDLG.ARC, the Q number of this article, or S12127.


 659. Windows SDK: SCROLL LOCK Key Missing from Virtual Key Table

 The SCROLL LOCK key is not included in the list of virtual keys in
 Table 3.10 on Page 280 of the "Microsoft Windows Software Development
 Kit Programmer's Reference" manual for Versions 2.03 and 2.10 because
 the SCROLL LOCK key is not defined in the WINDOWS.H header file.
 Therefore, if you want to perform a GetKeyState() on the SCROLL LOCK
 key, there is no VK_ code to use.

 To work around this problem, add a new entry to WINDOWS.H for the
 SCROLL LOCK key, as follows:

    #define VK_SCROLLLOCK    0x91


 660. Windows SDK: Checking for a Valid Selection in a List Box

 Question:

 In the Windows Cardfile application, if you bring up the Open dialog
 box and get into a directory that doesn't fill the entire list box and
 click in the empty part of the list box, you get garbage in the file
 text box. Is there any way to tell that garbage was returned?

 Response:

 Cardfile did not check the item number that was selected in the list
 box. You need to ask the list box which item was selected. If it is in
 a "blank" area, a -1 value will be returned.

 You can add the following routine to your dialog routine. Place this
 code right after the case statement for the list box in question. If
 you put the following code in Cardfile, a message box should be
 displayed when you click in a blank area:

 ----- start
    item = SendDlgItemMessage(hwnd, ID_LISTBOX, LB_GETCURSEL,0,0L);

    /* item == -1 if item is not in list */
    if (item < 0L ) {
      MessageBox(hwnd,(LPSTR)"listbox",(LPSTR)"item>list",MB_OK);
      break;  /* do nothing */
    }
 ----- end


 661. New Version of LIBENTRY.ASM in Software Library

 Previous versions of LIBENTRY.ASM are incorrect because they did not
 use Windows Prolog/Epilog code. Under certain low-memory conditions,
 where the DLL makes calls in its LibMain procedure that can cause
 memory movement, the system can lock up.

 Also, if the call to LocalInit in libentry fails, the current
 LIBENTRY.ASM will not clean up the stack properly.

 This problem has been corrected in the archived file LIBENTRY (which
 contains LIBENTRY.ASM) located in the OnLine Software Library. Use
 this file as a replacement for the LIBENTRY.ASM that is shipped with
 the SDK.

 LIBENTRY can be found in the Software Library by searching on the
 keyword LIBENTRY, the Q number of this article, or S12561. LIBENTRY
 was archived using the PKware file-compression utility.


 662. No Size Limits for Bitmap Planes in Windows Version 2.x

 Windows Version 2.x not only removes the 64K limitation for the total
 size of bitmaps, it also allows the individual planes of a color
 bitmap to be larger than 64K.

 However, bitmaps are still limited by the amount of memory available
 in the system; even if the device driver allows very large bitmaps,
 Windows may not be able to find enough free memory to create the
 bitmaps.


 663. SendMessage() and Becoming the Active Application

 Question:

 I have an application that is putting up a dialog box when it receives
 a message from another application through SendMessage(). However,
 when I do this, all mouse input seems to be lost and does not seem to
 get processed until the dialog box disappears. This is not what I
 want. I would like to be able to put up the dialog box and have
 everything proceed normally. What is wrong?

 Response:

 An application that receives a message from another application
 through SendMessage() cannot become the active application until it
 returns control from SendMessage(). This is mentioned in the
 documentation on InSendMessage() on Page 339 of the "Microsoft Windows
 Software Development Kit Programmer's Reference" for Versions 2.00,
 2.03, and 2.10.

 The following are three possible solutions to the problem:

 1. Have the sending application post the message to your application
    through PostMessage(). At SendMessage() time, none of the
    applications can retrieve messages from their queues; however, this
    is not a problem with PostMessage(). If the message is posted, you
    can put up the dialog box when you receive the message.

 2. Put up a message box instead of a dialog box. A message box is the
    only kind of window that can be created when a SendMessage() is
    done from another application because MessageBox() was written to
    work in this scenario. Of course, you are limited as to the kind of
    input you can receive with a message box.

 3. "Post back" a return value from the message sent with SendMessage()
    before putting up your dialog box. You can use the function
    ReplyMessage() to accomplish this. ReplyMessage() allows you to
    return a value to the sender without returning control to the
    sender. If you call ReplyMessage() before you create the dialog
    box, no input will be lost.


 664. Sharing the COM Port in Windows with an Old Application

 An old application that uses the serial port cannot share that serial
 port reliably with a Windows application. Characters may be lost
 during task switching.

 An old application that modifies the COMn port (where n is the COM
 port number) and is running under a .PIF file that does not activate
 the "directly modifies the COMn port" switch will cause this problem.
 Windows will allow any Windows application to use the COMn port. When
 the old application gives control of the port back to the Windows
 device driver, any information received by the COMn port may be lost
 during the switch.

 To solve the problem without allowing the old application to share the
 COMn port under Windows, activate the "directly modifies the COMn
 port" switch in the PIF Editor configuration session.


 665. Don't Use SB_BOTTOM or SB_TOP to Scroll an Edit Control

 To scroll the text in an edit control to the beginning or end, send
 the EM_GETLINECOUNT message to get the number of lines of text, then
 send the EM_LINESCROLL message to scroll by the returned number of
 lines, instead of sending the SB_BOTTOM or SB_TOP messages.

 SB_BOTTOM signals that the application should scroll to the end of a
 range. Likewise, SB_TOP signals that the application should scroll to
 the beginning of a range. However, edit controls ignore these
 messages. In addition, a scroll bar will generate this message only if
 it has the focus. The scroll bar doesn't get the focus in an edit
 control (or in general when it is attached to a window). You can tell
 if the scroll bar has the focus if the scroll box (thumb) is flashing
 (for an example of this, see the scroll bars for setting colors in the
 Control Panel).

 The following is an example of where this problem occurs:

 You are trying to get an edit control to scroll to the bottom of a
 file because you want to create a dialog box that displays a text file
 and automatically scrolls to the bottom of the file when it is first
 displayed.

 At the end of your INIT_DIALOG for the dialog box procedure, you send
 the following command to your edit control:

    SendMessage (hCtl, WM_VSCROLL, SB_BOTTOM, 0L);

 However, the text displayed in the control has not been scrolled at
 all. Substituting SB_PAGEDOWN or SB_PAGEUP scrolls correctly.


 666. Windows SDK: Getting International Information from Excel

 Question:

 Is there a way in Excel to test the current country settings, or do I
 have to create my own dynamic link library (DLL) to call one of the
 Windows functions to get this information?

 Response:

 The information you are looking for is already provided under the
 [intl] section of the WIN.INI file. Therefore, you don't have to
 create a DLL of your own; instead, use the GetProfileString() function
 already provided in Windows. The following is a working sample of a
 macro sheet that will get the information you want:

    Call GetProfileString
    =REGISTER("Kernel","GetProfileString","FCCCFH")
    =CALL(R[-1]C,"INTL","iCurrency","DEFAULT",,256)
    =CALL(R[-2]C,"INTL","sDecimal","DEFAULT",,256)
    =CALL(R[-3]C,"INTL","sList","DEFAULT",,256)
    =RETURN()

 For more information on the values found under the [intl] section of
 the WIN.INI file, please see the "Microsoft Windows Software
 Development Kit Programmer's Reference" under "File Formats,
 International Section" on Pages 659 and 660.


 667. CodeView for Windows: Cannot Use /2 Switch on IBM PS/2

 To use the /2 switch with CodeView for Windows (CVW.EXE), your
 computer must be equipped with both a monochrome display (MDA) and a
 color display (CGA/EGA/VGA). IBM PS/2 computers aren't currently
 configurable this way because they come with built-in VGAs or MDAs,
 but not both. If a hardware vendor starts selling MDAs that can be
 added to systems with built-in VGAs, this problem will be solved.

 Currently, there is no solution to this debugging restriction other
 than using CodeView in sequential mode (/T) with a debugging terminal.

 The preferred debugging environment for Windows development is to run
 your Windows application on a color monitor driven by a graphics card
 (such as an EGA) while CodeView runs on a monochrome display driven by
 an MDA.

 This requires that you install both an MDA and a CGA/EGA/VGA card on
 the same machine. Normally this is no problem. However, the
 environment on IBM's PS/2 computers is slightly different because the
 PS/2 comes with either an MDA or a VGA (but not both) built into the
 machine.

 An MDA card that plugs into the PS/2 and works in conjunction with the
 built-in VGA would allow PS/2 owners to have a dual-monitor system.
 However, such a card is not currently available.

 Another possible solution is to have CodeView run on the same screen
 as your application by making it windowed so that it would appear as
 just another Windows application. This feature is under review and
 will be considered for inclusion in a future release.

 However, one disadvantage to this solution is that if you could run
 the debugger and the debuggee on the same screen, you'd find the
 amount of space on the screen very limited: CodeView would take up
 valuable screen "real estate" from your application, and your
 application would take up "real estate" from CVW.


 668. Windows SDK: How Windows Discards Segments

 Question:

 I am having problems understanding how Windows discards segments. Is
 the following scenario possible?

    The environment is set up with very little available memory, and my
    program makes a call to GlobalAlloc(). As control passes from this
    code segment, Windows marks it as unused, so it is now a candidate
    for discarding. In fact, to fulfill the GlobalAlloc() call, the
    original code segment is discarded, either to make room for the
    Windows code that allocates memory or for the memory itself. Now,
    Windows has allocated the memory, and it needs to load in the
    original code segment to return from the call from GlobalAlloc().
    There is still not enough memory, so Windows looks for something to
    discard to make room. It discovers a large chunk of discardable
    memory (what it has just allocated for the program), and discards
    it. So, the call to GlobalAlloc() returns a handle that is
    immediately discarded.

 Response:

 The scenario you described is entirely possible, but it does not
 commonly occur. Windows keeps an LRU table, which contains the least
 recently used discardable segments. When one of these segments needs
 to be discarded to allow room for another segment, it continually
 discards the least recently used segment until enough memory is free
 to load the current segment. In the case you described, your
 application calls GlobalAlloc(), which causes segment discarding. Your
 application code segment is the most recently used except for any
 Windows code that is loaded as a result of the GlobalAlloc() call;
 therefore, for this segment to be discarded, all other discardable
 segments would have to be discarded first.


 669. Definition of BR Structure Used for STRETCHBLT Printer Escape

 Question:

 The following is an extract from the README.WRI file on the Microsoft
 Windows Software Development Kit Version 2.10 Development Utilities
 disk. I am trying to use the STRETCHBLT printer escape function and do
 not know how the BR structure is defined.

    APPENDIX C:
    Printer Escapes

    * short Escape (hDC, STRETCHBLT, nCount, lpInData, lpOutData)

    Comments

    The data structure pointed to by the lpInData parameter contains
    the following items:

       WORD ixDest;
       WORD iyDst;
       WORD cxDst;
       WORD cyDst;
       BITMAP FAR *lpbm;
       WORD ixSrc;
       WORD iySrc;
       WORD cxSrc;
       WORD cySrc;
       DWORD rop;
       DWORD lpbm;
       BR FAR *lpbr;

 Response:

 The definition of the BR structure is as follows:

 /* A physical brush (the device driver's complete brush description) */
 typedef struct
     {
     LOGBRUSH lb;        /* A copy of the logical brush */
     long   lid;         /* The brush id */
     char   rgbPat[8];   /* The brush pattern bitmap */
     }BR;


 670. Windows SDK: Values for WM_VSCROLL and WM_HSCROLL Messages

 Below are descriptions of the values that are passed in the wParam of
 WM_VSCROLL and WM_HSCROLL messages, their purposes, and when they are
 generated. Since these values are used for both WM_VSCROLL and
 WM_HSCROLL and are used for scrolling other than text, the terms
 "line" and "page" actually refer to an increment smaller than the
 current window and the size of the current window. "Line" can refer to
 a column of text characters, a column or row of pixels, or some other
 minor increment. "Page" can refer to a window's width of text, a
 window's width portion of a graphic, or some other major increment.

 Value              When Generated/Purpose

 SB_BOTTOM          Generated only if the scroll bar has the focus and
                    the END key is pressed. Not generated when
                    scrolling with the mouse. Tells the application to
                    scroll to the end of a range.

 SB_ENDSCROLL       Generated at the end of a series of incremental
                    scroll operations, (i.e., line up, line down, page
                    up, page down). For example, if the mouse is held
                    in the down arrow, a series of SB_LINEDOWNs are
                    generated and ended with the SB_ENDSCROLL. This
                    allows the application to perform some action for
                    each incremental scroll, or only when they are
                    complete.

 SB_LINEDOWN        Generated by clicking in the down arrow on the
                    scroll bar or by pressing the DOWN ARROW key if the
                    scroll bar has the focus. Tells the application to
                    scroll down one "line."

 SB_LINEUP          Generated by clicking in the up arrow on the scroll
                    bar or by pressing the UP ARROW key if the scroll
                    bar has the focus. Tells the application to scroll
                    up one "line."

 SB_PAGEDOWN        Generated by clicking below the thumb on the scroll
                    bar or by pressing the PGDN key if the scroll bar
                    has the focus. Tells the application to scroll down
                    one "page."

 SB_PAGEUP          Generated by clicking above the thumb on the scroll
                    bar or by pressing the PGUP key if the scroll bar
                    has the focus. Tells the application to scroll up
                    one "page."

 SB_THUMBPOSITION   Generated at the end of a series of SB_THUMBTRACKs,
                    which result from dragging the thumb. It returns
                    the final position of the thumb. This allows the
                    application to perform some action during scrolling
                    or only when the thumb is released.

 SB_THUMBTRACK      Generated when the thumb is dragged to a new
                    location in the scroll bar. It returns the current
                    position of the thumb.

 SB_TOP             Generated only if the scroll bar has the focus and
                    the HOME key is pressed. Not generated when
                    scrolling with the mouse. Tells the application to
                    scroll to the start of a range.


 671. Windows SDK: GetProfileInt() Returns Zero

 GetProfileInt() should return the value of an integer key from the
 Windows initialization file WIN.INI. However, if this value is
 negative, GetProfileInt() will return a zero rather than the negative
 value.

 To work around this problem, use GetProfileString() rather than
 GetProfileInt() and convert the character string to an integer by
 using the C run-time library function atoi().


 672. Free Memory Reported in the MS-DOS Executive About Box

 Question:

 Because my application consumes large chunks of global memory, I would
 like to inform the user of how much of this precious resource is
 still available.

 In a sample program, I found a reference to the command GlobalCompact()
 with -1 as argument. This call is supposed to return the total number
 of bytes free (instead of the largest chunk if called with argument
 0).

 However, the figure returned is between 3K and 9K below the figure
 reported in the About box in the MS-DOS Executive. Is there a better
 (more exact) way to determine the amount of free global memory?

 Response:

 GlobalCompact(-1) and GlobalCompact(0) are similar. Both report the
 largest contiguous block of free conventional memory that could be
 allocated if discarding and compacting take place. The difference
 between them is that GlobalCompact(-1) actually compacts and discards
 segments, while GlobalCompact(0) calculates the result without actually
 causing the effect. In general, neither one will return the same
 number as reported by the MS-DOS Executive. The Executive finds the
 largest contiguous block as well as all free blocks sandwiched between
 fixed code and data segments.

 To find all free memory available to your application, repeatedly call
 GlobalCompact(0) followed by a temporary GlobalAlloc() using the
 number of free bytes returned from the GlobalCompact(0) call. Repeat
 these steps until GlobalCompact() fails to find any free memory. Sum
 all the bytes allocated in each step, then free these allocated
 blocks. This is the total free memory available to your application.
 Your application may or may not be able to use the small fragmented
 blocks of free memory efficiently.

 Under LIM Version 4.0 Expanded memory, the above steps will continue
 to locate all of the free memory available to your application, but
 will have no relation to the number reported by the MS-DOS Executive.
 For background information on EMS, please refer to the KnowledgeBase
 article originally published in the "Microsoft Systems Journal" on the
 use of LIM Version 4.0 expanded memory by Windows Version 2.x.


 673. Spawning DOS Commands in a Stand-Alone Environment

 Question:

 We are installing a stand-alone version of our Windows program.
 Currently, our Windows program spawns tasks that format floppy disks,
 copy files to the formatted floppy disks, etc. When we run our program
 under the stand-alone environment, the program asks for WinOldAp(). Is
 there any way to get around this? Can we spawn DOS functions in an
 SAE?

 Response:

 WinOldAp() is not provided with the SAE environment; therefore,
 COMMAND.COM cannot be spawned to handle tasks such as copying and
 formatting. This is not allowed in the SAE licensing agreement.


 674. Windows SDK: WM_CANCELMODE Message Documentation Missing

 Question:

 What is the meaning of the WM_CANCELMODE message located in WINDOWS.H?

 Response:

 The WM_CANCELMODE message is sent out to cancel any mode the system is
 in, such as tracking of a scroll bar, or moving of a window. This
 message is sent when a message box comes up (e.g. while you have the
 button down in a scroll bar). This information was not included in the
 "Microsoft Windows Software Development Kit Programmer's Reference
 Guide" for Version 1.04. This information was corrected in Version
 2.03 of the Windows SDK manuals.


 675. Using SYMDEB with Monochrome Monitor Attached to Same Computer

 Question:

 Is it possible to use SYMDEB with a monochrome monitor plugged into
 the same computer, instead of connecting a remote terminal to a serial
 port?

 Response:

 Yes. You can either hook up a remote terminal to a serial port, or set
 up a secondary monochrome monitor on the same computer for debugging
 purposes.

 To use the secondary monochrome monitor, install it in a free slot and
 set the display switches to the settings recommended by the computer
 manufacturer. When you start SYMDEB, use the /m option to redirect
 output to the second monitor.

 When the /m option is given, SYMDEB redirects output to the secondary
 monitor, but continues to use the system keyboard for input until the
 application being debugged is started. While the application is
 running, SYMDEB yields complete control of the keyboard to the
 application. As soon as the application reaches a breakpoint or
 terminates, SYMDEB reclaims the keyboard and permits user input again.

 There is also a screen-swapping method used by SYMDEB; however, it is
 not appropriate for Windows.


 676. Windows: GENERIC.C Source Code Misprinted in Learning Guide

 The GENERIC Windows application listed on Pages 46-48 in the
 "Microsoft Windows Software Development Kit Learning Guide" for
 Versions 2.x is incorrect. The GenericInit() procedure is listed
 twice, and the About() procedure is missing.

 This documentation error was corrected on Page 38 of the "Microsoft
 Windows 2.0 Software Development Kit Update."

 The correct source code listing is also available on the Sample Source
 Code disk in the \LEARNING\GENERIC subdirectory.


 677. Windows SDK: How Windows/386 Handles INT 5C and INT 2A

 Question:

 Can Windows/386 handle INT 5C and INT 2A?

 Response:

 Yes, but there are restrictions. The following are three ways to
 handle the problem:

 1. Use INT 5C and observe the following restrictions:

    a. Windows/386 will only support TWO simultaneous asynchronous 5C
       calls with the InDOS flag clear and a maximum buffer size of
       2048 bytes.

    b. If you exceed the maximum buffer size, you will get the message
       "Too many outstanding NCB's."

 2. Use INT 2A or INT 5C and notify Windows/386 when you are in a
    critical region. If you use 2A, there are no restrictions on buffer
    size or on how many buffers you can use. However, if Windows/386
    changes virtual machines while you are in a critical section, it
    will crash. To run reliably, you MUST notify Windows/386 when you
    enter and leave a critical section by incrementing the InDOS flag.
    The method for doing this is described in detail in the Windows/386
    API which is posted in this KnowledgeBase as article Q36020 and in
    the Software Library as the file 386API.ARC. This file can be found
    in the Software Library by searching for the filename, the Q number
    of this article, or S12044.

    To test this code, run your network application in the background
    along with another background application and a nonexclusive
    foreground application. If the critical section code is not
    implemented correctly, your application will not run for more than
    a couple minutes.

    Some network applications (such as VTP) do not have short critical
    sections. If the network program stays in a critical section for
    any period of time, the system will appear to hang.

    It is important that the InDOS flag remain nonzero until the async
    request has been serviced.

 3. Put your network buffer in a TSR. This way you can use 2A and have
    whatever buffers you please. However, you may not want to have your
    network program in a TSR. Obviously, this is not a solution for
    Windows programs.


 678. Windows SDK: Communications Error CE_OVERRUN

 Question:

 While using the COM1: communications port at 9600 baud, I occasionally
 get CE_OVERRUN errors. What does this error mean? If you know that a
 character is lost, why can't Windows return the character?

 Response:

 The CE_OVERRUN error is due to either software or hardware handshaking
 problems. Communications under Windows is are implemented as follows:

 A comm driver is provided that handles all interactions with the comm
 hardware. The comm driver includes the interrupt service subroutine to
 manage the asynchronous event-driven communications.

 The comm driver uses transmit and receive queues to buffer incoming
 and outgoing data. The application can read and write to/from these
 buffers and poll the status of the driver. The application must
 set up the buffers and handshaking; otherwise, the comm driver will
 not interact properly with the hardware and data will be dropped.

 A CE_OVERRUN error indicates that the comm driver did not empty the
 hardware receive register before another character arrived. This is
 probably due to a lack of handshaking (XON/XOFF or hardware) or too
 small of a receive buffer. Windows cannot recover this dropped
 character because the character was dropped by the hardware.


 679. Setting Printer to Landscape or Portrait Mode from Application

 Question:

 How can I change the orientation of the printer from portrait to
 landscape (or vice versa) without going to CONTROL.EXE? I would like
 to do this from my program. Is this possible?

 Response:

 There is no printer escape to control portrait/landscape selection.
 Currently, each printer driver handles this internally and only allows
 changes from the configuration dialog box posted by the printer driver
 itself. The Control Panel invokes this dialog box using the printer
 driver. Therefore, the recommended method for changing this printer
 mode is to spawn Control Panel from your application.

 This feature is under review and will be considered for inclusion in
 a future release.


 680. Multi-Instance of Application and Expanded Memory Banks

 Problem:

 I am having difficulty using the -m (multi-instance) parameter of the
 resource compiler. I want to have a Windows Version 2.10 application
 use different banks of expanded memory each time a new instance is
 started.

 Response:

 The RC compiler does set a bit in the EXE header in response to using
 the -m switch. However, the Windows loader does not yet support this
 feature and does not load new copies of the application into separate
 EMS banks. This feature is under review and will be considered for
 inclusion in a future release of Windows.

 Therefore, the effect of the -m switch is to load only one copy of the
 code segments and each instance's data segment into the same EMS banks
 just as it would normally occur.


 681. Windows SDK: Checking the State of the Carrier Detect Line

 Question:

 Is it possible to determine the current state of the CD (carrier
 detect) line of a comm port by using Windows comm routines?

 Response:

 The comm driver only returns information about a change in the carrier
 detect line as reported by bit 3 of the modem-status register of the
 UART. The actual CD state flag, bit 7 of the modem-status register, is
 only used internally to the driver.

 If an intelligent modem such as a Hayes is connected, the application
 could send status requests to the modem to determine if the modem has
 lost the carrier. If connecting directly to some other hardware
 device, it will not be possible to determine the CD state, only that
 there was a change, possibly just a flicker. To add this feature would
 require modifying the comm driver; your modified comm driver would
 have to ship with your product and Windows would need to be
 reinstalled by the customer. The source code for the driver is
 available in the Windows Device Driver Kit. This kit is available
 under a nondisclosure agreement by submitting a request through
 OnLine.


 682. Windows SDK: Graying the Child Window Text

 Question:

 I'm having trouble graying text inside a control. I have two child
 windows that are of the class "button." There are times when they
 should not be active and I would like to gray them out to prevent user
 action. I also have a child window of class "static" that displays
 information that can become out of date. I would like to gray out the
 text of the information whenever the information is no longer up to
 date.

 I've tried using GrayString(), but either I'm using it wrong or it
 doesn't work for control windows. I can produce the gray text anywhere
 in the main window, except in the buttons and on the static window.

 How would I go about graying the text in a child window?

 Response:

 Controls are windows (child windows) that are responsible for handling
 keyboard, mouse, focus, activation messages, etc. They are also
 responsible for painting themselves and handling text strings. Using
 the GrayString() function only applies to your own windows, or to the
 control windows if you are subclassing them and attempting to modify
 the paint-message handling.

 None of this is necessary. Button and static controls will gray their
 text automatically if they are disabled with the EnableWindow()
 function.


 683. Windows SDK: Single-Line Mode in DrawText()

 When using DrawText() and one of the single line mode format styles
 (DT_BOTTOM, DT_VCENTER, or DT_TOP), the DT_SINGLELINE format must also
 be used. The default format is multiline. To use one of the three
 single-line formats, the format must be changed to single line. An
 example is as follows:

    DrawText(hDC, (LPSTR)lpString, lstrlen((LPSTR)lpString),
             (LPRECT)&lpRect, DT_SINGLELINE | DT_VCENTER);

 This requirement is also mentioned on Page 129 of the "Microsoft
 Windows Software Development Kit Programmer's Reference" manual for
 Versions 2.00, 2.03, and 2.10.


 684. Intdosx() Execution of Windows Applications

 Question:

 I use Intdosx() to execute several other Windows applications. The
 application to be executed is selected through a list box. When trying
 to execute a LARGE windows application, the tiled window appears on
 the screen and the application freezes. The same application works
 correctly if executed from the MS-DOS Executive window. I also pass a
 command line to the application through Intdosx(). In the application
 WinMain(), the first character of the command line disappears. Why?

 Response:

 The recommended and supported method of spawning applications is to
 use the INT 21 Function 4Bh call to spawn an application. Intdosx()
 is a C library function that is not supported under Windows and does
 not behave under Windows. There is an example of spawning in the
 Software Library named WINSPAWN.ARC. This file can be found in the
 Software Library by searching for the filename WINSPAWN.ARC, the Q
 number of this article, or S12011.


 685. Windows SDK: Changing Fonts in Edit Controls

 Problem:

 I am trying to modify the font style and size of the text in an edit
 control box. I am able to modify the style; however, the size always
 seems to be 10. If I use a larger size (anything larger than 10 or a
 larger-sized font like ROMAN) the characters are truncated.

 I am sending the EM_SETFONT message to the edit control. In the
 programmer's reference manual, the EM_SETFONT message is given in the
 "Messages Overview" chapter, but is not described in the "Message
 Directory" chapter. I called the message as follows

    SendMessage(hEditControl,EM_SETFONT,hFont,0L);

 where hFont is a handle to the font desired.

 Is there any way to also modify the character size of an edit control,
 or is it fixed at the system font size of 10?

 Response:

 The EM_SETFONT message does not exist.  See article Q37066 for more
 information.

 Windows looks for the system font information when Windows loads. From
 that point on, Windows calculates all of the character positions from
 the start-up information, which includes font height and width. The
 edit control is fixed at the system font. Any modifications can and
 will cause strange errors in the edit-control information.

 Suppose the customer positions his or her cursor over the 22nd
 character in the edit control and changes the character from an "a" to
 an "s". Windows, on the other hand, thinks that the cursor is placed
 over the 20th character, (based on the width of the system font), and
 replaces the 20th character with the "s". Similarly, errors can occur
 with vertical discrepancies.

 In other words, it is not recommended to modify the font style and
 size of the text in an edit-control box. You may be able to calculate
 the error for a given display configuration, but unless your program
 is designed for a vertical market, where the machine configuration is
 identical, problems will exist.


 686. Combining Modal and Modeless Dialog Boxes Sample Program

 Question:

 I'm having a problem combining modal and modeless dialog boxes. To
 duplicate the problem, I have modified the OnLine Software Library's
 TAB.ARC application (S12056), which changes HELLO.C to produce modal
 or modeless dialog boxes. Now the Go menu choice brings up a modeless
 dialog box and this box's Demo button brings up a modal dialog box
 with the modeless box as the parent. Everything works correctly until
 you close down the main window with both dialog boxes on the screen.
 This causes a fatal exit code 7 (invalid window handle). Why is this
 happening and how do I correct it? If possible, I would prefer not to
 simulate the modal box with a modeless one.

 Response:

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.

 There is a problem trying to get the modal dialog box to close
 correctly. We have created a workaround for this problem by creating
 two modeless dialog boxes, with the second dialog box acting as if it
 were a modal dialog box. When the second dialog box is called, we
 disable the first dialog box, and then as the "modal" dialog box is
 destroyed, we re-enable the first dialog box.

 The effect is the same, but this method does not RIP upon closing from
 the Control menu box.

 There is a sample application, MDLMDLS.ARC, in the Software Library
 that illustrates how to get around this problem and contains the
 necessary code to achieve the desired effect. This file can be found
 in the Software Library by searching for the filename MDLMDLS.ARC,
 the Q number of this article, or S12159.


 687. Limit to the Number of Characters Stored in a List Box

 Question:

 What is the limit to the number of characters stored in a Windows list
 box? Is everything stored in one buffer or are the items linked
 together in a list (i.e., total = number of chars + number of items)?

 Response:

 The memory limit is dependent strictly on the number of characters,
 not on the number of items in the list box. The memory for the strings
 is allocated from global memory, but the internal code assumes that
 the total amount of memory required by the strings in the list box
 will not exceed 32K. If the total memory required by the strings is
 more than 32K, the list box will not function correctly.


 688. Windows SDK: Changing Z-Order Using SetWindowPos()

 Problem:

 When using the Windows Version 2.10 function SetWindowPos() to order a
 window on the screen relative to the other windows (Z-order), I
 consistently get lockups. The "Microsoft Windows Software Development
 Kit Programmer's Reference" states that a window may be made the
 bottom-most window by passing 1 for the hWnd of the hWndInsertAfter
 parameter. However, this operation causes Windows to lock up and leave
 me with only cursor movement.

 Response:

 If an application tries to set itself to the background after it is
 already the last window in the list, the application drops from the
 Window Manager's list and disappears.

 To get around this problem, check to see if you are already at the end
 of the list using GetWindow(hWnd,GW_HWNDLAST), which returns the
 handle to the last window in the Window Manager's list. If you're
 already the last in the list, don't call SetWindowPos(hWnd,1,,,,,,).

 You may encounter some problems with screen refreshing after setting
 your window to the last in the list. The remaining windows don't
 redraw their client areas, so your window that has gone to the
 background is still visible in the client areas of the windows that
 were below your window when it went to the last in the list.

 To get around this problem, calculate the affected client area, then
 call IntersectRect() to determine the intersecting rectangle if any,
 and call InvalidateRect() [you could call IsRectEmpty() first]. Once
 the desired rectangle is invalidated, call UpdateWindow().

 There is a sample file in the Software Library called SETWNDPS.ARC
 that illustrates the above procedure. This file can be found in the
 Software Library by searching for the filename SETWNDPS.ARC, the Q
 number of this article, or S12163.


 689. Windows SDK: DirDlgList() Changes Current Drive and Directory

 Problem:

 I have noticed a side effect of using the DlgDirList() routine. After
 the routine executes, the current drive and directory are changed to
 the path that was passed to the routine.

 Response:

 This is expected behavior. Page 465 of "Programming Windows" by
 Charles Petzold describes the following scenario, which is similar to
 the one you described:

    "...suppose that drive C has a directory named WINDOWS and that
    your dialog box function contains this code:

       char szFileSpec[] = "C:\WINDOWS\*.TXT";

       [other program lines]

       DlgDirList (hDlg,szFileSpec,IDD_FLIST, IDD_FPATH, 0x4010);

    When DlgDirList returns, the current disk drive and subdirectory
    for the instance of the program will have been changed to
    C:\WINDOWS."

 If you want to overcome this side effect, use the C run-time library
 function chdir(). If you want to change the directory back to the
 original state, use the C run-time library function getcwd().


 690. ALT+SPACEBAR Causes Fatal Exit 0x0100 in Dialog Box

 If you have a dialog box with a menu, it must also have a system menu
 and caption. If you have a menu without a system menu and caption, and
 you enter ALT+SPACEBAR while in the dialog box, you will receive fatal
 exit 0x0100 when exiting the dialog box.

 This behavior can be duplicated by adding a menu item to a dialog box.
 Defining a menu item is described on Pages 36 and 37 of the "Microsoft
 Windows Software Development Kit Programming Tools" manual. Placing a
 menu in a dialog box is described on Page 46 of the same manual.


 691. Windows SDK: SAE Clarification on EISOLDAP.MOD

 Step 4 on Page 5 in the "How to Create a Single Application
 Environment Program" pamphlet states the following:

    "Remove EISOLDAP.MOD from the Additional Drivers Disk (it's
     on the Desktop Applications and Microsoft Windows Write
     Disk if you're using 1.2MB layout). Do make this change in
     SETUP.INF."

 This filename may appear elsewhere in SETUP.INF, but the only place it
 should be removed from is the [system] section. If it is removed
 elsewhere, SETUP.EXE will abort with an error message.


 692. Windows SDK: Not Enough Unallocated Raw Pages in CVW

 CodeView for Windows (CVW) in the Version 2.10 Microsoft Windows
 Software Development Kit requires a minimum of four 16K EMS pages. If
 not enough page frames are available, the following message may appear
 when invoking CVW:

    Not enough unallocated raw pages

 Removing other software that utilizes expanded memory (or changing
 parameters so that the software loads at different addresses) may
 resolve the EMS page conflicts. An example of this is reducing the
 size of a RAMDrive that uses expanded memory.


 693. Windows SDK: Undocumented Fatal Exit 0x000B

 Question:

 I am getting Fatal Exit 0x000B, which is not documented in the manual.
 What does it mean?

 Response:

 The USER Fatal Exit is RIP_DCCACHEFULL, meaning that you may have a
 GetDC() without a ReleaseDC(), or a BeginPaint() without an
 EndPaint(). There are five DC caches within Windows. If you have
 destroyed a window that still has a DC open, you will get this RIP
 code.

 To avoid using one of the five cached DCs, either use CreateDC() to
 obtain the DC, or define the window class as CS_OWNDC or CS_CLASSDC.


 694. Windows SDK: Saving and Restoring the Clip Region

 Question:

 Is there a way to temporarily change the clipping region and later
 restore it to its original value?

 I have a block of text that I would like to print with a different
 clipping region; when it has been printed, I would like to restore
 the first region. At a later time, I will want to do the same for
 graphics.

 Response:

 To save the clipping region (so that it may later be restored), use
 the following code:

    SaveDC( hDC );
    SelectClipRgn( hDC, hRgn );
    /* Code to be used in selected region */
    RestoreDC( hDC, -1 );
    /* The -1 restores the most recently saved DC */


 695. DDE & Windows/386: How to Communicate between Virtual Machines

 Question:

 The old-application support of DDE works for Windows 286, but not for
 Windows/386. It appears that support of DDE doesn't exist under
 Windows/386. Is there any way for a DOS application to communicate
 with a Windows/386 application? When I start a DOS application, I
 expect it to notify other applications that it has started, using DDE;
 however, on Windows/386, this doesn't seem to be happening. Using
 Windows/286, it worked correctly. Is DDE still supported on
 Windows/386?

 Response:

 Windows/386 does not support DDE for old applications. This feature is
 under review and will be considered for inclusion in a future release.

 There are a number of other ways you can communicate with cooperating
 old applications. They are as follows:

 1. The clipboard. DOS applications have the ability to read from or
    write to the clipboard. The methods for doing this are documented
    in the SDK. Microsoft Word incorporates this capability. This would
    be the method of choice in most instances.

 2. A shared file. This is not a super-fast method, although you could
    use a file on a RAM disk or rely on SMARTDRV to reduce actual file
    accesses. Remember to call SHARE.EXE from DOS before starting
    Windows/386 so that your file sharing works properly.

 3. Another method is to start a TSR with a buffer at a fixed location
    that can be read/written to by different applications. For this to
    work, the memory region used must be below the load point of
    Windows/386 or you will find that the different virtual machines
    have their own private copies of the address space.

 4. There is a program called Bridge/386 made by Softbridge
    Microsystems in Cambridge, Massachusetts, that may give you the
    functionality you want. There is a good description of this program
    in the "Microsoft Systems Journal" for September 1988.


 696. Windows SDK: Posting Non-Window Processes (Threads)

 Question:

 I want to convert a set of programs that use a simple non-preemptive
 multitasker to Windows. My problem is that I have about 12 processes
 to convert, but only half of them would actually control Windows on
 the display. The remainder of the processes are basically processing
 engines.

 What is the best way for defining process-only processes within
 Windows? In other words, separately scheduled processes that do not
 have display windows are not driven by Window events for the most
 part, other than user-messages from POSTMESSAGES. Do I define a
 WINPROC without a window; is this possible?

 Response:

 The Windows system does have a method to perform what you need.
 However, the application does not need to create a window. Instead,
 make a call to GetCurrentTask() and send the task handle to the other
 applications that message this task. This windowless application then
 performs a typical GetMessage() loop with a NULL window handle. The
 processing of the message is performed after GetMessage() returns. The
 other applications that signal this application use the
 PostAppMessage() function to activate the GetMessage() call.


 697. Windows SDK: Scrollbar Repositioning Problem

 Question:

 I'm writing an application where one scrollbar control must be able to
 update the values of other scrollbar controls.

 Everything works correctly; however, I have one scrollbar control
 updating another that is like one of the following:

 1. Has a different logical scroll range

 2. Is a different physical size

 Furthermore, when I drag the scrollbar thumb of the "master"
 scrollbar, a "ghost" thumb appears in a different location from the
 "real" thumb, and the two thumbs don't move together. Generally, the
 thumbs move in the same direction, but at different rates, so that the
 farther I move the real thumb, the more the ghost and real thumb
 diverge.

 What is happening and how can I work around this behavior?

 Response:

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.

 The scrollbar control is using a global variable (rather than a
 per-window variable) to store the last position. If an application
 updates the scroll with the mouse capture first and the others
 afterward, the global variable is destroyed by the second scrollbar
 and the thumb appears to jump around.

 If the application updates all of the other sliders, then updates the
 slider with the mouse capture, the ghost slider stops.


 698. No Redirection of Output with SYMDEB and Windows Programs

 Question:

 I am trying to debug my Windows application using SYMDEB with a
 secondary monitor. I invoke SYMDEB with the /m option, which allows
 for debugging with the secondary monitor. When I try using the output
 redirection commands documented on Page 137 in Sections 5.9.58 and
 5.9.59 of the "Microsoft Windows Programmer's Tools" manual, SYMDEB
 creates an empty file 1 byte long. Why is no debug information
 sent to the file?

 Response:

 This is a known problem when using either a remote terminal or a
 secondary monitor for debugging. SYMDEB internally does not check to
 see that you are using a secondary monitor. Therefore, the information
 is not correctly output to the file.

 Microsoft has confirmed this to be a problem with the Microsoft
 Symbolic Debug Utility in the Windows Software Development Kit
 Versions 2.x. We are researching this problem and will post new
 information as it becomes available.


 699. DDE Sample Code and Information

 There is a DDE sample code called DDE.ARC in the Software Library.
 This file can be found in the Software Library by searching for the
 filename DDE.ARC, the Q number of this article, or S10036.

 Unarc the DDE.ARC file, and refer to DDE.DOC for information on
 building the application.


 700. Windows SDK: Posting Frequent Messages within an Application

 Problem:

 I have a Windows application that frequently sends messages to itself.
 I am using the PostMessage() function to send the messages. The
 messages are being posted so frequently that no other processing (e.g.
 mouse movements) takes place.

 Response:

 Because PostMessage() places your messages directly in your
 application queue, mouse and other types of messages may not have a
 chance to be moved from the system queue to your application queue.

 Two methods to avoid this problem are as follows:

 1. Use a PeekMessage() loop to check for mouse and other system
    messages. If none are pending, do a SendMessage(). However, the
    SendMessage()s have to come from the PeekMessage() loop. The
    following is an example:

 while(TRUE) {
     if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
         if(msg.message == WM_QUIT) break;
         /* process message */
     } else {
         /* do other processing */
         ...
         SendMessage(hMyWnd,WM_USER,wParam,lParam);
         ...
     }
 }

 2. Use a PeekMessage() loop and post messages using PostMessage().
    First, do a PeekMessage() with filter values specified for standard
    messages. If true, process those messages. If false, use
    PeekMessage() to get any WM_USER messages. The advantage of this
    method is that the WM_USER message can be posted, using PostMessage()
    from anywhere in the program. The following is an example:

 while(TRUE) {
     if(PeekMessage(&msg,NULL,0,WM_USER-1,PM_REMOVE)) {
         if(msg.message == WM_QUIT) break;
         /* process messages */
     } else {
         if(PeekMessage(&msg,NULL,WM_USER,WM_USER,PM_REMOVE)) {
             /* process user messages */
         }
     }
 }

 FirstWndProc(hWnd,wMsg,wParam,lParam...)
 {
     ...
     PostMessage(hSecondWnd,WM_USER,MywParam,MylParam);
     ...
 }


 701. TAB Key Doesn't Generate WM_CHAR Message if SHIFT Key Down

 In Windows Version 2.10, a WM_CHAR message with wParam equal to VK_TAB
 will not be generated if the SHIFT key is down when the TAB key is
 pressed. This is a difference from previous versions of Windows. The
 change was made because DOS drivers don't translate SHIFT+TAB to an
 ASCII character. This change was made so as to make the translation
 compatible with the DOS translation. Future versions of Windows will
 continue to use the convention used in Windows 2.10.

 As always, a WM_KEYDOWN message is still generated when the SHIFT key
 is pressed or held down, but a WM_CHAR message will not be generated.
 The USER relies on looking at the WM_KEYDOWN messages. Applications
 should look for the WM_KEYDOWN message also. In certain subclassing
 situations, applications may also need to look for the WM_CHAR message
 with wParam equal to VK_TAB if they want to keep the WM_CHAR message
 from going to the original window procedure. This will have to be done
 because the WM_CHAR message is still generated if the SHIFT key is not
 held down.


 702. Windows SDK: Installing from Drive B

 Question:

 I have a 3.5-inch drive installed as Drive B. Can I use the 720K disk
 to install from that drive?

 Response:

 The install program included with the Windows Software Development Kit
 Versions 2.00 and 2.10 are hard-coded to install from Drive A. As a
 result, to install the Windows SDK software from Drive B, the MS-DOS
 ASSIGN or SUBST command must be used to make DOS think that Drive B is
 identical to Drive A. The syntax for the ASSIGN command is as follows:

    assign a = b

 The syntax for the SUBST command is as follows:

    subst a: b:\

 The SUBST command is the preferred command.


 703. FatalExit 0x00FF, lru: count bad, prev bad, next bad

 Problem:

 We are running SDK Version 2.10 (Windows 286) and our application is
 getting the message "lru: count bad" or "lru: bad next". Then a
 FatalExit 0x00FF is generated.

 Please tell me what this is indicating. It seems that this is
 something that was done sometime in the past and is detected somewhat
 later. This is making it hard for me to track down what is causing
 this problem.

 Also, please tell me what triggers a lru count check. I would like to
 know this so that I can make them occur more often, perhaps helping
 me to narrow down the error in my code.

 Response:

 The lru errors with RIP 0x00FF are as follows:

    <lru: prev bad>

    Free global memory list corrupted by wild write, pointer from
    previous entry in list does not point to current entry.

    <lru: next bad>

    Free global memory list corrupted by wild write, pointer in next
    entry does not point back to current entry.

    <lru: count bad>

    Free global memory list corrupted by wild write, final entry in list
    does not match expected final entry.

 A lru count check is performed when any of the following occur:

    GlobalAlloc

    GlobalRealloc

    Any code loading or discarding

    Any global memory movement

 We recommend using GlobalCompact(-1), ValidateFreeSpaces(), performing
 GlobalAllocs, and using Shaker to help determine where the wild write
 is occurring.

 You may want to place GlobalCompact(-1) throughout your suspected
 code, and check the validity of your strings and other variables by
 using CVW or Symdeb. GlobalCompact(-1) forces a memory checksum.

 In your WIN.INI file, you also should have the following options set:

    EnableHeapChecking=1
    EnableSegmentCheckSum=1
    EnableFreeChecking=1

 The WIN.INI options for debugging are discussed in the "Microsoft Windows
 Programmer's Reference" manual (Version 2.x) on Page 656.


 704. Windows SDK: Limit of Logical Coordinate System in Windows

 Problem:

 The limits of the logical coordinate system seem to be -16384 to
 16383, half that indicated in the manual. If you draw a horizontal or
 vertical line from the maximum to the minimum X or Y value, the line
 is not drawn. However, if you use line values just slightly below the
 limits, the line is drawn.

 Response:

 The limits of the logical coordinate system are indeed from -32768 to
 +32767 as documented. You may draw anywhere you choose within this
 coordinate system. However, you cannot set the window extent to
 greater than 32767. This might seem to limit the system to the -16384
 to 16383 range that you suggested, but this is not the case. If the
 viewport and window are set up properly, everything will be shown. For
 example, to set up the whole logical coordinate system to be shown
 within your application's area of the screen, the following code works
 properly:

 /*  Establish Viewport to be upper left quarter of client area */

   GetClientRect(hWnd, (LPRECT) &CRect);
   SetViewportOrg(hDC, 0, 0);
   SetViewportExt(hDC, (CRect.right - CRect.left) / 2, \
                       (CRect.bottom - CRect.top) / 2);

 /*  Set Window Origin and Extent such that total logical */
 /*     coordinate system will cover entire client area   */

   SetWindowOrg(hDC, -32767, -32767);
   SetWindowExt(hDC, 32767, 32767);

 If you set your viewport to be the entire client area, your window
 origin to be (0, 0), and your window extent to be (16000, 16000), the
 points that will be drawn are those from (0, 0) to (15999, 15999),
 inclusive. This covers 16000 points in each direction.


 705. Windows SDK: Arrays of Function Pointers under Windows

 Question:

 Can I have function pointers (or arrays of function pointers) with
 statically initialized elements and have the indirect function calls
 survive any movement of the associated code segments?

 Response:

 Yes, any function EXPORTed by a LIBRARY may be IMPORTed and used in a
 static array. For example, the following is valid:

    FARPROC functions[] = { GlobalAlloc, CreateDC, LocalAlloc };

 The new style executable header contains a jump table of functions
 that are exported (called a THUNK table).

 The header is loaded into fixed memory. Dynamic linkers get the
 addresses of the EXPORT jump table. The jump table in turn points to
 the target function (perhaps movable/relocatable). Because IMPORTed
 routines are resolved to the fixed addresses of the EXPORT jump table,
 you can statically initialize arrays with IMPORTed functions. The new
 executable contains relocation records that allow the loader to find
 the array and correct it.


 706. Windows SDK: How to Determine Stack Size

 Question:

 How can I determine how big I should set my STACKSIZE to be?

 Response:

 There are a few articles that discuss how the stack is used so that
 you can determine how much stack size you should allocate. However,
 since you don't know how much space Windows libraries use, there are
 two functions you can add to your application to determine how big
 your stack has grown to.

 You can find this sample application in the Software Library by
 searching on the filename STACK.ARC, the Q number of this article, or
 S12169.


 707. DIALOG.EXE Reads Compiled .RES Files, Not .DLG Files

 Problem:

 I created a dialog box using the dialog editor. From MS-DOS, I
 changed the position and text of a control in the dialog box by
 modifying the .DLG file produced by the dialog editor. Upon invoking
 the dialog editor again, the changes I made were not evident.

 Response:

 The dialog editor produces .DLG and .RES files for the dialog box
 created. When using the dialog editor to modify an existing dialog
 box, the dialog editor will look at the .RES file for information
 about the makeup of the dialog box. The .DLG file is completely
 ignored at this point. This is why the dialog editor does not
 recognize any .DLG changes. For the modifications to be recognized,
 the RC compiler must be used with the -r switch to compile the .DLG
 file. The new .RES file can then be loaded into the dialog editor and
 the changes will be recognized.


 708. Windows SDK: Smooth Movement of a Bitmap

 Question:

 How can we implement smooth movement of a bitmap around the screen? We
 are trying to create a prototype for a simple interactive animator,
 and we need a method to move a bitmap smoothly.

 Response:

 There is a sample application called FASTBLT.ARC in the Software
 Library that demonstrates how you can do this. Basically, you need to
 set up a pair of BitBlt() calls: one that erases the image and another
 that redisplays the image. The necessary ROP codes for BitBlt() that
 you use are SRCCOPY and SRCINVERT.

 FASTBLT.ARC can be found in the Software Library by searching on the
 filename, the Q number of this article, or S12196.


 709. Windows SDK: PeekMessage() Allows Other Applications to Run

 There is a sample application called PEEKF1.ARC in the Software
 Library that demonstrates how to allow other applications to run while
 you are doing a lot of processing. To run the program, you can use
 either the menu or the F1 and F2 keys to start or stop the processing.

 PEEKF1.ARC can be found in the Software Library by searching on the
 filename, the Q number of this article, or S12197.


 710. Windows SDK: API Sound Routines Don't Work Correctly under EMS

 Windows' sound and music routines do not always work correctly when
 running under EMS. This problem applies to both Windows/386 (which
 automatically supplies EMS) and Windows/286 when run with an EMS board
 or an EMS Limulator (software for 386 machines that turns physical
 extended memory into virtual expanded memory).

 Problems encountered when running under EMS include the following:

 1. Incorrect sounds are produced.

 2. Mouse double-clicks are ignored.

 3. Flashing cursors are noticeably sped up.

 4. The system crashes.

 One way to demonstrate this problem is to run the Software Library
 sample code for playing "Mary Had a Little Lamb" (MARY.ARC). This file
 can be found in the Software Library by searching on the filename
 MARY.ARC, the Q number of this article, or S12051.

 Running Windows without EMS prevents these problems. Windows' use of
 EMS can be disabled by running the following:

 	win -n                  (For Windows/286)
 	win386 -n               (For Windows/386)

 Microsoft is researching this problem and will post new information as
 it becomes available.


 711. Windows SDK: Passing Data from a DLL to the Calling Instance

 Question:

 My DLL contains dialog routines that use global data variables to save
 processing information. Some of the processing done in the DLL can
 take 30 seconds or more to process. How can I allow a second instance
 of the application to call my DLL before the first call is complete?
 What is the best way to deal with this situation? Is there a way for a
 dialog in a DLL to know which instance of an application is calling
 it?

 Response:

 A DLL does not know which instance is calling it. You will have to
 keep track of this yourself by passing the hInstance as one of the
 parameters. Also, if you have global data that you want each instance
 to use in the DLL, you can do one of the following:

 1. Set a flag so that only one instance can use the DLL at a time.

 2. Add additional space and keep track so that each instance has its
    own amount of memory. You can put the global variables of your DLL
    in a record structure or an array to handle this.

 There is a sample application in the Software Library called
 DLLINST.ARC that shows how you can keep track so that the dialog will
 pass the information to the correct calling instance.

 This file can be found in the Software Library by searching on the
 filename, the Q number of this article, or S12195.


 712. Windows SDK: EnumTaskWindows() Callback Function Never Called

 The EnumTaskWindows() function does not work. The callback function
 never gets called.

 EnumTaskWindows() can be easily simulated using EnumWindows() by
 calling the following:

     EnumWindows(MakeProcInstance(EnumTaskWindowsProc, hInst), hInst);

 Then, use the following for your callback function:

 BOOL FAR PASCAL EnumTaskWindowsProc(HWND hWnd, LONG lParam)
     {
     /* do nothing if the window is not in the current task */
     if (GetWindowTask(hWnd) != GetCurrentTask())
         return (TRUE);
     /* your code here */
     }


 713. WDEB386 Documentation Available on Software Library

 Documentation for the WDEB386 debugger is not included in the Windows
 version 3.00 Software Development Kit (SDK). This file is available in
 the Software Library in the form of a Windows Write file, WDEB386.WRI.

 WDEB386 can be found in the Software Library by searching on the
 keyword WDEB386, the Q number of this article, or S12560. WDEB386 was
 archived using the PKware file-compression utility.


 714. Cause of Fatal Exit 0x0006

 Fatal Exit 0x0006 is caused by an invalid DC handle.

 With a debugger running (SYMDEB, CodeView), break in after the RIP and
 do a k for the traceback. This should tell you which GDI routine is
 being called and which of your routines is calling it (provided the
 proper .SYM files are loaded).


 715. PM: LB_RESETCONTENT and the WM_SETREDRAW Flag

 The thumb in the list box scroll bar does not function correctly when
 the following sequence of events occurs:

 1. WM_SETREDRAW flag is set to zero to prevent the screen from
    flashing while the list box is being updated.

 2. LB_RESETCONTENT message is sent to clear the list box and move the
    scroll thumb to the top of the scroll bar.

 3. WM_SETREDRAW flag is set to nonzero so the updated list box can be
    displayed.

 The following code fragment should clear and update the list box, then
 move the scroll thumb to the top of the scroll bar. However, the list
 box is cleared and updated, but the scroll thumb does not move. Also,
 after this code is executed, the thumb for the scroll bar does not
 react to either mouse or keyboard input.

         SendMessage(GetDlgItem(hDlg,IDLISTBOX),WM_SETREDRAW, 0, 0L);
         SendMessage(GetDlgItem(hDlg,IDLISTBOX),LB_RESETCONTENT,0,
                     (LONG)(LPSTR) 0);

         for (i = 0; i < 50; i++)  {
                 sprintf(str,"newline %d", i );
                 SendDlgItemMessage(hDlg,IDLISTBOX,
                     LB_INSERTSTRING, -1, (LONG)(LPSTR) str);  }

         SendMessage(GetDlgItem(hDlg,IDLISTBOX),WM_SETREDRAW, 1, 0L);
         InvalidateRect(GetDlgItem (hDlg, IDLISTBOX), 0L, 0);

 Microsoft has confirmed this to be a problem with Windows Version
 2.10. We are researching this problem and will post new information as
 it becomes available. In the meantime, you can work around the problem
 by including the following statement after the for loop:

              SetScrollPos(hCtl,SB_VERT,0,TRUE);

 This will reset the scroll bar's thumb position to zero. Mouse and
 keyboard input will then be processed properly.


 716. Windows SDK: DLLs Must EXPORT Functions by Ordinal

 To avoid a Windows Version 2.10 bug, all DLLs must EXPORT their
 functions by ordinal rather than by name. To EXPORT by ordinal, a DLL
 function list in the EXPORTs section of the DEF file must be followed
 by "@<some number>". This has always been the recommended way to
 EXPORT because it is faster and consumes less memory, but it is now a
 requirement due to a Windows 2.10 kernel bug.

 If you have tight memory conditions, you may experience problems
 calling DLL functions that are located in a nonresident segment if the
 DLL EXPORTed by name rather than by ordinal.

 Microsoft has confirmed this to be a problem with Version 2.10 of
 Windows. We are researching this problem and will post new information
 as it becomes available. This problem only occurs if a DLL EXPORTs by
 name rather than by ordinal. This means that all DLLs must EXPORT by
 ordinal.


 717. Use of TRANSPARENT and OPAQUE Background Modes Affect Lines

 When using a dotted or dashed line, the coloring of the spaces between
 the dots or dashes depends on the background color and background
 mode. The default background mode is OPAQUE, and the default
 background color is white.

 To prevent the gaps from being filled in, use SetBkMode() to change
 the background mode to TRANSPARENT. You can also use SetBkColor() to
 change the background color. When the background mode is TRANSPARENT,
 Windows ignores the background color, and does not fill in the gaps.


 718. Windows SDK: FatalExit 0x0280 Caused by FAR WinMain()

 If a FatalExit 0x0280 (ERR_GMEMHANDLE, invalid global handle) occurs
 in a Microsoft Windows application upon entrance to WinMain(),
 be sure that the keyword "FAR" is not in the WinMain() function
 declaration, as in the following example:

    int FAR PASCAL WinMain (hInst, hPrevInst, lpszCmd, nCmdShow)

 The Microsoft Windows kernel expects a near pointer instead of a far
 pointer for the program's entry point. Declaring WinMain() as FAR will
 cause the above fatal exit.


 719. Windows SDK: Creating a Multiple-Line Message Box

 To display more than one line in a message box, use a newline
 character in the text string where a line break is required. The
 following string declaration is an example of this:

    LPSTR lpText[] = "This is an example\n of a line break.";

 The following code fragment is an example of a multiple-line message
 box and is located in Section 11.9.7, Pages 197-198, of the "Microsoft
 Windows Software Development Kit Programmer's Learning Guide" manual:

    sprintf(str,
            "Not enough memory to load %s.\n%s exceeds %ld bytes.",
            OpenName, OpenName, MAXFILESIZE);
    MessageBox(hDlg, str, NULL,
               MB_OK | MB_ICONQUESTION);


 720. FatalExit 0x0504 Produced from Incorrect lpTableName

 FatalExit code 0x0504 can occur if the incorrect name of an
 accelerator table is given in the LoadAccelerators() function. The
 lpTableName parameter must match an accelerator table name in the
 application's resources.


 721. Child Windows and the MINIMIZE Flag

 According to Page 104 in Chapter 13 of the "Microsoft Windows Software
 Development Kit Application Style Guide," Windows will minimize child
 windows if the application creates the proper icons. This is a
 documentation error.

 The ability of the child window to become minimized was never designed
 as part of the product, nor will it be supported.


 722. CS_BYTEALIGNCLIENT Doesn't Work When Window Moved/First Shown

 In Windows 2.x, the CS_BYTEALIGNCLIENT class style does not properly
 byte align the client areas of windows when they are moved with
 MoveWindow() or SetWindowPos(), or when they are first shown. This
 style does cause windows to be correctly aligned when they are moved
 manually, or when the mouse is clicked in a nonclient area of a window
 with this style.

 The workaround is to get the width of the vertical border of the
 window and take this width into account when creating or moving the
 window. For instance, the following code will ensure that the client
 area of a window with a size border would be properly aligned on
 vertical screen coordinate 8; any x coordinate evenly divisible by 8
 is on a byte boundary:

 nBorderWidth = GetSystemMetrics(SM_CXFRAME);
 MoveWindow(hWnd,8-nBorderWidth,20,200,200,FALSE);


 723. WM_SETFOCUS: wParam Not Always Window Losing Focus

 The wParam of the WM_SETFOCUS message is documented to be the handle
 to the window that is losing the focus. The wParam does indeed contain
 this handle if the focus is changing because of a call to SetFocus().
 It does not contain this handle, however, if the focus is changing due
 to mouse or keyboard input. The low-order word of lParam of the
 WM_ACTIVATE message (when the wParam equals 1 or 2) DOES contain the
 handle to the window that is losing the focus. If, when you get the
 focus, you care which window previously had the focus, check the low
 order word of the WM_ACTIVATE message to determine which window
 previously had the focus.

 The same problem exists for the WM_KILLFOCUS message. The wParam of
 this message does not always contain the handle to the window that
 will receive the input focus. The low-order word of lParam of the
 WM_ACTIVATE message (when the wParam equals 0) DOES contain the handle
 to the window that will gain activation. Check this parameter to
 determine which window will be activated.


 724. LineTo() into Memory DC with Dotted Pen Causes Hang

 The machine will hang during certain LineTo() calls into memory DCs
 using a dotted pen and transparent background mode. Microsoft has
 confirmed this to be a problem in Windows Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.

 To work around this problem, use PolyLine() with two points to
 accomplish the same task. The problem occurs very infrequently; it
 only occurs with certain current positions in combination with certain
 LineTo() coordinates.


 725. SAE Apps Must Use "rc -LIM32" Whether They Use LIM 3.2 or Not

 In an SAE environment, the SAE application must bind the resources to
 the executable with "rc -LIM32 appname". The "-LIM32" switch sets a
 byte in the EXE header that tells the Windows kernel that the
 application uses LIM Version 3.2 memory. The Windows kernel makes
 assumptions that the first application will be marked as using LIM
 Version 3.2 memory. If this switch is not used, the SAE environment
 will be unstable and fatal exits will occur in the debugging version.
 It is not necessary to use this switch for all applications that run
 under an SAE package. This must only be done with "app 0" (the
 application that replaces the MS-DOS Executive).


 726. Escape GETPHYSPAGESIZE Doesn't Return Output Dimensions

 The printer escape GETPHYSPAGESIZE should not be used to determine the
 size of the output area. This escape returns the size of the physical
 page in device units. This size is usually larger than the output area
 of a page because there is usually a border around the page that the
 printer cannot write to. More meaningful values can be obtained by
 calling GetDeviceCaps() with indexes HORZRES and VERTRES. These calls
 will return the width and height of the output area in device units.


 727. Fatal Exit 0xff: "Free Memory Overwrite" Due to Kernel Bug

 There is a harmless bug in the Windows Version 2.10 kernel that can
 cause a fatal exit 0xff: "free memory overwrite" to occur when closing
 one instance of an application that has two or more instances running.
 This fatal exit can only occur when running with LIM Version 4.0. This
 free memory overwrite cannot cause any problems because the write will
 always occur E2 bytes into an area in which a TDB (task database) was
 just freed. This area will always be a free space, so no segments will
 be overwritten.

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.

 To avoid this problem while developing applications on machines with
 LIM Version 4.0, do not close instances of applications when two or
 more instances of that application are running. If this cannot be
 avoided, turn off free memory checking. This can be done by setting
 "enablefreechecking=0" in the [kernel] section of the WIN.INI file.
 For more information on this debugging switch and others, please refer
 to Page 656 of the "Microsoft Windows Software Development Kit
 Programmer's Reference."


 728. Text and Background Colors Affect BitBlt() from Mono to Color

 When doing a BitBlt() from a monochrome bitmap to a color bitmap, GDI
 transforms all white bits (1s) to the background color of the
 destination DC. GDI transforms all black bits (0s) to the text (or
 foreground) color of the destination DC.

 When doing a BitBlt() from a color bitmap to a monochrome bitmap, GDI
 sets to white (1) pixels that match the foreground color of the source
 DC. All other bits are set to black (0).

 These features are mentioned in the BitBlt() documentation on Page 158
 of the "Microsoft Windows Software Development Kit Programmer's
 Reference."


 729. Petzold's Undocumented Long String Functions

 Question:

 Charles Petzold's book, "Programming Windows," mentions some
 undocumented long string function calls. Are there any others?

 Response:

 Yes, those "undocumented" functions have always been provided with
 Windows; however, they were never documented. They will be documented
 in a future release of the manual.

 These functions have always been provided in the sample Cardfile
 sources that we give out in a file called WINEXP.H along with the long
 file I/O routines. The following is a list of the functions that the
 file contains, along with their C run-time equivalents:

    lstrcmp     = strcmp
    lstrcpy     = strcpy
    lstrcat     = strcat
    lstrlen     = strlen
    lstrbscan   =  see Cardfile source code for this
    lstrbskip   =  see Cardfile source code for this

 Additional long string functions are provided in the "Microsoft
 Windows Software Development Kit Programmer's Learning Guide," along
 with their source codes. The following are the files on the Windows
 SDK Version 2.10 sample code disk that contain some additional long
 string functions:

    LEARNING\CLIPTEXT\CLIPTEXT.H
    LEARNING\EDITFILE\EDITFILE.H
    LEARNING\FILEOPEN\FILEOPEN.C
    LEARNING\FILEOPEN\FILEOPEN.H
    LEARNING\PRNTFILE\PRNTFILE.C
    LEARNING\PRNTFILE\PRNTFILE.H
    LEARNING\SHOWFONT\SHOWFONT.C
    LEARNING\SHOWFONT\SHOWFONT.H

    _lstrncpy   Copies a specified number of characters from a
                string to a buffer.

 We have provided the source code for these functions so that you can
 modify them for your particular needs.

 In Chapter 10, "Controls and Dialog Boxes," Section 10.11.6, "Add
 Helper Functions," of the "Microsoft Windows Software Development Kit
 Programmer's Learning Guide," there is a table on Page 181 that lists
 some additional functions that you may find useful.

 Warning: Using the near version of these calls on FAR data can cause
 wild writes, fatal exits, inconsistent behavior and crashes to occur.
 Use the long versions of these routines.


 730. DSPY and DDESPY: Spying on DDE Messages

 Problem:

 I'm trying to use the DSPY program to spy on the DDE messages going
 through the system. However, it doesn't work with EMS.

 Response:

 There is now an enhanced version of DSPY that will work with EMS, LIM
 Version 4.0. We had to make the part of the program that did the
 spying to be in a fixed DLL so that it was not banked out. The program
 now contains two modules, DDESPY and DSPYDLL.

 This file can be found in the Software Library by searching on the
 filename DDESPY.ARC, the Q number of this article, or S12210.


 731. Bringing Up Printer Dialog Box from Your Application

 The printer dialog box can be brought up from within an application by
 loading the printer driver, getting the procedure address of the
 "DEVICEMODE" procedure exported by the printer driver, and calling
 that procedure. This works because all printer drivers must export a
 function named "DEVICEMODE", which brings up its dialog box.

 In the Software Library there is an example of an application that
 brings up the currently selected printer's dialog box. This file can
 be found in the Software Library by searching on the filename
 DEVMODE.ARC, the Q number of this article, or S12211.


 732. Windows SDK: Minimum Records for a BIFF File

 Question:

 What is the minimum number of records necessary for sending BIFF
 using DDE or a file?

 Response:

 A BIFF DDE object requires at least the following four records:

 1. rtBof
 2. rtDimensions
 3. [1 or more of:] rtBlank, rtInteger, rtNumber, rtLabel, rtBoolErr
 4. rtEof

 If rtFormula records are supplied, the value is extracted from them
 and used as well. These may be followed by rtString records for
 formulas with string results.


 733. Windows SDK: Classes Registered in DLLs Are Not Cleaned Up

 When the last instance of an application closes, Windows looks at all
 classes registered in the system to see if they belong to that
 application. Windows does not do the same when a DLL closes. This
 means that a DLL that registers a class must go through a little extra
 work to ensure that the class is destroyed when the DLL is closed.

 The key to the destroying of a window class is the handle to the
 module that owns the class. For applications, all instances of a given
 application share the same module handle. Because DLLs have only one
 instance, there is a direct correspondence between an instance handle
 and a module handle. If the module handle in a class is a DLL's module
 handle, the class will never be destroyed. This means that to cause a
 class registration to be destroyed, a DLL that registers a class must
 set the handle to the module in that class to the handle to a module
 of an application that is closing. The best time to do this is when
 the last application that links to the DLL closes.

 Here is one method for determining when the last application that
 links to a DLL closes. First, register the class upon DLL
 initialization. (There are a few articles in the KnowledgeBase that
 describe DLL initialization. If you are not familiar with DLL
 initialization, please look for these articles by searching on the
 keywords "dll" and "init".) Next, whenever an application that links
 to the DLL is started, it should make a call into a function in the
 DLL. When this function is called, the DLL should increment a count
 that represents the number of applications (or application instances)
 linking to it.

 Additionally, the applications should make a call into the DLL when
 the applications close. At this time, the DLL should decrement the
 reference count. When this reference count decrements to zero, the DLL
 should set the handle to the module in the class, to the handle to the
 module of the application that just called the exit procedure. This
 means that the application must pass the handle to its module to the
 exit procedure. The application can get the handle to its module by
 calling GetModuleHandle(MAKEINTRESOURCE(hInstance)). The following is
 the code to the functions in the DLL that implement this scheme:

 /* DLL code */

 int nReferenceCount=0; /* number of apps currently referencing us */

 void FAR PASCAL AppEntry(void)
 {
 nReferenceCount +=1;
 }

 void FAR PASCAL AppExit(hModule)
 HANDLE hModule;       /* this is the handle to the app's module */
 {
 nReferenceCount -=1;
 if (nReferenceCount == 0)
     SetClassWord(hWnd,GCW_HDMODULE,hModule);

 /*
    "hWnd" must be an existing window of that class; this window must
     be destroyed before the application closes; may have to create
     a nonvisible window of that class for the SetClassWord() call
 */

 }


 734. HIMEM.SYS and Windows/386

 You can place the HIMEM.SYS released with the Windows Version 3.00
 pre-release in your CONFIG.SYS and have it work correctly with both
 Windows/386 Version 2.10 and Windows/386 Pre-Release. You don't have
 to have two different CONFIG.SYS files and you don't have to reboot
 your machine to use both versions of Windows.


 735. Debugging an Application

 Question:

 Can I use SYMDEB or CVW to debug my application? If not, what is the
 easiest way to debug my application when I have a problem?

 Response:

 You cannot use either SYMDEB or CVW with this pre-release of Windows.
 You can only use WDEB386.

 An easier way to implement a trace in your program is to use fprintf
 to the auxiliary port, as follows:

      fprintf (stdaux, "i = %d\n\r", i );

 This will send the formatted string to your AUX port.


 736. Windows SDK: Getting the Current Working Directory in Windows

 The getcwd() C run-time function is not safe to use in Windows because
 it calls malloc. The current working directory can be obtained safely
 in Windows by using the OpenFile() function. The following sample
 application demonstrates how this is done:

 /***********************************************************/
 /*  getcwd.c  */

 #include "windows.h"

 int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
 HANDLE hInstance, hPrevInstance;
 LPSTR lpszCmdLine;
 int cmdShow;
 {
     OFSTRUCT MyOFStruct;

     OpenFile("",(LPOFSTRUCT) &MyOFStruct,OF_PARSE);
     MessageBox(GetFocus(),MyOFStruct.szPathName,"The Path",MB_OK);

 }

 /***********************************************************/
 /*  getcwd.def  */

 NAME    getcwd

 DESCRIPTION 'Simple Microsoft Windows Application'

 STUB    'WINSTUB.EXE'

 CODE    MOVEABLE
 DATA    MOVEABLE

 STACKSIZE   128
 HEAPSIZE    128

 /***********************************************************/
 /*  getcwd.mak  */

 getcwd.obj: getcwd.c
     cl -d -c -AS -Gsw -Od -Zdi getcwd.c

 getcwd.exe: getcwd.obj getcwd.def
     link4 /noe /CO getcwd,/align:16,/map,slibw,getcwd.def
     mapsym getcwd


 737. Windows SDK: Fatal Exit 0x00FF When Calling Arc() or Ellipse()

 The Arc() and Ellipse() function calls may generate fatal exit 0x00FF
 when the bounding rectangle is large, but well within the 32000 unit
 limit for the width or height that is documented.

 Microsoft has confirmed this to be a problem with Version 2.10 of the
 Windows Software Development Kit (SDK). We are researching this
 problem and will post new information as it becomes available.

 The following is a short code fragment that demonstrates the problem;
 although this sample calls Arc(), the same problem occurs when calling
 Ellipse():

         case WM_PAINT:
             hDC = BeginPaint (hWnd, &ps);

             SetMapMode (hDC, MM_TEXT);

             x1 =  0; y1 =  0;
             x2 = x1; y2 = y1;

             while (x2 < 10000) {
                 length = sprintf (szBuffer, "(%d, %d)", x2, y2);
                 TextOut (hDC, 10, 10, szBuffer, length);
                 Ellipse (hDC, x1, y1, x2, y2);
                 x2 += 100;
                 y2 += 100;
             }

             EndPaint (hWnd, &ps);
             break;

 This program displays an ellipse initially bounded by the rectangle
 (0,0), (0,0). The ellipse increases in size by the bounding rectangle
 (0,x2) (0,y2), where x2 and y2 increase by 100 at each loop iteration.
 This works correctly until x2 and y2 reach 8300. At this point, a
 fatal exit 0x00FF is issued.


 738. Windows SDK: Return Value from GetMenuState()

 There is a documentation error on Page 283 in the "Microsoft Windows
 Software Development Kit Programmer's Reference" for Versions 2.x,
 regarding the return value from GetMenuState(). The documentation
 states that the return value is NULL if the menu or specified item
 does not exist. This is incorrect. A NULL return value is a valid
 menu item state.

 If the "hMenu" is valid but the "wId" is invalid, a -1 is returned
 signifying an error. If the "hMenu" is invalid, a fatal exit will
 occur.


 739. DUMPDESC.ARC: Windows Program to Read an EXE Description Line

 There is a file in the Software Library named DUMPDESC.ARC that is a
 small-model Windows application showing how to read the description
 line out of an EXE file. This program was compiled using the Windows
 Version 2.10 Software Development Kit (SDK). The program reads its own
 description each time it paints. The relevant portions of the code are
 in the routine DumpDescPaint().

 Since the Windows and OS/2 EXE header formats are the same, this
 method also works in OS/2 applications.

 DUMPDESC.ARC can be found in the Software Library by searching on the
 filename, the Q number of this article, or S12229. DUMPDESC.ARC was
 archived using the PKware file-compression utility.


 740. Why GlobalLock() Fails on 0-Byte Blocks of Memory

 Question:

 If you GlobalAlloc() a segment of size 0 (zero), Windows will return a
 handle to the memory. If you then attempt to GlobalLock() using that
 handle, Windows returns a NULL pointer. This makes sense from the
 point of view that you can't write to an empty segment, but in this
 case wouldn't it make sense to return NULL to the GlobalAlloc() that
 requests a zero-size segment? Otherwise, of what use is it to be able
 to GlobalAlloc() an empty segment?

 Response:

 A 0-byte block of memory is treated by the system as if it is
 discarded. Consider the following scenario:

 1. hMemory = GlobalAlloc (GMEM_MOVEABLE, 0L);

    This gives you a handle to a block of memory of length 0 (zero).

 2. wFlags = GlobalFlags (hMemory);

    wFlags is 0x4000, which is GMEM_DISCARDED. Thus, you must think of
    the memory object as if it has already been discarded.

 3. lpszMemory = GlobalLock (hMemory);

    This fails. lpszMemory == 0.

 4. hMemory = GlobalReAlloc (hMemory, 1L, GMEM_MOVEABLE);

    This gives back your handle, but sets the size of the block to 1
    byte.

 5. wFlags = GlobalFlags (hMemory);

    wFlags = 0 (zero), which is GMEM_FIXED (but in version 3.00
    protected mode, GMEM_FIXED and GMEM_MOVEABLE are the same).

 6. lpszMemory = GlobalLock (hMemory);

    This gives you a valid far pointer to a 1-byte buffer.

 This shows how memory handles can be reused after having been
 discarded. When the lock count of a GMEM_DISCARDABLE object goes to 0
 (zero), it is a candidate for discarding. If it has been discarded,
 you cannot lock it and use it because the memory it refers to has been
 deallocated. But you can reallocate it and then lock it. In the above
 case, we created the block of 0 (zero) size from the beginning, but
 Windows noticed this and set its flags to GMEM_DISCARDED so it (and
 you) would know how to treat it.


 741. Video Seven FastWrite and VRAM Video Modes

 **********************************************************************
   CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL
   CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL
 **********************************************************************

 Below is a list of the Video Seven Fastwrite and VRAM modes, as
 documented by Video Seven. Note that the frequencies are independent
 of the number of colors; therefore, the 640 x 480 x 256 color mode
 should run on a normal VGA monitor (you should check with Video Seven
 to be sure). Also note that the high-resolution modes won't run on a
 MultiSync and that some of the very high-resolution modes won't run on
 all MultiSyncs (for example, the MultiSync II has a horizontal sync
 range of 15.5 to 35 KHz and vertical sync of 50 to 80 Hz, so you can't
 run the 1024 x 768 modes)

 The different frequency specifications on the VRAM 800 x 600 x 16 and
 800 x 600 x 256 modes is taken Video Seven's documentation.

 The modes supported by each card are as follows:

 Video Seven FastWrite VGA
 -------------------------

     with 256K DRAM
                                         Monitor Timing
         Resolution      Colors      Vertical    Horizontal
         ----------      ------      --------    ----------
         800x600         16          56 Hz       35.0 KHz
         720x540         16          60          35.0
         752x410         16          70          31.5
         640x480         16          60          31.5

         600x400         256         70          31.5
         320x200         256         70          31.5

     with 512K DRAM option
                                         Monitor Timing
         Resolution      Colors      Vertical    Horizontal
         ----------      ------      --------    ----------
         640x480         256         60          31.5

 Video Seven V-RAM VGA
 ---------------------

     with 256K DRAM
                                         Monitor Timing
         Resolution      Colors      Vertical    Horizontal
         ----------      ------      --------    ----------
         800x600         16          56 Hz       35.0 KHz
         720x540         16          60          35.0
         752x410         16          70          31.5
         640x480         16          60          31.5

         600x400         256         70          31.5
         320x200         256         70          31.5

         600x400         256         70          31.5
         320x200         256         70          31.5

         1024x768        2           60          48.5
         1024x768        4           60          48.5

     with 512K DRAM option
                                         Monitor Timing
         Resolution      Colors      Vertical    Horizontal
         ----------      ------      --------    ----------
         1024x768        16          60          48.5
         800x600         256         51.5        32.5
         720x540         256         60          35.0
         640x480         256         60          31.5

 Notes
 -----

 1. To use Windows 3.00's Palette Manager in all its 256-color
    splendor, order cards with the 512K option. Video Seven lists
    second sources for DRAM and VRAM chips in their manuals:

                   FASTWRITE               VRAM
                   (100 ns DIP 64K x 4)    (150 ns ZIP 64K x 64)
                   --------------------    ---------------------

       NEC         UPD41464C10             UPD41264V-12
       Mitsubishi  M5M4464P-10             M5M4C264L-12
       Fujitsu     MB81464-10              MB81461-12-PSZ
       Panasonic                           MN47464L-120ns

 2. Both cards provide hardware cursor support.

 3. We don't know yet what card and which modes the Video Seven Windows
    3.00 display driver is being written for. Once the display driver
    is built, it should be easy for Video Seven to port it to all
    256-color modes on both cards.

 4. The V-RAM cards are faster than the FastWrite cards, but they cost
    more.

 5. A VRAM is essentially a dual-ported DRAM chip that reduces the
    conflict between processor access to the display memory and the
    need to continually dump the contents of the display memory to the
    CRT.


 742. Indirectly Calling LocalAlloc()

 Question:

 Some Windows API routines call LocalAlloc(). Each of these calls has
 the potential of moving the Data Segment (DS) because LocalAlloc()
 decrements the lock count on DS. This, in turn, will invalidate any
 FAR pointers to data items in DGROUP.

 Which Windows API routines directly or indirectly call LocalAlloc()?

 Response:

 We cannot document which functions call LocalAlloc() because the
 functions may change in a future release of Windows.

 Instead, you should use LockData() to guarantee the validity of your
 FAR pointers to DGROUP, as shown in the following code fragment:

    LockData(0);
    /* generate and use FAR pointers to DGROUP here */
    UnlockData (0);
    /* don't use those DGROUP pointers any more! */


 743. How About Box Calculates Free System Resources

 Question:

 How is the Free System Resources figure in the Program Manager's About
 dialog calculated?

 Response:

 The calculation is the same for all modes (although it is not done for
 real mode). The following formula describes how it is calculated in
 Windows version 3.00; this may or may not be accurate in future
 versions of the product:

 % free = min (% free of USER's DS, % free of GDI's DS)

                        (64K - current size of DS) + free blocks in Heap
 % free USER/GDI heap =  ------------------------------------------------ * 10
                                64K - (size of statics and stack)

 Or, in other words:

                         Current free heap assuming 64K
 % free USER/GDI heap =  --------------------------------   * 100
                         Maximum heap assuming 64K

 There is no external API defined to calculate these values.


 744. File Handles and Virtual Machines in Windows/386

 Question:

 I have defined FILES=30 in my CONFIG.SYS file. Will these 30 file
 handles be available in each Windows/386 virtual machine, or are the
 same 30 file handles used by all virtual machines?

 Response:

 When using Microsoft Windows/386 version 2.11, the total number of
 files opened by the virtual machines -- including the Windows virtual
 machine -- cannot exceed the number of file handles defined in your
 CONFIG.SYS file, minus the five file handles used by the system.

 In other words, if you have FILES=30 in your CONFIG.SYS file, you have
 25 file handles available for use by Windows and any non-Windows
 applications running in other virtual machines.

 Note that within the Windows virtual machine, each Windows application
 gets its own PSP. This means that there is no competition between
 Windows applications for file handles. Thus, if the Windows virtual
 machine has 20 file handles available to it, each application can open
 15 files (remember that DOS reserves five predefined handles.)


 745. Communications Problems Under Windows

 Below is a discussion of some general problems with serial
 communications under Windows versions 2.x. The following issues are
 covered:

 1. Windows/286 versus Windows/386 and interrupt latency

 2. Problems in the updated 2.11 COMM.DRV driver

 3. The use of SMARTDRV, RAMDrive, and other TSRs that use extended
    memory

 4. Future plans for communications under Windows

 Windows/286
 -----------

 Communications problems under Windows/286 are relatively few because
 Windows is running in real mode. Comm port interrupts are handled
 immediately by the Comm driver. If interrupts are disabled for too
 long, Comm interrupts will be lost. However, this is very rare under
 Windows/286.

 2.10 and 2.11 Bug Fixes to Comm Driver
 --------------------------------------

 With Windows 2.10, an updated Comm driver was provided that was
 supposed to correct problems related to a bug in the 8250b UART that
 is used in the PS/2 50z. This driver corrected some problems, but
 broke Xon/Xoff flow control for many configurations. The Windows 2.11
 Comm driver was supposed to correct this, but still had some problems,
 especially at high baud rates (9600). For more information, query on
 the following word in the OnLine Knowledge Base:

    COMM211X

 This information contains a reference to an updated driver in the
 Software/Data Library, and instructions for installing the driver.

 Using Extended Memory
 ---------------------

 There is an additional problem that may cause CE_OVERRUN errors and
 data loss when using SMARTDrive, RAMDrive, or other drivers; TSRs; or
 applications that access EXTENDED memory; and you are running on an
 80286 computer. These programs must switch into and out of protected
 mode to move data back and forth between conventional and extended
 memory. These transitions are relatively slow.

 Also, during the transition, interrupts are disabled. Therefore, a
 Comm interrupt may get lost during one of these transitions. This will
 be more of a problem on some machines than others. The speed of this
 transition varies on the speed of the processor, speed of the
 installed memory, etc. If you are using an 80386 computer, this
 shouldn't be a problem, since the 80386 is designed to make this
 transition easier and faster.

 Windows/386 and Interrupt Latency
 ---------------------------------

 Windows/386 has some additional problems beyond those found in
 Windows/286. Applications that perform serial communications
 activities under Windows/386 may encounter CE_OVERRUN errors and data
 loss. The problem is most severe at high baud rates, such as 9600
 baud. The problem will be less severe at lower baud rates, but may
 still occur. It will occur more often if multiple virtual machines
 (VMs) are running (that is, you are running standard DOS applications
 in addition to Windows).

 The source of the problem is the Windows/386 virtual machine
 architecture as it relates to interrupt latency. There is a certain
 amount of overhead associated with the virtualization of interrupts
 and device ports. However, the largest problem in this area is
 interrupt latency caused by transitions between VMs.

 Windows/386 performs preemptive multitasking between VMs; several
 times a second, Windows performs a "task switch" from one VM to
 another. If you are running COMMAND.COM or a DOS application under
 Windows/386, you have two VMs. If the Comm port interrupt occurs while
 the DOS VM is active, the interrupt can't be processed because the
 Comm driver can't be called until the Windows VM becomes active again.
 Normally, this will be handled quickly enough, but there are times
 when it isn't.

 The following are factors that will effect interrupt latency and task
 switches:

 1. Windows can't perform a task switch while the INDOS flag is set
    (meaning DOS is in a critical section). The INDOS flag is set when
    calls are made to DOS; that is, when int21 and other DOS interrupts
    are called. Therefore, task switches can't occur during file I/O,
    directory manipulation, screen I/O, getting or setting the system
    time, etc. Just running COMMAND.COM in a window will cause calls to
    DOS for blinking the cursor.

    Heavy file I/O in a DOS application may cause the INDOS flag to be
    set for a relatively long period of time. Floppy file I/O will have
    the greatest impact. If the INDOS flag is set for too long, a task
    switch won't occur soon enough for the Comm interrupt to be
    processed correctly.

 2. Interrupts won't get processed if interrupts are disabled. There
    are various times in Windows and in DOS when interrupts are
    disabled. Also, Windows/386 provides expanded memory (EMS)
    emulation for banking Windows applications into and out of
    conventional memory. Interrupts are disabled during the EMS task
    switch. These times are generally very short, but when they occur
    in conjunction with task switch latency, they can combine to cause
    the problem.

 3. Higher priority interrupts get processed before lower priority
    interrupts. This often isn't a problem; however, it has been seen
    more often when using a serial mouse. The mouse has a higher
    interrupt priority than the Comm port. If the mouse is using one of
    the Comm ports, communication with the mouse is relatively slow.
    Thus, if you are moving the mouse a lot, the mouse processing is
    taking a high priority and taking a relatively long time to
    complete. Therefore, the Comm interrupt may be missed.

 In all of these cases, a faster machine will perform better, since the
 time spent with the INDOS flag set, or with interrupts disabled, is
 less, and more time will be available to process the Comm interrupts.

 There is no easy, foolproof workaround. Windows/286 will generally
 perform better than Windows/386 for communications. Both will perform
 better at lower baud rates (that is, 300-2400 baud). As in all serial
 communications, the only way to guarantee that data is not lost is to
 use a packet/protocol transmission scheme. Using this type of
 protocol, you can detect errors in packets and, more importantly,
 request retransmission of the packet.

 Future Plans
 ------------

 Microsoft is working on enhancing the Windows/386 virtual machine
 architecture for future versions of Windows in order to minimize the
 possibility, or impact, of interrupt latency. We intend to improve
 Windows so that it can be used for all types of serial communications.


 746. DPMI Specification Available from Intel

 The DOS Protected Mode Interface (DPMI) Specification is available
 free of charge by calling Intel Corporation at the following phone
 number:

    (800) 548-4725

 International customers can obtain the DMPI Specification by calling
 the Intel sales office for their country.


 The DPMI Specification was defined to allow DOS programs to access the
 extended memory of PC architecture computers while maintaining system
 protection. DPMI defines a specific subset of DOS and BIOS calls that
 can be made by protected-mode DOS programs. It also defines a new
 interface via software interrupt 31h that protected-mode programs use
 to allocate memory, modify selectors, call real-mode software, etc.

 DPMI will commonly be used for communicating with TSRs and DOS device
 drivers from protected-mode applications. If a buffer of data is being
 passed to the protected-mode software, you must allocate the buffer
 below 1 MB so that it is accessible to the real-mode software. You
 also must translate the buffer's address from a selector address to a
 segment address. If the real-mode software is to call back to a
 function in the application, you must allocate a real-mode callback
 address. DPMI provides services to perform all of these tasks.

 The Windows standard mode DOS extender and Windows enhanced mode
 provide translation services for most commonly used interrupts, so the
 driver or application can call DOS, BIOS, and other common services
 without using the DPMI services. When communicating with networks,
 TSRs, or other real-mode software, for which Windows doesn't provide
 automatic translation, you must call the DPMI services.

 We recommend that the DPMI services only be used in Windows device
 drivers or DLLs. For manipulating selectors, applications should use
 the KERNEL selector functions, as documented in the SDK. If an
 application needs to use the DPMI services, we recommend that those
 portions of the application be placed in a DLL. This is because
 calling the DPMI services from application isn't guaranteed to work in
 the future. It is guaranteed for drivers and DLLs.


 747. Polyline() Omits Line Segments When GDI's Heap Is Large

 When GDI's heap grows past 32K, Polyline() will frequently omit line
 segments that are moving from right to left.

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.


 748. Windows SDK: OpenFile() Not Returning APPEND Subdirectory

 Question:

 If I use the OpenFile() function to get a file that was in a directory
 that I used with the MS-DOS APPEND function, the file is found;
 however, the szPathName field is not filled in correctly.

 Response:

 Microsoft is researching this problem and will post new information
 as it becomes available.

 You will still have to verify the path and APPEND environment on your
 own to be able to work with existing versions of Windows.

 There is also a sample code example called ENVR.ARC that shows how to
 get all of the environment variables. This file can be found in the
 Software Library by searching on the filename, the Q number of this
 article, or S12228.


 749. Windows SDK: Linker Switch /F Can Reduce Size & Increase Speed

 The linker switch /FARCALLTRANSLATION (abbreviated /F) can reduce the
 size of an application and increase its execution speed.

 When the Windows loader loads an application, it creates a thunk (a
 relocation code fragment) for all far calls. When these calls are to
 the same segment as the one the call is in, the thunks are
 unnecessary. When the /F switch is used with the linker, far calls to
 the same segment are replaced by NOP, PUSH CS, and CALL NEAR. These
 near calls then don't require a thunk, which saves space. The speed of
 execution improves because the call no longer needs to be done using
 the thunk code.

 If the routine is only called from within the same segment, additional
 space and time can be saved by declaring the routine near. This avoids
 the NOP and PUSH CS instructions.

 If you are considering using the /F linker switch, please read the
 caveats on Page 276 in Section 12.2.19 of the "Microsoft CodeView And
 Utilities Software Development Tools for the MS-DOS Operating System"
 manual for C Versions 5.00 and 5.10.

 Windows programs should NOT use the /PAC linker option since it
 conflicts with the SEGMENTS definitions given in the DEF file.

 Keywords: /PAC /PACKCODE /NOP /NOPACKCODE /F /FARCALLTRANSLATION /NOF
 /NOFARCALLTRANSLATION


 750. Windows SDK: SetMapperFlags(hDC, ASPECT_FILTERING) Operation

 The purpose of SetMapperFlags() with ASPECT_FILTERING is to restrict
 the set of fonts available on a device to those that most closely
 match the aspect ratio of the device.

 Aspect ratio filtering is done in two stages, as follows:

 1. When the application calls SetMapperFlags(), the first stage scans
    the currently loaded fonts for the closest matching aspect ratio.

 2. When the logical font is mapped to a physical font, the second
    stage eliminates from consideration fonts that don't exactly match
    the ratio found in the first stage.

 The first stage scans the currently loaded fonts, considering those
 that are raster fonts, not the system or terminal font, and not
 device-specific fonts. For each font, the first stage compares the
 device aspect ratio as defined by LOGPIXELSX and LOGPIXELSY from
 GetDeviceCaps() with the font aspect ratio, as defined by dfVertRes
 and dfHorizRes in the physical font. (See the discussion below for
 descriptions of dfVertRes and dfHorizRes.) If the difference between
 the aspect ratios is smaller than the current minimum difference, the
 first stage saves the aspect ratio of the current font. After scanning
 all fonts, it saves the final aspect ratio with the minimum difference
 in the DC.

 The second stage filters out fonts that are to be considered for
 mapping. It discards all raster fonts other than the system font that
 don't exactly match the aspect ratio saved in the DC by the first
 stage in SetMapperFlags().

 Notes

 To disable font aspect ratio filtering, call SetMapperFlags() with the
 ASPECT_FILTERING bit clear.

 The current filtering values are returned as a long by
 GetAspectRatioFilter(), with the Y value in the low word and the X
 value in the high word of the result. These values can be compared
 against the tmDigitizedAspectX and tmDigitizedAspectY fields of the
 TEXTMETRIC structure, or the dfVertRes and dfHorizRes values in the
 physical font.

 The dfVertRes and dfHorizRes values in the physical font are
 equivalent to the tmDigitizedAspectX and tmDigitizedAspectY fields of
 the TEXTMETRIC structure, where

    tmDigitizedAspectX == dfVertRes
    tmDigitizedAspectY == dfHorizRes

 The coordinates are intentionally reversed because one pair measures
 resolution and the other measures aspect ratio.

 SetMapperFlags() checks against the current set of fonts as determined
 by AddFontResource() and the [fonts] section of WIN.INI. If fonts are
 subsequently added or removed by AddFontResource() or
 RemoveFontResource(), SetMapperFlags() must be called again. Please
 refer to the printed documentation on WM_FONTCHANGE.

 SetMapperFlags() marks the font as dirty so that the next reference to
 the logical font (using TextOut(), GetTextMetrics(), etc.) will cause
 the font to be remapped.


 751. GETLINE Behaves Erratically with Multiline Edit Controls

 Problem:

 I am having problems using a multiline edit control inside a dialog
 box. I need to print the text inside the edit control, so I send a
 EM_GETLINECOUNT, then go into a loop sending EM_GETLINE until all of
 the text is retrieved and printed.

 This works fine about 95 percent of the time. However, the function
 sometimes fails at random. Instead of printing a full page of text, a
 blank page is printed. Using debug statements, I find that the
 EM_GETLINECOUNT worked fine, but that the EM_GETLINE is inexplicably
 returning NULL lines each time I send the message.

 I haven't found consistent cases where this is reproducible, but
 usually after failing a few times, it starts working and stays working
 until I reboot or restart Windows. The messages are verified as
 correct, and the Edit control definitely contains exactly the same
 text each time.

 Response:

 In the buffer that you are providing to receive the text, you are
 required to enter the maximum number of characters to be transferred.
 If this byte happens to be zero, nothing will be transferred. The
 behavior you describe appears erratically because it depends on what
 happens to be in that byte in the buffer, which may vary from one run
 to the next.


 752. Windows SDK: DMA and Windows/386

 Question:

 I want to use a device that does DMA input and output with
 Windows/386. Will this work?

 Response:

 There is a problem using DMA with the 386 running in virtual machine
 mode because the DMA bypasses the virtual machine address translation
 performed by the 386 chip.

 There are two solutions to this problem:

 1. Only use DMA to those parts of the virtual machine for which
    VIRTUAL=REAL. In these regions, there is no address translation,
    and therefore data will read and write correctly. Under
    Windows/386, memory is divided into two types: that which is shared
    between all virtual machines, and that which is different for each
    virtual machine (instanced). Address translation does not take
    place for shared memory since VIRTUAL=REAL. Generally, all memory
    below where Windows/386 loads is shared, and everything above that
    point is instanced (there are some exceptions). Therefore, if you
    DMA to a buffer in your program data area, that will almost
    certainly fail. The solution is to provide a buffer below where
    Windows/386 loads by running a TSR prior to running Windows/386.
    Note that this buffer will be in the address space of all virtual
    machines, so be careful. Once the data is in the buffer, you can
    move it to where it really should be using MOV instructions (which
    do use the proper address translation). This is less than optimally
    efficient but it does work.

 2. Use a driver written in protected mode that can determine the real
    address of the target address range in the virtual machine. This
    cannot be done in the present release of Windows/386 but is very
    seriously under consideration for a future release. Some known
    devices are supported in this way inside Windows/386 (e.g.
    floppies, hardcards), but if your device does not look like one of
    these devices, then only option 1 is available right now.


 753. Windows SDK: FatalExit 0x0001 and Improper Use of WM_CTLCOLOR


 It is possible to change colors within a dialog box by intercepting
 the WM_CTLCOLOR message and returning the handle to a brush. However,
 if the brush handle is not a stock object it should be created before
 the dialog box is displayed and the handle stored in a variable. The
 following code should NOT be used within a dialog box procedure:

 ----------------------------------------------------------------------
 case WM_CTLCOLOR:                              /* Green Edit Fields */
    if (HIWORD(lParam)==CTLCOLOR_EDIT)
      return (CreateSolidBrush ( 0, 255, 0 ));
    break;
 ----------------------------------------------------------------------

 The WM_CTLCOLOR message is sent to the dialog box procedure every time
 any changes are made to the dialog box (such as a button push or
 moving between fields). Each time this message is interpreted, a new
 brush is created and stored in the GDI DATA segment. This can be
 verified by using the Heapwalker application (HEAPWALK.EXE) included
 with the Microsoft Windows Software Development Kit (SDK) Versions
 2.03 or later and repeating the operation. When Windows runs out of
 memory for the resources, a FatalExit 0x0001 (insufficient memory for
 allocation) is generated.

 A workaround to avoid this fatal exit is to modify the program by
 handling the CreateSolidBrush() function outside the message loop
 (or during the WM_CREATE or WM_DESTROY messages), so that it will
 only be called once. The following code skeleton and fragment show
 what changes should be made to WinMain() and the above dialog box
 procedure code fragment:

 ===================== WINMAIN ========================================

 /* Global Variable */

 HBRUSH    hBrushGreen;

 WinMain (...)
 {

    [ Register and Create Windows ]

    hBrushGreen = CreateSolidBrush ( RGB ( 0, 255, 0 ));

    while (GetMessage(...))
      {
      TranslateMessage (...);
      DispatchMessage (...);
      }

    DeleteObject (hBrushGreen);

    exit (msg.wParam);
    return (msg.wparam);
 }

 ----------------- Dialog Procedure code fragment ---------------------

    case WM_CTLCOLOR:                           /* Green Edit Fields */
       if (HIWORD(lParam)==CTLCOLOR_EDIT)
         return hBrushGreen;
       break;

 ======================================================================

 This example creates a global brush only once in WinMain(), processes
 all messages in the queue, and then removes the brush from memory.
 Another advantage to this method is that any part of the program can
 use this brush without using up extra resources.


 754. Windows SDK: Missing Backslash in FileOpen Printed Source Code

 The SeparateFile() function in the FileOpen sample application on
 Pages 181-182 of the "Microsoft Windows Software Development Kit
 Programmer's Learning Guide" will not compile as printed. Two of the
 lines in the function definition should have two backslashes instead
 of a single backslash.

 The FILEOPEN.C file contains the proper function definition; only the
 printed documentation is incorrect. FILEOPEN.C is located in the
 \LEARNING\FILEOPEN directory of the Sample Source Code disk in the
 1.2-megabyte disk set, the \FILEOPEN directory of the Learning Guide
 Sample Source Code disk in the 720K disk set, and the \FILEOPEN
 directory of the Sample Programs 2 disk in the 360K disk set.

 The following is the correct syntax for the first nine lines at the
 top of Page 182:

 {
     LPSTR    lpTmp;

     lpTmp = lpSrcFileName + (long) _lsrtlen(lpSrcFileName);

     while (*lpTmp != ':' && *lpTmp != '\\' && lpTmp > lpSrcFileName)
         lpTmp = AnsiPrev(lpSrcFileName, lpTmp);

     if (*lpTmp != ':' && *lpTmp != '\\') {

 The only changes are in the sixth and ninth lines from the top of the
 page. These are the lines that compare *lpTmp with a colon and a
 backslash.

 The single backslash causes the C compiler to interpret "\'" as an
 escape sequence and misinterpret the closing apostrophe. Compiling
 with a single backslash will cause the following C compiler errors:

 1. C2015      too many chars in constant

 2. C2016      no closing single quote

 3. C2146      syntax error : missing ')' before identifier 'ident'
               ('ident' is 'lpTmp' for first comparison, '_lstrcpy' for
               second comparison).


 755. Windows SDK: Leaving Code and Data in Memory after Termination

 Question:

 What would cause the code and data of an application to remain in
 memory after the application was terminated?

 Response:

 In order for the code, data, and resources to be freed by Windows, the
 application must post itself a WM_QUIT message. This is usually done
 at the time the overlapped window for the application is destroyed. An
 example of this is demonstrated by the following code fragment:

    case WM_DESTROY:
       PostQuitMessage(0); /* return code set to zero */
       break;

 Without processing the message in this manner, an application's code
 and data would still be resident in memory after the application
 terminated. A good way to tell if the application has cleaned up after
 itself is to run HEAPWALK.EXE, as follows:

 1. Select Walk Heap under the Walk menu.

 2. Select Module under the Sort menu to sort the heap by name.

 Any items associated with your application would show up in the list.

 Another candidate for unfreed memory upon termination are GDI objects.
 Any GDI object that is created must be destroyed, or it will continue
 to occupy space in GDI's data segment. A call to DeleteObject() must
 be made for each GDI object created to free all memory used by the
 objects. This would not cause the code and data segments to remain,
 but is merely another situation in which applications can leave
 unclaimed memory (in this case, in the GDI) after termination. Also,
 any DCs that are created must be deleted with DeleteDC(), and any DCs
 that are obtained with GetDC() must be released with ReleaseDC().
 Calls to BeginPaint() must be matched with calls to EndPaint().


 756. Windows SDK: Accelerator to Exit Causes Growth of USER's DS

 Calling DestroyWindow() and quitting the application in response to an
 accelerator generated message causes the DS of the USER to grow.
 TranslateAccelerator() is locking the accelerator resource before
 sending the message to the window, but the resource never gets
 unlocked.

 This problem can be avoided by calling PostMessage(hWnd,WM_CLOSE,0,0L)
 rather than DestroyWindow(), as shown below. This delay in closing the
 window causes the accelerator resource to be unlocked before the
 window is closed. In response to WM_CLOSE, DefWindowProc() calls
 DestroyWindow() and, therefore, has the same effect as calling
 DestroyWindow() immediately, but avoids the problem of leaving the
 locked resource around.

 To reproduce the problem, modify the SHAPES sample application as
 follows:

 1. Modify SHAPES.H by adding "#define IDEXIT 500".

 2. Add an Exit item to the menu in the .RC file with the id "IDEXIT".

 3. Add an item to the accelerator table, as follows:

       "^X", IDEXIT

 4. Include the following in the window proc:

 ...
     case WM_COMMAND:
         if(wParam == IDEXIT)        // these three
             DestroyWindow(hWnd);    //  lines are the
         else                        //  only changes
             ShapesCommand( hWnd, wParam );
         break;

     case WM_DESTROY:
         PostQuitMessage( 0 );
         break;

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

 5. Make and run the program.

 6. Exit by pressing CTRL+X.

 7. Repeat many times.

 Check the size of the USER's DS before and after performing the steps
 above, and you will notice that the DS of the USER has grown
 substantially.

 Microsoft is researching this problem and will post new information as
 it becomes available.


 757. Old Application Running in a Window Hangs on Int10 Function07

 Calling Interrupt 10H Function 07H in a standard DOS application while
 running in a window under Windows/286 will cause the system to hang.
 You must power down to restart the system. Pressing CTRL+ALT+DEL
 doesn't work. This problem only occurs if the height of the area of
 the screen to scroll is one line.

 The program will work correctly when running full screen, under DOS,
 or under Windows/386.

 Setting in.h.al=0 clears the line without hanging.

 Setting in.h.al=1 or greater hangs the system.

 To avoid this problem, only call Interrupt 10H Function 07H to scroll
 an area greater than one line in height, or set the number of lines to
 scroll to zero (this has exactly the same effect as scrolling by one
 line, i.e., it clears the line).

 To reproduce this problem, call the following piece of code from a
 standard DOS application while it is running in a window:

 scroll()
 {
  in.h.cl = 0;       // upper left x
  in.h.ch = 23;      // upper left y
  in.h.dl = 79;      // lower right x
  in.h.dh = 23;      // lower right y
  in.h.bh = 07;      // attribute for blanked area
  in.h.ah = 07;      // function
  in.h.al = 01;      // number of lines to scroll
  flags = int86(0x10,&in,&out);
 }

 Microsoft is researching this problem and will post new information as
 it becomes available.


 758. Windows SDK: Creating Windows Vector Fonts

 There is a file in the Software Library named WINVECT.ARC that shows
 how to build Windows vector fonts.

 WINVECT.ARC can be found in the Software Library by searching for the
 filename, the Q number of this article, or S12237. WINVECT.ARC has
 been archived using the PKware file-compression utility.


 759. Windows SDK: TIFF 5.0 Information

 Question:

 Where can I get information about how to read and write TIFF files?

 Response:

 The Aldus Corporation can supply you with the most recent manual,
 along with a companion disk. To get this information, please call
 Aldus at (206) 628-2320 and ask for Product Information for TIFF.


 760. Article on Windows 2.03's Use of Expanded Memory Is Available

 There is an article in the Software Library that discusses the
 Lotus/Intel/Microsoft (LIM) Expanded Memory Specifications (EMS)
 support of Windows Version 2.03. The article was reprinted from the
 January 1988 issue of the "Microsoft Systems Journal." It was written
 by Paul Yao, coauthor of the "Programmer's Guide to Windows." The
 original LIM/EMS article contained several detailed illustrations that
 could not be reproduced. For more information on these diagrams and
 the author, please refer to the "Microsoft Systems Journal."

 This article can be found in the Software Library by searching on the
 keyword WINEMS, the Q number of this article, or S12242. WINEMS was
 archived using the PKware file-compression utility.


 761. WDEB386: Initial Break Point

 The WDEB386 documentation is missing an important piece of
 information. WDEB386 supports a /b command-line switch to cause an
 initial break point that gives you a debugger prompt before beginning
 execution.


 762. Windows SDK: System Topics with Excel and DDE

 Problem:

 I am sending a WM_DDE_REQUEST to Excel. I initiate the DDE
 conversation with the application Excel and the topic "System". I then
 request the item "Selection".

 When Excel responds by sending me a WM_DDE_DATA message, it sets both
 fAckReq and fRelease to 0 in the DDEUP struct.

 Response:

 Microsoft has confirmed this to be a problem in Microsoft Excel
 Version 2.10. We also found another problem with using the System
 topics. This occurs when using LIM 4.0: the results you get back are
 not reliable; you should not use them.

 The fact that the Ack and Release are both zero may also be related to
 this problem. We are researching this problem and will post new
 information as it becomes available.


 763. MessageBox() MB_STEMMODAL Not a Valid Flag

 Page 374 of the "Microsoft Windows Software Development Kit
 Programmer's Reference" for Versions 2.03 and 2.10 refers to an
 MB_STEMMODAL flag in the "Comments" section under the MessageBox()
 function. This is a documentation error; the flag doesn't exist. The
 flag instead should be MB_SYSTEMMODAL.


 764. Windows SDK: Getting the Filename of the Running Application

 The lpCmdLine parameter passed to WinMain contains the command line
 used to start the application. However, rather than the name of the
 application, something similar to "WIN200.OVL" is provided as the
 first argument.

 It is possible for an application to obtain its filename by using the
 GetModuleFileName function. The following code demonstrates how this
 is done:

 /*************  getappnm.c  *************/

 #include "windows.h"

 int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
 HANDLE hInstance, hPrevInstance;
 LPSTR lpszCmdLine;
 int cmdShow;
 {
     char cModuleFileName[256];
     short iMFLen;

     iMFLen = GetModuleFileName(hInstance,cModuleFileName,250);
     // note: hInstance works out to be the same as hModule
     //         if used at the start of WinMain.

     if(iMFLen > 0)
         MessageBox(GetFocus(),cModuleFileName,"The Name",MB_OK);
     else
         MessageBox(GetFocus(),"No Name Returned","",MB_OK);

 }

 /*********** getappnm.def ************/

 NAME  getappnm

 DESCRIPTION 'Simple Microsoft Windows Application'

 STUB    'WINSTUB.EXE'

 CODE  MOVEABLE
 DATA  MOVEABLE

 STACKSIZE   128
 HEAPSIZE    128

 /************** getappnm.mak ***************/

 getappnm.obj: getappnm.c
     cl -d -c -AS -Gw getappnm.c

 getappnm.exe: getappnm.obj getappnm.def
     link4 /noe getappnm,/align:16,,slibw,getappnm.def


 765. EditFile Scroll Bars Not Updated Properly after Shrinking

 Problem:

 I am having trouble getting scroll bars to update when a window
 shrinks. There is no problem when the window grows. This problem can
 be illustrated with the EditFile sample application included with the
 Microsoft Windows Software Development Kit (SDK) Version 2.10 by
 running EditFile and resizing the window.

 Response:

 This problem can be solved by using TRUE as the bRepaint parameter of
 the MoveWindow() function in processing the WM_SIZE message. This
 message is processed in the EditFileWndProc() function of the
 EDITFILE.C file.

 The code fragment to change is as follows:

 .
 .
 case WM_SIZE:
   MoveWindow(hEditWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), FALSE);
   break;
 .
 .

 Change the above code fragment to the following:

 case WM_SIZE:
   MoveWindow(hEditWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
   break;

 This allows the window to be resized, and successfully redraws the
 scroll bars in the window.


 766. Build New Windows Libraries after Adding New C Libraries

 If a new set of C libraries is installed (e.g. medium memory models),
 the Microsoft Windows Software Development Kit (SDK) must be installed
 again. Otherwise, link error messages such as "Unresolved Externals"
 will occur when linking Microsoft Windows programs.

 When the Windows SDK is installed using INSTALL.EXE, the specified
 libraries directory is searched for Microsoft C libraries ?LIBCE.LIB
 and ?LIBCA.LIB. These are combined libraries with either the emulator
 or the alternate floating point math package. For each memory model
 found, appropriate library files are built for linking with Microsoft
 Windows programs.

 When a new memory model is built with Microsoft C's setup software, it
 is not modified for linking with Microsoft Windows programs.

 For information on how to build the Version 2.10 libraries without
 going through the entire SDK setup program, search in the
 KnowledgeBase on the keyword LIBCAW.BLD.


 767. Slow Boot Windows from Secondary Monochrome Monitor

 If a "slow boot" version of Microsoft Windows is invoked from the
 secondary monochrome screen in a dual-monitor system, unexpected
 results may occur.

 If a slow boot version of Microsoft Windows is installed, everything
 works as expected until after exiting the MS-DOS Executive. Both
 screens will then be blank, and the cursor will be blinking on the
 monochrome screen. At this point, if you type, the cursor will not
 move and no characters will be on the screen, giving the mistaken
 impression that the system has crashed. However, if you enter the
 command "MODE CO80", the color screen will clear and the expected
 prompt will appear there.

 If a "slow boot debugging" version of Microsoft Windows is installed,
 the system crashes as soon as Windows is invoked.


 768. GetAsyncKeyState() Doesn't Support Mouse Virtual Keys

 Question:

 Does GetAsyncKeyState() work with the mouse virtual key codes, such
 as VK_LBUTTON?

 Response:

 No, GetAsyncKeyState() does not support the mouse virtual key codes.
 The key state table contains no information about the state of the
 mouse, since the mouse is not a keyboard key.


 769. OS/2's Resource Compiler Not Compatible with Windows

 The protected-mode resource compiler included with the OS/2 SDK
 (Software Development Kit) is not compatible with Windows resources.
 This means that Windows programs cannot be completely built in OS/2
 protected mode. You must switch into real mode so that you can use the
 Windows version of RC; however, all of the other tools needed to build
 a Windows application can be run in protected mode.


 770. CVW: DLL Breakpoints Ignored When EMS Disabled for WIN.COM

 When Windows' use of EMS is disabled, CodeView for Windows Version
 2.10 will not stop at breakpoints that are set in the initialization
 code of a Dynamic Link Library.

 For example, suppose you have a Windows application called "myapp,"
 which uses a DLL called "mydll," and this DLL has an initialization
 routine called "libentry." If you invoke CodeView with

    >cvw /2 /l myapp /l mydll c:\windows\debug\win.com -n

 and set a breakpoint at the DLL start-up code with

    >bp LIBENTRY
    >g

 then Windows will ignore the breakpoint you just set.

 Invoking WIN.COM without the -n switch solves the problem.

 Microsoft has confirmed this to be a problem in Version 2.10. We are
 researching this problem and will post new information as it becomes
 available.


 771. Calculating Memory Requirements for Old Applications

 Question:

 How much memory is required to spawn a non-Windows application in a
 window, and how is this value derived? We want to find out if there's
 enough memory to spawn an application BEFORE we try spawning it.

 Response:

 There is no good way for your application to programmatically
 determine how much memory a given old application will require to run.
 One reason is that sometimes when you run an old application, Windows
 has to bring in the WinOldApp() module and then load and run the old
 application; at other times, WinOldApp() will already be loaded
 because of other old applications that are running, so only the old
 application itself has to be loaded. The following statistics
 demonstrate this for you:

    PIF     PIF     FreeMem FreeMem Memory
    Req'd   Des'd   Before  After   Usage
    -----   -----   ------  -----   -----

    160     160     418     155     263     First copy run
    160     160     153     152       1     Second copy run

 In the above table, the first two columns are the PIF file settings
 for Memory KB Required and Memory KB Desired, the next two are dynamic
 memory usage reports from Charles Petzold's FREEMEM program (available
 on the Software Library, as described in the KnowledgeBase; see
 below), and the last column is the difference in available memory
 reported by FREEMEM as a result of loading that copy of that
 application.

 As you can see in the above figures, the second copy of the
 application costs almost nothing in memory usage. This is because the
 two copies of the application are sharing the same memory area and are
 being swapped in and out of memory (to disk, to a RAM drive, or to
 expanded memory, depending on the WIN.INI settings).

 Another factor is the order in which old applications are loaded.
 Refer to the following statistics. In the first case, an 80K program
 is loaded and then a 160K program; in the second case, the 160K
 program is loaded and then the 80K program:

 Case 1:

         PIF     PIF     FreeMem FreeMem Memory
         Req'd   Des'd   Before  After   Usage
         -----   -----   ------  -----   -----

          80      80     418     244     174     App #1 runs first
         160     160     244      37     207     App #2 runs second
                                         381     Total memory usage

 Case 2:

         PIF     PIF     FreeMem FreeMem Memory
         Req'd   Des'd   Before  After   Usage
         -----   -----   ------  -----   -----

         160     160     418     154     264     App #2 runs first
          80      80     154     153       1     App #1 runs second
                                         265     Total memory usage

 These results are not as unusual as they may appear. In the second
 case, the larger application (App #2) is loaded first. This sets the
 WinOldApp() swapping partition large enough to hold the application.
 Now, when the smaller application (App #1) is run, it fits into the
 partition already set up. In contrast, in the first case, the smaller
 application is run first, so the swapping area isn't set large enough
 to hold the larger application; when it gets loaded, WinOldApp() has
 to create a completely separate partition to hold it.

 The amount of memory needed to run an old application varies,
 depending on the following:

 1. Whether or not WinOldApp is loaded

 2. Whether the existing swap area (if any) is large enough to hold it

 3. Whether the application will run full screen or in a window

 4. Whether the application needs its screen saved in text or graphics
    mode

 5. Whether you're in large-frame, small-frame, or no EMS

 6. The contents of the memory Required and Desired fields in the PIF
    file

 7. Other factors related to the subtle inner workings of Windows'
    memory manager and/or the WinOldApp() module

 FREEMEM can be found in the Software Library by searching on the
 filename, the Q number of this article, or S10005. For a brief
 description of the program, query on the keyword FREEMEM.


 772. Windows SDK: Differences between USERF.EXE and USERS.EXE

 Question:

 In the Windows SDK (Software Development Kit) Version 2.10, I found
 two debug versions of USER.EXE: USERF.EXE and USERS.EXE. What are the
 differences between the two? I need this information to install my
 slow-boot version of Windows.

 Response:

 USERS is USER "Small," meaning that it is a smaller and slower version
 of the module. It sacrifices speed for size.

 USERF is USER "Fat," meaning that it is a fatter and faster version of
 the module. It sacrifices size for speed.

 However, the trade-offs aren't black and white. USERS is used when you
 don't have HIMEM.SYS, and therefore all of Windows has to reside in
 your computer's under-640K memory area. Because DOS memory is so
 precious, Windows uses the smaller version of USER in order to
 minimize its consumption of this precious RAM.

 USERF is used when you have HIMEM.SYS installed, and therefore have an
 extra segment of memory starting just under the 1-MB line, giving you
 an extra 64K of RAM. By putting the USERF module in this "extra"
 memory segment, Windows frees up more space in the cramped under-640K
 memory area. Using USERF and HIMEM.SYS gives you both speed and space,
 and is thus the recommended configuration.

 Determining which version of the SYM files to use (USERF.SYM or
 USERS.SYM) is as easy as knowing what kind of system you have. If you
 have a 286 or 386 machine with extended memory and you're running
 HIMEM.SYS, Windows will install USERF.EXE, and you should use
 USERF.SYM for debugging. Otherwise, Windows uses USERS.EXE, so you
 should use USERS.SYM.


 773. Windows SDK: Documentation for the SetTextAlign() Function

 The SetTextAlign() function description is somewhat unclear and the
 description of the return value is incorrect. The return value should
 be described as follows:

    The return value specifies the PREVIOUS alignment: the alignment is
    contained in the low-order BYTE. The alignment values can be
    obtained by performing an AND with the above wFlags values.

 The wFlags parameters are bit flags, but they are exclusive. You
 should OR the vertical and horizontal alignment, and update
 flags together to create the desired alignment. Note that the flags
 are defined as follows:

 /* update flags */
 TA_NOUPDATECP    0  or  00000000
 TA_UPDATECP      1  or  00000001
     note: TA_UPDATECP causes the X and Y parameters of TextOut() to be
     ignored. It will position the text using the current point and
         will update the current point after drawing the text.

 /* horizontal alignment */
 TA_LEFT          0  or  00000000
 TA_RIGHT         2  or  00000010
 TA_CENTER        6  or  00000110

 /* vertical alignment */
 TA_TOP           0  or  00000000
 TA_BOTTOM        8  or  00001000
 TA_BASELINE     24  or  00011000

 Therefore, if the alignment is currently the default and you call the
 following, the return value would be 0 or TA_NOUPDATECP | TA_LEFT |
 TA_TOP, and you can see that the new alignment is TA_UPDATECP |
 TA_CENTER | TA_BASELINE.

 SetTextAlign(hDC,TA_NOUPDATECP | TA_UPDATECP | TA_LEFT | TA_RIGHT |
 TA_CENTER | TA_TOP | TA_BOTTOM | TA_BASELINE);

 This new alignment would center the text horizontally at the baseline
 on the current point. The current point would remain at the center
 point of the text. If TA_LEFT were specified rather than TA_CENTER,
 the text would be drawn starting at the current point, aligned on the
 baseline, going to the right. The current point would be updated to be
 to the right of the last character.

 The descriptions for the flags are also misleading. They only fully
 apply if no other flag is added. However, the name does give the
 correct connotation.


 774. Windows SDK: MAPSYM Size Limit

 There is a limit in MAPSYM in the number of lines it can process in a
 segment. If the limit is exceeded, an out of memory error may be
 returned, or a "No Entry Point" error may be returned. Up to a maximum
 of 3500 lines per segment, MAPSYM will work correctly and predictably.
 Around 4000 lines per segment, MAPSYM may behave erratically. Around
 4500 lines per segment, MAPSYM will fail. It is necessary for only one
 segment to approach or exceed the limit for MAPSYM to have this
 problem.


 775. Windows SDK: How to Print EPS

 Question:

 How should I go about handing an EPS (Encapsulated PostScript) piece
 of my document to the printer? Obviously, it only works with a
 PostScript printer.

 Response:

 To send an EPS piece of your document to the printer, do the
 following:

 1. Use the DEVICEDATA printer escape to send the information.

 2. Frame the Postscript with "gsave" and "grestore". If you are doing
    anything complicated, you should do a "save" and "restore" instead.

 The PS driver doesn't reorder output. Also, since it isn't a banding
 device, the spooler shouldn't cause any problems.


 776. Timer Callback Function Doesn't Have to Be in Fixed DLL

 It is not necessary to put the callback function for a timer in a
 FIXED code segment DLL. The callback function is called inside the
 DispatchMessage() function. Therefore, when using EMS, there is no
 chance that the application will be banked out when the timer callback
 needs to be called. If the callback function is in a DLL, the DLL's
 code segment, or a copy of its CS, is banked with the application.
 Therefore, the DLL will be banked in with the application whose
 dispatch call calls the callback function.


 777. Building a Debugging Version of Windows SAE

 Question:

 I have installed a Standalone Application Environment (SAE) version of
 Windows 2.10 for feasibility testing. In order to debug my
 applications more thoroughly, I tried to install a "debugging" version
 of Windows using my SAE install disks (1.2 MB, high density).

 When "MKDEBUG WIN" (using SETUP.EXE) prompts me for the
 Setup/Build/Displays1 disk, I put the disk in Drive A; however, the
 setup program does not seem to read it. It looks at the disk, but then
 reissues the same prompt for the same disk.

 What do I need to do to install a debugging SAE Windows?

 Response:

 Instead of trying to use the MKDEBUG batch file to build a debugging
 SAE version of Windows, you should instead make a backup of your SAE
 install disks and then copy the debugging versions of USER.EXE,
 KERNEL.EXE, and GDI.EXE onto them. Then, go through the normal SAE
 installation.


 778. Windows' Support of OS/2 Family API Calls

 Windows does not support applications coded with OS/2 Family API
 (FAPI) calls.

 FAPI works in the following manner:

 1. Your code contains references to OS/2 FAPI calls [such as
    DosOpen(), DosRead(), etc.].

 2. When you load the program into OS/2 protected mode, the system
    loader dynamically links the FAPI calls to the OS/2 system-services
    DLLs.

 3. When you load the program into real mode (MS-DOS or OS/2
    compatibility box), what actually gets loaded is a small program
    called the "FAPI Loader and Linker." It is this program that loads
    the real code; at this time, it dynamically links the FAPI calls to
    a special library of support routines that translate FAPI calls
    into 80x86 code and MS-DOS interrupts (INT 21 Function xx).

 This process is also described in Gordon Letwin's "Inside OS/2"
 (Microsoft Press, 1988), starting on Page 251.

 FAPI works well for programs that need to run in MS-DOS and OS/2
 protected mode. The problem is that Windows uses the "New EXE Format"
 for programs, bypassing the standard MS-DOS entry point. For example,
 when you try to run a Windows program outside of Windows (in MS-DOS),
 the message "This program requires Microsoft Windows" appears and the
 program terminates. MS-DOS is not responsible for this message; the
 Windows program itself is. The way the Windows program works is very
 similar to OS/2: it uses dual entry-points into the .EXE; in MS-DOS, a
 short program that prints the above message runs, while in Windows, a
 true Windows application runs using the other entry point in the .EXE.

 You can now see the problem: if the FAPI Loader and Linker program
 is run using the standard MS-DOS .EXE entry point, but Windows starts
 an application using a different entry point, the dynamic linking of
 the FAPI routines will not occur.

 Therefore, FAPI calls cannot be used in Windows applications.

 To avoid this problem, do the following:

 Instead of using low-level DOS calls (INT 21 Function xx) for your
 Windows application, and OS/2 API calls [DosRead(), DosOpen(), etc.]
 for your Presentation Manager application, use the C run-time I/O
 routines for all of your applications.

 This will work because the Microsoft C Compiler and the run-time
 libraries supply versions of the libraries that work in both OS/2 and
 MS-DOS. By moving your C code to PM and to Windows, you'll save the
 effort of rewriting it, and the appropriate conversion routines will
 be supplied at link-time.

 Be sure not to use high-level (stream) I/O routines in C with Windows;
 you should stay with the low-level (handles) versions. This is
 discussed on the OnLine KnowledgeBase as well as in Charles Petzold's
 "Programming Windows" (Microsoft Press, 1988).


 779. SAE: Clarification on Deleting Fonts

 In the Single Application Environment (SAE) instruction packet, Step 2
 of the "Preparing a New Disk Set" section says to remove some font
 files. These font files are only used with the standard DOS
 application interface and, therefore, are not needed in an SAE
 environment.

 The instructions say to remove the FONTSQ??.FON files. This is
 misleading; the double question marks do not refer to wildcard
 characters. Instead, they indicate that files whose names start with
 FONTSQ AND contain two additional characters are to be deleted. For
 Windows 2.10, the following are the .FON files to be removed:

    FONTHIUS.FON
    FONTLOUS.FON
    FONTSQUS.FON
    FONTSQNO.FON
    FONTSQPO.FON
    FONTSQCA.FON
    FONTSQES.FON

 If the ERASE FONTSQ??.FON command is used, it will also remove the
 FONTSQ.FON file, which is a required file and should not be deleted.

 Note: These files are only to be removed from the floppy disk. Do not
 alter or remove their references in the SETUP.INF file.


 780. Windows SDK: HeapWalker Column Definitions

 The meanings of the seven columns of information that HEAPWALK Version
 2.10 displays are as follows:

    1 - Segment address of object 'Segment arena header'
    2 - Size of object in bytes
    3 - Lock count
    4 - Discardable memory flag (such as "D")
    5 - Name of object's owner
    6 - Type of object (such as resource, code, data, etc.)
    7 - Additional information such as code segment number or type
        of resource

 The documentation completely omits any information regarding files
 saved using HEAPWALK. HeapWalker automatically attaches a section
 called **-Module-**, which has five columns indicating the following:

    Column  Description

    1)      Module name
    2)      Number of discardable segments loaded in memory
    3)      Number of bytes these discardable segments occupy
    4)      Number of bytes that nondiscardable segments are taking in
            memory
    5)      Total number of bytes that module is occupying in memory
            (3+4=5)

 Most of this information can be found in the book "Programming with
 Windows" by Tim Farrell. No further information on the **-Module-**
 section of this article is available.


 781. Windows SDK: Local Variables Losing Values between Messages

 Local variables in callback procedures that are not declared "static"
 can lose their value between message executions. This is because
 nonstatic local variables are pushed onto the stack each time the
 procedure is entered and are popped off when the procedure call is
 complete. The procedure for a window is reentered each time a message
 is sent to that window.

 Sometimes program flow is not as expected within a child window. For
 example, if a child window calls the following function, messages will
 be sent to both the child window and the parent window:

    MessageBox(hParentWnd, --, --, -- );

 The program flow may be disrupted. In the following abbreviated
 example, the value of the local variable FOO may be lost in an
 application (changing the variable to static solves the problem):

 /* ------------------------------------------------------------------ */

 long FAR PASCAL ChildProc(hChildWnd, iMessage, wParam, lParam)
 HWND    hChildWnd;
 int     iMessage;
 WORD    wParam;
 LONG    lParam;
 {
    int  FOO;                  /* Should be static! */
    char szShowValue[10];

    switch(iMessage)
       {
       case WM_CREATE:

          FOO = 4;
          break;

      case FOO_MESSAGE:

          MessageBox(hParentWnd, (LPSTR)"Foo", (LPSTR)"Bar", MB_OK);
          sprintf(szShowValue, "%d = FOO", FOO);
          MessageBox(hparentWnd, (LPSTR)szShowValue, (LPSTR)"Value", MB_OK);
          break;
      }
 }

 /* ------------------------------------------------------------------ */

 In this example, program flow does not wait for the OK button to be
 pressed on the first message box, because the message box was hooked
 to hParentWnd. However, many messages are sent to both the parent and
 child windows when this message box is created, and in the process of
 the message translation, FOO is initialized upon entry of the
 procedure. The second message box may not be called immediately, and
 this would cause the FOO variable to be popped from the stack when the
 procedure call returns.

 The solution is to make FOO a static variable.


 782. Cannot Change the Icon for a Standard DOS Application

 Question:

 Can the icon for an application running under a .PIF file be changed?

 Response:

 Under the current versions of Windows, the icon displayed for a
 minimized DOS application running under a .PIF file cannot be changed.


 783. NAME Statement in .DEF File Must Start with a Letter

 In the module definition file for a Microsoft Windows application, the
 application name that is defined by the NAME statement must begin with
 an alphabetic character. If the name begins with any other type of
 character, including a numeric character, the LINK4 linker will report
 a syntax error in that line of the .DEF file.

 The following .DEF file does not generate the error:

    NAME             PROG1
    DESCRIPTION      'Example of good Name'
    STUB             'WINSTUB.EXE'

    CODE             MOVEABLE DISCARDABLE
    DATA             MOVEABLE MULTIPLE

    HEAPSIZE         8192
    STACKSIZE        8192

    EXPORTS
      SimpleWinProc          @1

 The following .DEF file generates a syntax error on line 1:

    NAME             1PROG
    DESCRIPTION      'Example of bad Name'
    STUB             'WINSTUB.EXE'

    CODE             MOVEABLE DISCARDABLE
    DATA             MOVEABLE MULTIPLE

    HEAPSIZE         8192
    STACKSIZE        8192

    EXPORTS
      SimpleWinProc          @1

 Therefore, the workaround is to be sure to use an alphabetic character
 as the first character in the program name.


 784. Windows SDK: Xon/Xoff Flow Control Use in Comm Functions

 A few things must be done to allow Windows to provide Xon/Xoff flow
 control during transmission and reception of serial communications
 data.

 To enable transmit flow control, do the following:

 1. Set the fOutX field of the DCB structure to TRUE.

 2. Set the XoffChar field of the DCB structure to the character to use
    to signal Xoff. On most systems, this is 0x13.

 3. Set the XonChar field of the DCB structure to the character to use
    to signal Xon. On most systems, this is 0x11.

 To enable receive flow control, do the following:

 1. Set the fInX field of the DCB structure to TRUE.

 2. Make sure the XoffChar and XonChar are set as described above.

 3. Set the XoffLim field of the DCB structure to a number appropriate
    for the baud rate and system from which data is being received. In
    many cases, zero (0) will work correctly. However, some systems may
    not respond immediately to an Xoff and will continue to send data
    for a certain amount of time. To avoid overflowing the buffer,
    XoffLim should be set to a number that is reasonable for the
    system. This number is the number of characters from the top of the
    queue, which, if the queue is filled to this point, will trigger
    the sending of an Xoff.

 4. Set the XonLim field of the DCB structure to a number appropriate
    for the baud rate and system from which data is being received. In
    many cases, zero (0) will work correctly. However, some systems may
    not respond immediately to an Xon, and a pause in communications
    will occur if the queue is completely emptied before sending the
    Xon character. This number is the number of characters remaining
    before the queue is empty, which, when the queue is emptied to this
    point, will trigger the sending of an Xon.


 785. How to Run in a Loop and Not Lock up Windows

 Question:

 I have a function, func(), inside a loop. It appears as follows:

     for (i = 0; i < 1000; i++)
         {
         func();
         }

 I want a dialog box to appear just before I enter the loop. The dialog
 box must show a counter, i, to let the user know how far we are in
 this process. The dialog box must also have an abort button to allow a
 user to cancel the process before completion. I do not need (or want)
 to abort while inside func(), but I do need to check to see if the
 abort button has been pressed just before func() is called, as
 follows:

     for (i = 0; i < 1000; i++)
         {
         if (bAbort)
             get out and close the dialog box;
         func();
         }

 How do I do this in Windows?

 Response:

 There is a sample application in the Software Library called PEEKF1
 that does something very similar. It has just been modified and
 improved. This file can be found in the Software Library by searching
 for the filename PEEKF1.ARC, the Q number of this article, or S12197.

 You should use PeekMessage() to get and dispatch the next message on
 the message queue. In the PEEKF1 sample, the program reads a file
 continuously and prints some output in its window to show how it is
 doing. While the program is running, you can pull down the menu or
 stop the program using an accelerator key. You can also run several
 instances of the program at the same time, with all instances reading
 the same file or all instances reading different files.

 The following is the key piece of code from the PEEKF1 program:

          /*              CHECK IF USER WANTS ATTENTION

      if (PeekMessage((LPMSG)&msg, (HWND)NULL, 0, 0, 1) ) {
          if (TranslateAccelerator(hWnd,hAccelTable,(LPMSG)&msg)==0) {
              TranslateMessage((LPMSG)&msg);
              DispatchMessage ((LPMSG)&msg);
          }
      }


 786. Distinguishing the ALT and F10 Keys under Windows

 Question:

 Is there any way for my application to recognize the F10 key? Why does
 the F10 key function the same as the ALT key?

 Response:

 The the F10 key and the ALT key generate the same WM_KEYDOWN message
 because this is part of IBM's SAA standard.

 To distinguish between the two keys, you need to look at the
 WM_SYSKEYDOWN message. When the F10 key is pressed, it generates the
 WM_SYSKEYDOWN message with the parameter VK_F10 (0x79). When the ALT
 key is pressed, it generates a WM_SYSKEYDOWN message with the
 parameter VK_MENU (0x12).

 To verify this, you can run the KEYLOOK program on Pages 147-149 of
 Charles Petzold's "Programming Windows."



 787. Link Error L2028: Automatic Data Segment Plus Heap Exceed 64K

 The LINK4 error L2028 is caused when the data segment required by the
 application exceeds 64K. This can be calculated by adding the HEAPSIZE
 and STACKSIZE portions of the .DEF file, plus the global data and
 static data. These two numbers of the .DEF file plus the size of the
 global and static data need to be under 65536 bytes. The global data
 refers to global variables in an application, not the global heap.

 The solution is to reduce the amount of the HEAPSIZE, STACKSIZE,
 global data, or static data. If the application utilizes large amounts
 of global data, some of this data should be moved into the memory
 covered by Windows [e.g. utilizing GlobalAlloc()]. If the data
 consists of constants, such as string tables, these can be moved into
 Windows resources using the RC compiler.


 788. Changing Dialog Box Captions from Outside Dialog Box

 Question:

 I'm opening a modal dialog box from a modeless dialog box. My resource
 script for the modal box has a caption style for which the text is
 described in the resource script. I want to change the caption text
 dynamically while the modal box is open. The modeless parent knows
 what the text should be, and knows it before the Dialogbox() function
 is called. After the modal box is called, is there a way to send the
 SetWindowText() call from the modeless parent? If not, how can I send
 the call before the DialogBox() function is called, since the window
 has not yet been created? I assume that the window class for the modal
 box is registered by virtue of its resource script.

 Response:

 In the following example, let's assume that the caption of the modal
 dialog box should get the name of whatever is in the edit field in the
 modeless dialog box identified by IDD_EDITMODELESS:

 1. Just before calling DialogBox() to bring up the Modal DB, use the
    following to place the contents of the edit field into a GLOBAL
    buffer:

       GetDlgItemText(hDlg, IDD_EDITMODELESS, GlobalBuffer, LengthOfEditField)

 2. In the WM_INITDIALOG message-processing section of the modal dialog
    procedure, use the following to change the caption text:

       SetWindowText(hDlg2, GlobalBuffer)

 You would probably wish to set the caption to "" in the .RC script
 file so that the user does not see two different titles flash in the
 caption bar (one at creation, and one when Step 2 is executed).


 789. Hiding the Scroll Bar in a List Box or Edit Control

 Question:

 How can I hide the scroll bar in a list box or edit control? For
 example, if my list box is 10 lines high and there are currently only
 5 entries, I want to hide the scroll bar.

 Response:

 This can be accomplished in two ways:

 1. Call ShowScrollBar() to hide the scroll bar explicitly.

 2. Call the SetScrollRange() function to hide or show the scroll bar
    algorithmically. If the nMinPos and nMaxPos parameters are equal,
    the scroll bar will be hidden. If these parameters are different,
    the scroll bar will be shown. Therefore, whenever a line is added
    to or deleted from the list box or edit control (or other type of
    window), set the scroll range to the total number of lines minus
    the number of lines that can be displayed at one time in the
    control. This way, when there are more lines than can be displayed,
    the scroll bar will be visible, and when there are fewer lines than
    are needed to fill the display rectangle, the scroll bar will be
    hidden.


 790. Windows SDK: WIN87EM and Fatal Exit 412

 Problem:

 We are having difficulty with consistent launches of additional
 instances of our application. We get the following error message:

    "Unable to load non-resident name table from WIN87EM"
    FatalExit code = 0x0412

 Response:

 This error occurs because of a low memory condition. There is a
 potential problem in Windows Version 2.10 with DLLs exporting
 functions by name rather than ordinal. WIN87EM, released with the SDK
 Versions 2.00 and 2.10, exported by name rather than by number. There
 is a newer version of WIN87EM in the Software Library that includes
 ordinal numbers inside the LIB file. This file can be found in the
 Software/Data Library by searching on the keyword WIN87EM, the Q
 number of this article, or S12193. WIN87EM was archived using the
 PKware file-compression utility.

 Please make sure you use both of the new LIB and EXE files.


 791. ILINK: Fatal Error L1268: .SYM/.ILK Mismatch

 Problem:

 When using ILINK to relink the C files I have changed, I get the
 following error message:

    ILINK: fatal error L1268: .SYM/.ILK mismatch

 Response:

 As described on Page 64 of the ILINK documentation contained in the
 "Microsoft CodeView and Utilities Software Development Tools for MS
 OS/2 and MS-DOS Operating Systems Update" for C Version 5.10, the /inc
 switch produces both a .SYM file and an .ILK file. Both of these files
 contain pertinent data for ILINK. Also, please note that on Page 96 of
 the SYMDEB section of the "Microsoft Windows Software Development Kit
 Programming Tools" manual, the MAPSYM command creates a .SYM file from
 the MAP file.

 Since the MAPSYM command follows the link4/inc command line, the .SYM
 file created to be used with ILINK is overwritten by MAPSYM and
 therefore will not be recognized by ILINK. If you were to comment the
 MAPSYM line out of the make file, the above error will disappear. The
 symbolic information is already created; therefore, you do not need
 MAPSYM. If you want to update the information in your .SYM file, you
 have to do a full link with the /inc option; ILINK does not do this
 for you.


 792. Allowing Application Cleanup before the System Closes

 Question:

 How can an application clean up and terminate when Windows is
 terminated from the system menu of the MS-DOS Executive? This appears
 to be possible because applications such as Excel and Write ask if you
 wish to save any open files when Windows is terminated in this
 fashion.

 Response:

 Windows will send a WM_QUERYENDSESSION message to all running
 applications when you attempt to close the MS-DOS Executive. This
 message is sent to all windows to ask if Windows can be closed. If any
 window returns FALSE, the Windows session will not be ended. As soon
 as one window returns FALSE to this message, Windows stops sending
 this message, and then sends WM_ENDSESSION messages with wParam equal
 to zero to all windows that had received the WM_QUERYENDSESSION
 message. If all windows return TRUE to this message, a WM_ENDSESSION
 message with a nonzero wParam will be sent to all windows. When your
 window receives this message with a nonzero wParam, it is time to save
 any files or do any other cleanup necessary.


 793. Windows SDK: Determining Line Number within an Edit Control

 Problem:

 I am writing a program that deals with subclassing an edit box. I have
 a multiline edit box within a dialog box and I need to calculate the
 line number whenever one of the lines is clicked on by trapping the
 scroll-bar message; however, I can't get the correct thumb position
 when the message changes.

 Response:

 The line number of the line you click on within an edit control is
 most easily determined by using the EM_LINEFROMCHAR message. A sample
 application using this method can be found in the Software/Data
 Library by searching on the keyword EDITCNT, the Q number of
 this article, or S12255.


 794. Windows SDK: Windows Applications and the Math Coprocessor

 Question:

 How can I have my Windows application use in-line floating point
 instructions so that it will run as fast as possible when a math
 coprocessor is present?

 Response:

 By default, the C optimizing compiler will produce in-line floating
 point instructions, specifically the -FPi option. If an application
 was linked with WIN87EM.LIB and a math coprocessor was present in the
 system, it would perform floating point math using in-line floating
 point instructions. Otherwise, the emulator library would be used to
 evaluate the floating point expressions.

 If an application was developed to specifically use the coprocessor, a
 short assembly language routine could be linked in to speed up
 execution time. This assembly routine would remove all tests for the
 presence of a coprocessor at link time. When the application is
 executed, it would not spend time fixing up its code to use the
 coprocessor; it would use the coprocessor by default. This assembly
 language routine is documented in Section 7.3.1 of the "Microsoft C
 Optimizing Compiler for the MS-DOS Operating System User's Guide."

 However, attempting to run this modified Windows application on a
 machine that does not have a coprocessor present will cause problems.
 Specifically, when the Windows application attempts to do floating
 point math calculations, Windows immediately returns directly to DOS
 without producing any FatalExit codes.


 795. Windows SDK: Applications without Any Resources

 If you have an application that does not have any resource file, and
 therefore is not run through RC.EXE, any resources that it loads from
 a DLL will not be considered to be Version 2.00. This is because the
 RC compiler marks a version number in the EXE file. Therefore, in this
 case, if you have an ampersand (&) on a menu, it will not be converted
 to an underscore. To solve the problem, you will have to make a
 one-line RC file and run your application through it.

 This will be fixed in the next major release of Windows.


 796. Windows SDK: DLLs Must EXPORT Functions by Ordinal Number

 There is a bug in the Windows Int 3F (segment loader) handler. The bug
 is related to a reentrancy problem. The only time the Int 3F handler
 is reentered is if a call is made during low memory conditions to a
 nonpresent DLL function that was exported by name. This problem also
 can occur if that function was exported by ordinal, but the segment
 containing the function also contains a function exported by name.
 The problem also can occur if all functions in the segment getting
 loaded are exported by ordinal, but one of those functions calls a DLL
 function exported by name. The symptoms of this problem vary, but may
 include the DLL function never getting called or the stack pointer
 (sp) getting larger than the base pointer (bp).

 To avoid this problem, all functions exported by DLLs must be
 exported by ordinal, as in the following:

    EXPORTS
       HelloWndProc @1

 Exporting by number is faster and occupies less Windows memory than
 exporting by name. WIN87EM.LIB and WIN87EM.EXE shipped with the
 Windows 2.03 and 2.1 SDKs export functions by name rather than by
 ordinal. There are newer versions of these files in the Software
 Library in which the functions are exported by ordinal. The file
 WIN87EM can be found in the Software Library by searching on the
 keyword WIN87EM, the Q number of this article, or S12193. Please make
 sure you update both the LIB and EXE files.


 797. Using __ahincr in C Code

 There is no standard method for accessing an exported value from
 within a C application. However, it is possible to use __ahincr in C
 code in the following two ways:

 1. Write an assembler routine, similar to the example in the
    prerelease documentation, that returns __ahincr.

 2. Use the following code to get __ahincr:

    HANDLE hKernel;
    long FAR * lp_ahincr;
    WORD __ahincr;
    ...
    hKernel = LoadLibrary("kernel.exe");
    if(hKernel == NULL)
        __ahincr = 1;
    else{
        lp_ahincr = (long FAR *)GetProcAddress(hKernel,MAKELONG(114,0));
        __ahincr = LOWORD(lp_ahincr);
        FreeLibrary(hKernel);
    }

 Note: 114 is the ordinal of __ahincr, which can be obtained using
 exehdr.


 798. Windows SDK: RC Compiler: FATAL: Macro Expansion Too Big

 Problem:

 We tried to use a "chaining" technique to define the identifiers for
 strings that are located within a stringtable in our resource file.

 The identifiers are defined in an include file. By "chaining," I mean
 that the value of an identifier depends on the value of the previous
 identifier plus a predefined increment. The following is an example:

 #define  ID_START    100
 #define  ID_INC      1

 #define  ID1         ID_START
 #define  ID2         ID1 + ID_INC
 #define  ID3         ID2 + ID_INC
 #define  ID4         ID3 + ID_INC
 #define  ID5         ID4 + ID_INC
    :      :                     :
    :      :             :
   etc    etc           etc

 The advantage of this "chaining" technique is that when identifiers
 are added, it ensures that every identifier will be unique.

 Everything works fine when we have a small number of identifiers and
 strings. However, as identifiers and strings are added, eventually the
 resource compiler produces the following error message:

    ~rc1234.tmp(145) : FATAL : compiler limit : macro expansion too big
    EXEC Failed - make sure RCPP.EXE is installed correctly.

 This problem is occurring because the RC compiler does not do
 mathematical computation. The lines of code you gave would actually be
 expanded as follows:

 ======== Original code ========  =========== EXPANDED CODE ========
 #define  ID_START    100         100
 #define  ID_INC        1         1

 #define  ID1    IDSTART          100
 #define  ID2    ID1 + ID_INC     100 + 1
 #define  ID3    ID2 + ID_INC     100 + 1 + 1
 #define  ID4    ID3 + ID_INC     100 + 1 + 1 + 1

 etc.

 What is happening is that after so many lines, the expansion of the
 line is exceeding the buffer allotted for macro expansions. Since the
 RC compiler does not do arithmetic, it interprets all #defines as
 characters.


 799. WinSDK: Last Line Corrupted in Multiple Selection List Boxes

 There is a problem with Windows 2.03 and 2.10 multiselection list
 boxes that can cause the text in the last line of the list box to get
 corrupted. This problem occurs if there are not enough entries in the
 list box to fill the client area and you click the mouse in the area
 below the last item in the list box.

 Microsoft has confirmed this to be a problem in Versions 2.03 and
 2.10. We are researching this problem and will post new information
 as it becomes available.

 There is a sample application in the Software Library that shows how
 to work around this problem. This file can be found in the
 Software/Data Library by searching on the keyword MULTLIST, the Q
 number of this article, or S12256. MULTLIST was archived using the
 PKware file-compression utility.


 800. Windows SDK: Text and Background Colors Must Be Solid

 Background colors [which are set with SetBkColor()] and text colors
 [which are set with SetTextColor()] must be solid colors (achieved
 without dithering). When you call one of these functions, GDI calls
 GetNearestColor() on that color to get the nearest solid color. To
 find out what colors are solid on a given device, call
 GetDeviceCaps() with NUMCOLORS as the nIndex parameter. You then
 can call Escape(GETCOLORTABLE) with lpIndex values between 0 and the
 number of solid colors minus one to determine what the solid colors
 are.


 801. Windows SDK: OpenFile() with the Sharing and Inheritance Bits

 Question:

 Can I use the DOS INT 21h function 3Dh register AL bits with the
 Windows OpenFile() function?

 Response:

 When used with OF_CREATE set in wStyle, OpenFile() uses the 3Ch
 function and the AL flags are not used. For other OF_* styles,
 OpenFile() sets AH to 3Dh and AL to the low byte of the wStyle
 parameter so that the AL bits can be used.

 Note that the sharing bits require the DOS SHARE utility to be loaded.


 802. ALT+9 Keystrokes "Eaten" by Windows/286 Version 2.10

 When running DOS ("old" or "non-Windows") applications that use the
 BIOS keyboard services (INT 16H Function 0), ALT+9 is "eaten" (not
 passed along to the application) by Windows/286 Version 2.10 if you
 don't mark the application's .PIF file as "directly modifies
 keyboard."

 This problem does not occur in Windows/386 Version 2.10.

 The keystrokes ALT+1, ALT+2, ..., ALT+8 are correctly passed on to old
 applications; only ALT+9 fails to arrive.

 This is a problem in Windows' WINOLDAPP module, which is responsible
 for running non-Windows applications in the Windows environment, and
 allows users to switch to and from DOS applications.

 Microsoft has confirmed this to be a problem in Version 2.10 of
 Windows/286. We are researching this problem and will post new
 information as it becomes available.


 803. WM_CLOSE While Dialog Box Is Displayed Crashes Windows

 If a WM_CLOSE message arrives when an application-modal dialog box is
 displayed, Windows crashes (with a RIP 0007 in the debugging version).
 Also, if the system menu is currently displayed when the WM_CLOSE
 message arrives, the system crashes (with a RIP 01f0 in the debugging
 version).

 Microsoft is researching this problem and will post new information as
 it becomes available. An application shouldn't receive a WM_CLOSE
 message while modal dialog boxes (or menus) are displayed; therefore,
 this isn't normally a problem for applications.

 The problem can be demonstrated if you put up a dialog box and set a
 timer so that a WM_CLOSE will arrive while the dialog box is up. The
 following code fragment illustrates this:

     ...
     case IDSABOUT:
         SetTimer (hWnd,99,5000,NULL);
         DialogBox( hInst, MAKEINTRESOURCE(ABOUTBOX), hWnd, lpprocAbout );
         break;
     ...
     case WM_TIMER:
         KillTimer (hWnd,wParam);
         PostMessage (hWnd,WM_CLOSE,TRUE,0L);
         break;

 If you close the About box before the timer goes off, the application
 will terminate as expected. If you leave the About box up, Windows
 will crash with a RIP 0007. If you close the About box, but open the
 System Menu (and leave it open), Windows will crash with a RIP 01f0.


 804. Windows SDK: GetClientRect() Coordinates Are Not Inclusive

 Question:

 When calling GetClientRect(hWnd, &Rectangle), does the rectangle
 specify the coordinates of the top left and bottom right, inclusive?
 In other words, if I plot a vertical line on the right side of the
 rectangle using Rectangle.right as the x-coordinate, will I see it, or
 are Rectangle.right and Rectangle.bottom really the dimensions of the
 window?

 Response:

 The coordinates returned by GetClientRect() are not inclusive. If you
 want to draw a border around the edge of your client area, you need to
 draw it from the coordinates (Rectangle.left, Rectangle.top) to
 (Rectangle.right-1, Rectangle.bottom-1).


 805. Unhighlighting the Menu Bar When an Accelerator Is Pressed

 Question:

 I'm using an accelerator (^O) to open a file and bring up the window's
 "open file" dialog box. When a user presses CTRL+O, "File" on the menu
 bar remains highlighted until the dialog box is finally closed. On the
 other hand, when the mouse is used to select from the menu or the
 ALT+F O mnemonic is used, the highlighted "File" on the menu bar
 reverts to normal, the menu pulls down, and the dialog appears. How
 can I make "File" become unhighlighted when the dialog appears?

 Response:

 The following are two ways to cause the menu to be unhighlighted when
 a dialog is put up:

 1. In the accelerator definition in the .RC file, specify NOINVERT for
    that accelerator. This will cause the menu to not be highlighted at
    all.

 2. Call HiliteMenuItem() to unhighlight the menu. For the best effect,
    you would want to do this in your dialog function. The following
    code would go in the dialog function:

       ...
       case WM_INITDIALOG:
           hParentWnd = GetParent(hDlg);
           HiliteMenuItem(hParentWnd, GetMenu(hParentWnd),
                          DLG_MENU_ID, MF_UNHILITE);
           return(TRUE);
       ...

 Note: DLG_MENU_ID is the ID assigned to the menu item that was used in
 the accelerator definition.


 806. SetDlgItemText() Doesn't Generate an EN_CHANGE Message

 SetDlgItemText(), when called for an edit control, no longer sends a
 WM_COMMAND message with wParam equal to the control ID and lParam
 containing EN_CHANGE in the high-order word. This problem will be
 addressed in future versions of the prerelease.


 807. Windows SDK: RESETDEV Function Code in EscapeCommFunction()

 Question:

 In the communications function EscapeCommFunction(), when you request
 the RESETDEV function code, exactly what happens to state of the
 communications port (device)? To what state is the device reset? If
 this function is called when no communications errors are apparent,
 will it affect future communications functions on the indicated
 device?

 Response:

 The documentation is rather unclear on the RESETDEV case of
 EscapeCommFunction(). RESETDEV asserts the RESET line on an LPT port,
 thereby telling the printer or other device to reset itself. It has
 no effect on a serial port.


 808. Windows SDK: SHIFT+CTRL Keyboard Accelerators

 Question:

 How can keyboard accelerators be specified to operate only when BOTH
 the SHIFT and CTRL keys are held down? The resource compiler
 documentation (Page 54 of the "Microsoft Windows 2.00 Software
 Development Kit Update") gives notation such as the following:

    "A", IDM_CONTROL_SHIFT_A, SHIFT CONTROL

 This causes the RC compiler Version 2.10 to fail. If the syntax is
 altered to the following (with commas), then the accelerator compiles
 but only affects shifted "A" characters:

    "A", IDM_CONTROL_SHIFT_A, SHIFT, CONTROL

 The following syntax also seems to be incorrect:

    "^A", IDM_CONTROL_SHIFT_A, SHIFT

 This last combination results in the accelerator being invoked with
 only a CTRL+A.

 Response:

 The correct syntax is as follows:

    "A", IDM_CONTROL_SHIFT_A, VIRTKEY, SHIFT, CONTROL
                              ^^^^^^^
 The VIRTKEY is necessary to correctly catch the SHIFTs and CONTROLs.


 809. Windows SDK: PaintRgn and FillRgn Do Not Work in a Metafile

 PaintRgn and FillRgn do not work inside a metafile in Windows
 Versions 2.03 and 2.10. They will be supported in the next major
 release of Windows.


 810. Using DestroyMenu() on Currently Assigned Menu Handle

 When you call DestroyMenu() you should always make sure that it is not
 the current menu associated with the window, [i.e., the handle that
 has currently been assigned to the window through a call to
 SetMenu(hWnd, hMenu) or a menu currently associated with a window
 because it is a copy of the class menu or the menu given in the
 CreateWindow() call]. If you call DestroyMenu() while the window is
 still around, you will have a menu defined for the window that is
 invalid. In addition, Windows makes a similar call to DestroyMenu()
 when the window is destroyed. If you have invalidated the handle
 through a call to DestroyMenu(), there will be problems when Windows
 makes its similar call to DestroyMenu() using the invalidated handle.


 811. DestroyMenu() Frees Memory Associated with Submenus

 Question:

 Does DestroyMenu() destroy all submenus attached to a menu, or do I
 have to destroy them individually?

 Response:

 Destroying the handle to the main menu will subsequently free all
 memory associated with the submenus (pop-ups) that are related to
 the main menu.

 The following is an example:

 1. Create a handle to a menu called "hmenu", as follows:

       hmenu = CreateMenu();

 2. Create a handle to a group of submenus called "hMenuPopup", as
    follows:

       hMenuPopup = CreateMenu();

 3. Create the submenus, as follows:

       ChangeMenu (hMenuPopup, NULL, "&New",    IDM_NEW,     MF_APPEND);
       ChangeMenu (hMenuPopup, NULL, "&Save",   IDM_SAVE,    MF_APPEND);
       ChangeMenu (hMenuPopup, NULL, "&Save As, IDM_SAVE_AS, MF_APPEND);

 4. Add to the main menu, as follows:

       ChangeMenu (hMenu, NULL, "&File", hMenuPopup, MF_APPEND | MF_POPUP);

 5. Later, when you don't need the menu, call DestroyMenu():

       DestroyMenu(hMenu);

 The memory allocated for both the main menu and the submenus will be
 freed.


 812. Developing Common Code for Both Windows and Macintosh

 Question:

 Are there any products that can run a Windows program on a Macintosh
 either by recompiling or using an interpreter (perhaps something
 similar to MicroGrafx's Mirrors for the Mac)?

 Response:

 We are not aware of any kind of Windows-to-Macintosh interpreter or
 translator that will allow you to directly run Windows code on the
 Macintosh, but there are some alternatives:

 1. The January 1989 issue of the "Microsoft Systems Journal" contains
    two relevant articles titled "Developing Applications with Common
    Source Code for Multiple Environments" and "Porting Apple Macintosh
    Applications to the Microsoft Windows Environment."

 2. The "Windows Developer Guidebook" [available by calling (800)
    426-9600] contains a listing for a product that helps you develop
    code in an environment-independent way. The product is called XVT
    (Extensible Virtual Toolkit) for Microsoft Windows, Presentation
    Manager, and Macintosh. XVT is made by the following company:

       GSS (Graphical Software Systems)
       9590 SW Gemini Drive
       Beaverton, OR  97005
       (503) 643-8642

    According to GSS, "XVT allows developers to create and maintain one
    set of source code and port it to Windows, PM, and Macintosh
    environments."

 3. A third-party product called CommonView contains Glockenspiel C++
    object libraries to simplify creation of applications for Windows
    and OS/2 Presentation Manager (PM) using the same core source code.
    Contact the company to find out more about porting to the Macintosh.

    CommonView is a set of class libraries for creating MS Windows and
    Presentation Manager applications. They come with the C++ compiler,
    which is an object-oriented version of C. They are actually
    translators that translate C++ code into C code, which you can then
    run through your C compiler. You can write an application using
    CommonView, and it will isolate you from the particular Windows
    environment. A demonstration of CommonView is available on Compuserve
    in LIB 6 (windows). The product is distributed by Imagesoft, (800)
    245-8840 or (516) 767-2233.

 XVT and CommonView are manufactured by vendors independent of
 Microsoft; we make no warranty, implied or otherwise, regarding these
 products' performance or reliability.


 813. Windows SDK: Landscape Versus Portrait Mode

 The printer driver considers the page to be in Landscape mode when you
 use the Escape(..., GETPHYSPAGESIZE, ...) function, if the page width
 is greater than the page length.


 814. Releasing Control with QueryAbort() in a Printer Driver

 QueryAbort() is used by a printer device driver to release control to
 other applications and allow any abort dialog boxes to respond to
 input. It does this by calling the application's abort procedure that
 was set via the SETABORTPROC escape. The abort procedure calls
 PeekMessage(). If no abort procedure was set by the application,
 QueryAbort() returns TRUE. This has the same effect as the application
 calling its own abort procedure or the spooler calling the abort
 procedure.

 If a driver needs to make sure the abort dialog responds and that
 other applications keep running during any particularly long
 operation, it can call QueryAbort() at regular intervals. Every
 quarter or half second is a reasonable interval. Calling QueryAbort()
 is relatively inexpensive, but may take time if some other application
 is slow in relinquishing control.

 QueryAbort() has the same parameters and return values as the
 application's abort procedure. The driver should call it with the hDC
 passed in the SETABORTPROC escape, and zero if there is no error
 condition. One of the SP_* flags defined in WINDOWS.H may be passed if
 there has been an error. If zero is returned, the driver should fail
 its current and subsequent operations until QueryAbort() returns a
 nonzero value.

 The declaration of QueryAbort() is as follows:

     short FAR  PASCAL QueryAbort(HDC, short);


 815. Windows SDK: Creating a Dialog Box the Size of the Window

 Question:

 Is there any way to determine the size of a dialog box before it is
 opened? I want to create a main window that exactly contains a
 modeless dialog box. Is there an easy way to control this size?

 Response:

 In the example below, hWnd and r have been declared as global in
 scope, as follows:

    RECT  r;
    HWND  hWnd;

 You need to add the following code to your dialog routine:

       case WM_INITDIALOG:
           GetWindowRect(hWnd,(LPRECT)&r);
           MoveWindow(hDlg, r.left,r.top,
                    r.right-r.left,r.bottom-r.top, TRUE );
         break;

 This routine will cause the dialog box to appear on the screen,
 equivalent in size to the window specified by hWnd.


 816. Handling WINDOWS.H Redefinition of min() and max()

 When "including" some C include files, such as STDLIB.H, a warning
 message similar to the following is issued during compilation of a
 Windows application:

    C:/C/INCLUDE/windows.h(107) : warning C4005: 'max' : redefinition
    C:/C/INCLUDE/windows.h(108) : warning C4005: 'min' : redefinition

 These compiler warning messages are produced because the 'min' and
 'max' macros are #defined in both WINDOWS.H and the C header file(s).
 You can eliminate the warning messages by compiling with the -D switch
 (-DNOMINMAX) or inserting the following line anywhere before the
 inclusion of WINDOWS.H:

    #define NOMINMAX


 817. Different Rectangles from GetUpdateRect() and BeginPaint()

 The "Microsoft Windows Software Development Kit Programmer's
 Reference" for Versions 2.03 and 2.10 states that the rectangle
 retrieved by GetUpdateRect() (described on Page 313) is identical to
 that retrieved by BeginPaint() (described on Page 156). There is a
 case where these rectangles are different. If you have two windows
 that overlap and the focus is shifted from the window on the top to
 the window on the bottom, then GetUpdateRect() identifies the entire
 window as invalid and BeginPaint() recognizes only the portion of the
 window that is overlapped.

 Microsoft is researching this problem and will post new information as
 it becomes available.


 818. Message Box Style MB_SYSTEMMODAL Does Not Support /n

 Problem:

 When I am using the style MB_SYSTEMMODAL in my message box, the /n
 character, which I use to break the line, is ignored. Inserting the /n
 into the string breaks the line at the /n when I am using any other
 style.

 Response:

 The message box style MB_SYSTEMMODAL is designed to allow a message
 box to function under low memory conditions. For this reason, the
 style is only capable of supporting a single line of text. Therefore,
 it is not possible to break a line by inserting a carriage return into
 the middle of the string when using the MB_SYSTEMMODAL style.


 819. Windows SDK: Edit Controls Ignore EM_SCROLL Message

 Problem:

 Edit controls don't recognize the EM_SCROLL message. Therefore, I
 can't send EM_GETTHUMB in the wParam of the EM_SCROLL message to get
 the current thumb position.

 Response:

 Use the GetScrollPos() function to get the thumb position of the edit
 control's scroll bar, as in the following example:

    nScrollPos = GetScrollPos(hWndEditControl, SB_VERT);


 820. Line Continuation and Carriage Returns in String Tables

 Problem:

 When using a string table, I would like to continue long lines on the
 following line, but not force a carriage return, as in the following
 example:

 STRINGTABLE
 BEGIN
 IDSLONGSTRING,"This is a long line of text so I would like -
 to continue it on the next line."
 END

 Also, I would like to force a carriage return into the middle  of the
 string, as follows:

    This is a long line of text so I would like
    to force a carriage return.

 Response:

 The RC compiler does not offer a line-continuation symbol for strings
 in string tables.

 To force a carriage return into a long line of text, use one of the
 methods described below.

 One method is to force the carriage return using \012\015. The
 following example demonstrates the use of \012\015 and should be
 considered to be on one continuous line:

 STRINGTABLE
 BEGIN
 IDSLONGSTRING, "This is a long line of text so I would like \012\015 to force
 a carriage return."
 END

 For more information on this method, query in the KnowledgeBase on
 the following keywords:

    STRINGTABLE WinLoadString()

 Another method of forcing a carriage return is to press ENTER and
 continue the line on the next line. The following example will force
 a carriage return after the word "like."

 STRINGTABLE
 BEGIN
 IDSLONGSTRING, "This is a long line of text so I would like
 to force a carriage return"
 END

 If you try to use the \n or other \ characters, the RC compiler will
 ignore them.

 Note: There is a 255-character limit (per string) in a string table.
 For more information on this limit, query in the KnowledgeBase on the
 following keywords:

    STRINGTABLE length 255


 821. Difference between LocalDiscard() and LocalFree()

 There is a difference between freeing a block and discarding it. When
 you discard a local block, its content is removed from the local heap,
 but its handle remains valid. In contrast, when you free a local
 block, not only is its content removed from the local heap, but its
 handle is removed from the table of valid local memory handles. The
 Windows functions LocalDiscard() and LocalFree() let you discard or
 free blocks. (A local memory block can be discarded or freed only if
 there are no outstanding locks on it.)

 The reason you may want to discard a block rather than free it is that
 you may want to reuse its handle. To reuse the handle, call
 LocalREAlloc() with a nonzero size value. By reusing the handle in
 this way, you save Windows the effort otherwise spent freeing an old
 handle and creating a new one.


 822. Windows SDK: WM_TIMER Messages

 Timers in the Windows environment are not synchronous, i.e., when a
 timer "goes off," Windows does not stop processing and send a WM_TIMER
 message or call the timer event procedure. Instead, Windows sets some
 bits internally, to ensure that the task that "owns" the timer will
 run (as soon as the current task does something to yield control).
 When this task does eventually run and eventually calls GetMessage()
 or PeekMessage(), and there are no other messages available for this
 task, Windows will scan the timer list to see if any timers for this
 task have "gone off." If they have, Windows returns a WM_TIMER message
 or calls the timer event procedure. The following are two important
 facts that you must recognize:

 1. The WM_TIMER message and timer-event process happen ONLY when an
    application is sitting on a GetMessage() or PeekMessage(). They do
    not happen at timer interrupt time.

 2. Only timers for the task that is currently active are scanned when
    looking for messages. A PARTICULAR TIMER IS OWNED BY THE TASK THAT
    WAS ACTIVE WHEN THE TIMER WAS CREATED.

 The following is an example of the incorrect use of timers:

 An application sets a keyboard hook. Inside the keyboard hook, a timer
 is set. It is important to realize that the keyboard hook is called
 from within GetMessage() or PeekMessage() whenever a task is pulling
 keys out of the system queue (the place where keyboard and mouse
 events go at interrupt level). The task that is active at the time the
 keyboard hook is called is completely random. Basically, it is the
 task that owns the windows with the focus.

 Because a timer is owned by the active task at the time it is created,
 and any task may be the active task when the keyboard hook is called,
 the timer created in this manner will be owned by some random task.
 This, combined with the following facts, result in inconsistent,
 random behavior for this timer.

 1. Timers are only scanned for the active task.

 2. Timers are retrieved at message level.

 3. All applications handle messages differently.

 The basic rule is that you MUST NOT create a timer from within a hook
 such as a keyboard hook.


 823. Windows SDK: fabs Function Does Not Work in a DLL

 The fabs C run-time function does not work in a dynamic link library
 (DLL). Instead, you should use the following:

   float MyFabs(float f)
   {
     if (f < 0.0)
       return (f * -1.0);
     else
       return f;
   }


 824. EN_UPDATE Notification Code Only for Multiline Edit Controls

 The EN_CHANGE notification code is only sent to the parents of
 ES_MULTILINE edit controls in Windows Versions 2.03 and 2.10. This
 notification code is not sent to the parents of single-line edit
 controls.


 825. First Paragraph of DGROUP Reserved for Windows' Use

 Windows reserves the first paragraph of every DS (data segment, or
 DGROUP). This space is used for a number of things, including the
 local heap pointers at DS:6 and DS:7. Any application or library with
 a data segment MUST reserve the first 16 bytes for Windows (the
 Microsoft C Compiler does this automatically).


 826. Beta: Note to Users of the OPCPROJ Alias

 **********************************************************************
   CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL
   CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL BETA CONFIDENTIAL
 **********************************************************************

 Congratulations! You've made it to the OPCPROJ section of the OnLine
 KnowledgeBase. Your SPB54 account number gives you access to all
 OPCPROJ articles, as well as articles in the Windows 3.00 Pre-Release
 Program and regular Windows 2.x articles.

 To find articles dealing with OPCPROJ issues, query on the words
 "opcproj" and/or "mps".

 To find articles dealing with the Windows Pre-Release Program, query
 on "beta and prod(winsdk)" (this will also give you the OPCPROJ
 articles).


 827. Windows 2.11 File Locations Incorrect For MKDEBUG

 If MKDEBUG is invoked to install the debugging version of Windows/286
 from a 3.5-inch set of Windows/286 Version 2.11 disks, the SETUP
 program continually asks for the Setup and Build disk.

 Locations of the files defined in the SETUP286.INF file used by the
 MKDEBUG.BAT supplied with the Windows SDK (Software Development Kit)
 Version 2.10 are different for the Microsoft Windows/286 Version 2.11
 3.5-inch disks.

 To work around this problem, modify the SETUP286.INF file located in
 the \DEBUG directory of the Windows SDK Development Files 1 disk as
 follows, after making a backup:

 1. Locate the following line in the [windows] section of the
    SETUP286.INF file:

       (1:MSDOS.EXE)

    Change it to the following:

       (2:MSDOS.EXE)

 2. Locate the following line in the [display] section of the
    SETUP286.INF file:

       (2:IBMPS250.DRV,"VGA","100,96,96","7")

    Change it to the following:

       (1:IBMPS250.DRV,"VGA","100,96,96","7")

 It is best to also replace the KERNEL.EXE and KERNEL.SYM files in the
 DEBUG subdirectory with the Version 2.11 debugging kernel files. These
 files can be found in the Software/Data Library by searching for the
 keyword DEBUG211, the Q number of this article, or S12261. DEBUG211
 was archived using the PKware file-compression utility.


 828. .MAP Files for Debugging Version of Windows Version 2.10D

 The .MAP files for KERNEL, USERS, USERF, and GDI are built for the
 debugging version of Windows 2.10D.

 These .MAP files can be found in the Software/Data Library by
 searching on the keyword WINMAP21, the Q number of this article, or
 S12316. WINMAP21 was archived using the PKware file-compression
 utility.


 829. Windows SDK: LINK4 Error L2022: Export Undefined

 The following are two possible causes of the L2022 link error. If this
 error occurs, the exact error message that LINK4 produces should be
 checked. Linking with the /INF[ORMATION] switch provides some
 additional information that may be helpful in determining the cause of
 this error message. The following are possible causes of this error:

 1. A misspelled label of a function that is in the application.

    This is an obvious error that is solved easily by correcting the
    spelling mistake.

 2. Unprintable characters that appear at the end of the EXPORTS
    statement in the module definition (.DEF) file.

    This error is caused by unprintable characters in a bad .DEF file.
    These characters can be interpreted as a valid EXPORT label if the
    EXPORT statement occurs as the last statement in the .DEF file.
    This error will produce link error L2029 with a blank list of
    unresolved externals.

    The solution is to clean up the .DEF file to remove the unwanted
    characters.

 Link error L2022 is mentioned in Section 11 of the "Microsoft CodeView
 and Utilities Update" manual on Page 77.


 830. Windows SDK: Maximum Memory from GlobalAlloc()

 The maximum amount of global memory that GlobalAlloc() can allocate
 depends on how much memory can be allocated in one contiguous block
 after discarding and moving memory. GlobalCompact(-1) returns this
 maximum amount.

 This limit is the same, regardless of whether or not LIM 4.0 expanded
 memory is present in the system. If an application needs more memory
 than what GlobalAlloc() can offer, and expanded memory is available,
 an application can make LIM 3.2 calls to allocate any extra memory it
 requires. If these calls are present in the application, the
 application is responsible for managing all LIM 3.2 accessed memory
 with LIM 3.2 EMS calls. An application using LIM 3.2 calls should
 invoke RC.EXE with the -LIM32 switch.

 Note: If more than 64K is locked, the return value from GlobalLock()
 must be cast to a huge pointer and assigned to a huge pointer
 variable. The following is a sample code fragment:

    GLOBALHANDLE  hGMem;
    char huge     *lpGMem;

    hGMem = GlobalAlloc(GMEM_MOVEABLE, 0x30000L);
    lpGMem = (char huge *)GlobalLock(hGMem);

 An excellent reference for Windows global memory management function
 information is Chapter 8 of Charles Petzold's "Programming Windows"
 book, especially Pages 305-314.

 For information on LIM EMS calls, contact Intel at 1 (800) 538-3373
 and ask for the Lotus/Intel/Microsoft (LIM) Expanded Memory
 Specification (EMS) Version 4.0.


 831. Windows SDK: "TxDelay" Functionality and Unit Measurement

 The "TxDelay" element in the DCB structure is a field reserved for
 future expansion. It is not implemented in any version of the Windows
 communications driver (COMM.DRV) included with Windows Versions 2.03,
 2.10, or 2.11.

 If you want this element implemented, you need to request a Windows
 Device Driver Kit (DDK), which will supply you with the necessary
 information on making a COMM device driver of your own. The unit
 measure for this field will depend on the implementation of the new
 serial communication device driver.


 832. Windows Interrupt Driven I/O Device Drivers

 Question:

 Is it possible to write an interrupt-driven device driver under
 Windows for devices other than the standard displays, ports, and
 printers? Will the device driver perform better written as a DOS TSR
 or as a Windows DLL? Will the same techniques work under both
 Windows/286 and Windows/386?

 Response:

 Most drivers that can be written as DOS TSRs can also be written as
 Windows DLLs. The Windows version will be at least as fast as the DOS
 version, and in most cases will be faster.

 The same method works under both Windows/286 and Windows/386, unless
 the device contains its own on-board DMA controller. Please refer to
 the section on Windows/386 considerations below.

 The primary requirement for an interrupt-driven device driver is
 fixed, resident code and data segments for use by the interrupt
 service routine. Windows DLLs can provide these segments. Both DLL
 FIXED code and FIXED data segments are loaded below the EMS bank line
 and are never moved or discarded.

 The second requirement is a method of notifying the application of an
 event completion, or of incoming data. Because the state of the
 application's memory objects is uncertain at interrupt time, neither a
 TSR nor a DLL can call any routine in the application at interrupt
 time. The solution is to use a combination of PostMessage() and timers
 to notify the application. Because the scheduler is nonpreemptive, and
 applications are scheduled whenever they have any messages and other
 applications have released control, posting a message is as fast as
 any method of communicating with an application.

 Algorithm
 ---------

 The general algorithm is to have the DLL maintain a circular queue of
 incoming events and provide a routine to the application for querying
 and removing items from the queue. Whenever the application queries
 the queue, the DLL sets a flag on the queue. When an event occurs, the
 DLL checks the flag. If the flag is set, the DLL posts a message to
 the application and clears the flag. This causes the application to
 get a message whenever an event has occurred since the application
 last checked the queue. Whenever the application receives a queue
 message, it should check for multiple queue events because additional
 events may have occurred since the message was posted.

 This would all be fine as it stands, except for a limitation in
 PostMessage(). In order to be callable at interrupt time,
 PostMessage() uses a fixed-length message queue, which is subject to
 overflow. Hence, PostMessage() may fail. This is a rare occurrence,
 but the driver must take it into account. The solution is to add a
 low-frequency timer (between 0.5 and 1.0 seconds, approximately) to
 the application. Whenever the application receives this timer, it
 should query the DLL for events in the same way it would if it had
 received a posted queue message. Using this algorithm, the application
 will usually handle events as fast as they are scheduled, but may
 occasionally lag by the interval of the backup timer.

 Performance Issues
 ------------------

 The Windows DLL method is guaranteed to be as least as fast as an
 MS-DOS TSR. Both will retrieve data from the driver's buffer only when
 the application is scheduled, but the Windows DLL method has less of a
 negative impact on memory management and possible thrashing. While the
 MS-DOS TSR must always be memory resident, only the interrupt portions
 of the DLL need to be resident. The application callable portions can
 be movable and/or discardable.

 The Windows DLL implementation will cause the application to be
 scheduled at the next next slot available to it, as soon as that
 occurs. It only requires a low-frequency timer as a backup in the
 event a posted message is lost. The TSR implementation requires the
 use of a high-frequency timer to achieve the same result. A
 high-frequency timer will degrade system throughput more than will a
 low-frequency timer.

 Both methods retrieve data from the device at interrupt time. Both
 methods are restricted because of fixed buffer sizes and must be able
 to stop the data flow or allow retransmission of data lost because of
 overruns.

 Notes
 -----

 The DLL's interrupt service routine can't directly or indirectly call
 the Windows memory manager because the memory manager isn't reentrant
 and the interrupt may have occurred during a memory manager operation.
 This implies that calls to LocalAlloc(), GlobalAlloc(), and related
 routines and calls to any routine in a moveable or discardable segment
 are disallowed from the interrupt service routine.

 The DLL can use two methods for allocating its queue and pointers. The
 DLL can allocate the data space at compile/link time so that its data
 segment will be allocated its full size and will never grow, or it can
 allocate the memory with GlobalAlloc(GMEM_FIXED | GMEM_NOT_BANKED, ...)
 in an initialization routine called by the application. Using
 GlobalAlloc() allows the application to control the queue size.

 The DLL will need to protect its own queue from reentrance by the
 application query routine and the interrupt routine. This can be done
 by disabling interrupts with cli/sti instructions while the
 application updates the queue pointers, or with a combination of flags
 (semaphores) and disabled interrupts.

 The DLL doesn't need to worry about protecting PostMessage() from
 being reentered because the internal routine WriteMessage() is
 protected by cli/sti instructions.

 Please refer to the source of the COMM driver supplied in the Windows
 Device Driver Kit (DDK) for an example of how to write an interrupt
 driven device driver.

 Windows/386 Considerations
 --------------------------

 This method assumes that only the Windows virtual machine will be
 communicating with the device, and that no other virtual machine
 should receive interrupts from the device. To achieve this, the
 programmable interrupt controller must have that interrupt masked off
 at the time Windows/386 is loaded. When the Windows DLL initializes
 the device, it should then unmask the interrupt and hook the interrupt
 vector. This method ensures that all interrupts from the device will
 come to the Windows DLL.

 The DLL should remask the interrupt when the application is through
 with the device so that Windows/386 can be exited and reentered. A
 simple DOS application that masks the interrupt may be written as a
 debugging aid. This application can be run from DOS before Windows/386
 is entered.

 The interrupt will be serviced immediately, unless there is another
 interrupt being serviced by another virtual machine, in which case
 there will be a delay until that interrupt is complete.

 The posted message will be available to be serviced when the Windows
 virtual machine gets its time slice and becomes active.

 If the device uses one of the motherboard DMA controllers, Windows/386
 will correctly virtualize the DMA addresses. However, if the device
 contains its own DMA controller, special precautions must be taken. In
 this case, a DOS TSR must be loaded before Windows/386 so that it can
 allocate a buffer below Windows/386 where real addresses are identical
 to virtual addresses. If the TSR is loaded before Windows/386, any
 space it allocates will be suitable for DMA. The TSR must then be
 polled by the Windows application.

 For the Windows virtual machine to run and handle the messages posted
 by the DLL, there must be no exclusive mode DOS machines running. If
 there are any, the Windows virtual machine will be frozen until the
 user terminates or switches away from the exclusive machine.


 833. Getting Your Application Ready for Retail Release

 Below is a list of steps to take when moving your application from
 DEBUGGING mode to RETAIL release.

 Code File Changes
 -----------------

 1. Change the MAKE file compile line from something such as the
    following

       cl -W2  -Od -FPa -Zpei myapp.c

    to

       cl -W2  -Os -Fpe -Zpe  myapp.c

    The -Zi will not put in line information for CVW and the -FPe will
    use the floating point coprocessor if it is present. You can use
    the -FPa switch during debugging so that you don't get any Fatal
    Exit 409s. For more information, refer to other articles in this
    knowledge base on this topic.

 2. Remove the /co switch from the LINK4 line.

 3. Remove any debugging code, or flip any debugging switches you may
    have in your application off.

 4. When moving from the -FPa to -FPe floating point options, you must
    supply the WIN87EM.EXE file with your application and link to
    WIN87EM.LIB.

 The User Interface
 ------------------

 1. Check all windows and dialog boxes to make sure they fit on all
    display screens. The CGA driver is the lowest resolution screen
    supported by Windows.

 2. Because the CGA will support only two colors, make sure that the
    colors you use do not get mapped to black on black or white on
    white.

 3. Make sure that your application will work without the mouse, making
    sure that the keyboard interface is all set up,  especially in
    dialog boxes.

 4. Check all fonts used for spacing.

 In the README.WRI file that comes on your Windows Development
 Utilities disk, Section 6, "Performing a Slow Boot," explains how to
 easily change your windows configuration to test with different
 devices.

 Documentation
 -------------

 To make screen shots of your application for the manual, you can use
 the SNAP.EXE program supplied with the Windows Software Development
 Kit (SDK). Once you take a "snapshot" of your window, it will be
 placed on the clipboard. Then, paste the "snapshot" into the Write
 application and print it from there.


 834. CodeView Error: Invalid Executable - Please Relink

 If you receive the error message "Invalid Executable - Please Relink,"
 CodeView will exit and you won't be able to debug the application. The
 following are some known causes for this error:

 1. Using the alloc_text pragma. C Version 5.10 generates invalid
    CodeView information when this pragma is used. Early prerelease
    versions of CVW will produce this error message if the executable
    contains segments with this problem.

    This message will not be generated in the 1.52 and later versions
    of the prerelease. However, you will not be able to debug the modules
    containing the alloc_text pragma.

    C 6.0 generates correct Codeview information when using the
    alloc_text pragma. Therefore, if you use C 6.0, you will be able to
    use this pragma and debug correctly.

 2. Attempting to debug a non-Windows application. When you first start
    CVW, you provide the name of the application to debug. If you give
    the name of a standard DOS application, you will receive this
    message. This is to be expected.

 3. Attempting to debug an un-MARKed Windows 2.x application. When you
    first start CVW and provide the name of an un-MARKed Windows 2.x
    application, a dialog box is displayed with the following message:

       The Windows 2 file ... that you are about to run has not been
       marked as being compatible with the standard memory management
       features of Windows 3.0...

    If you click the Cancel button, you will receive the error message
    and you won't be able to debug the application. If you click OK,
    you will be able to debug successfully. This is to be expected.

 4. Attempting to debug a Windows application or DLL that hasn't been
    run through the resource compiler. You need to add a line to the
    .MAK file, after the link line, to run the .EXE through RC. This
    is true even if you have no resources for the application or DLL.

 These are the cases we have encountered so far. Microsoft is
 researching this problem and will post new information here as it
 becomes available.


 835. Windows SDK: Using Shared Memory between Applications

 When using DDE to transfer data between two applications, you must
 always remember that all shared memory objects in DDE are to be
 treated as read only by the recipients. This is stated on Page 118 of
 the "Microsoft Windows Software Development Kit Windows Extensions"
 manual, in the DDE section.

 These guidelines were created to ensure that your application will
 work correctly when using expanded memory, since the global memory
 objects of the application will be bank switched out when the
 application is no longer the current task. The following steps should
 be applied to transferring data between applications where Application
 A and Application B are two applications communicating via DDE.

 Application A
 -------------

 hGlobalA = GlobalAlloc(..,GMEM_DDESHARE,....)
 hPointerA = GlobalLock(hGlobalA)
 Write information to Memory specified by hPointerA
 GlobalUnlock(hGlobalA)
 PostMessage(...,hGlobalA,...)

 Application B
 -------------

 hGlobalB = GlobalAlloc(..,GMEM_DDESHARE,...)
 hPointerB = GlobalLock(hGlobalB)
 hPointerA = GlobalLock(hGlobalA)
 Copy memory in Global Area for application A into Global memory are
 for application B.
 Modify item in memory area specified by hPointerB.
 GlobalUnlock(hGlobalB)
 GlobalUnlock(hGlobalA)
 GlobalFree(hGlobalA)
 PostMessage(...,hGlobalA,...)

 The main point of this example is to illustrate the necessity for
 copying the data in the Global Area created by A into a Global Area
 created by B, then modifying the information in Global Area B and
 passing the handle to B's Global Memory area back to A (rather than
 assuming that the handle to A's global memory will reflect the changes
 made by Application B, which is not the case).

 You cannot pass a Global Memory handle to Application B, modify the
 memory using that Global Handle, and then have A use the same handle
 to retrieve the updated information. The global object created by
 Application A and copied to B's banks is not copied back to A's banks.
 A still has the original object (not modified) residing in its banks.


 836. Windows SDK: GetCurrentTime() and GetTickCount() are Identical

 Question:

 What is the difference between the functions GetCurrentTime() and
 GetTickCount()?

 Response:

 The functions GetCurrentTime() and GetTickCount() are identical. Both
 return the amount of milliseconds (+/- 55 milliseconds) since the user
 started Windows, and both are declared in WINDOWS.H as DWORDs.

 To test these two functions, use the following code:

 DWORD dwTickCount;
 DWORD dwCurrTime;
 char szCurrTime[50];

 dwTickCount = GetTickCount();
 dwCurrTime = GetCurrentTime();

 sprintf (szCurrTime, "Current Time = %lu\n
          Ticktime = %lu",dwCurrTime,dwTickCount);

 MessageBox(hWnd,szCurrTime,"Times",MB_OK);


 837. Windows SDK: Using GlobalFlags() to Test for GMEM_DISCARDABLE

 When testing for GMEM_DISCARDABLE, do not use the following:

    if(GlobalFlags(hMem) == GMEM_DISCARDABLE)

 Instead, use the following:

    if(GlobalFlags(hMem) & GMEM_DISCARDABLE)

 This is necessary because GlobalFlags() returns a one, i.e., 0x0100,
 rather than 0x0F00 if the object is discardable. It is also possible
 that the return value could be 0x0800 or 0x0400 or 0x0200. These cases
 are also taken care of with the bitwise AND.


 838. Passing Numbers to a Named Range in Excel through DDE

 Question:

 Using DDE, I need to place a set of numbers into a named range within
 an Excel spreadsheet. I can get single numbers into single cells or a
 number into the first cell in a named range, but how do I get an array
 of numbers into a named range?

 Response:

 To send an array to Excel via DDE, you must send the array in TEXT,
 CSV, or BIFF clipboard format.

 For example, if you want to send three numbers for one ROW in CSV
 format, use 1,2,3 (where each number is separated by a comma). If you
 want to place three numbers in one COLUMN in CSV format, place the
 CR/LF (Od 0a) (carriage return/line feed) characters after each number
 in the set.

 If you would like to send the numbers via the TEXT format separated into
 columns, place the CR/LF characters between each number in the set. To
 organize the numbers into one row, place a TAB (09) character between
 each number.


 839. Creating the Focus Marker for Any Standard Control

 Question:

 How can I draw the dotted lines that indicate that a particular
 standard control (push button, radio control, etc.) has the focus? Can
 I also get information on how Excel draws its gridlines or borders for
 each cell?

 Response:

 To accomplish this, first select the gray brush, then do a series of
 PatBlt()s with a ROP value of PATINVERT. The following example
 demonstrates how to do this via the paint routine in the Hello
 application:

 void HelloPaint( hDC )
 HDC hDC;
 {
   RECT  rB;

   rB.left    =  10;
   rB.top     =  10;
   rB.bottom  =  24;
   rB.right   = 126;

   SelectObject( hDC, GetStockObject(GRAY_BRUSH) );

   TextOut( hDC, rB.left+2, rB.top, szMessage, MessageLength );

   PatBlt(hDC, rB.left,  rB.top,    rB.right-rB.left,    1, PATINVERT);
   PatBlt(hDC, rB.right, rB.top,    1,   rB.bottom-rB.top,  PATINVERT);
   PatBlt(hDC, rB.right, rB.bottom, -(rB.right-rB.left), 1, PATINVERT);
   PatBlt(hDC, rB.left,  rB.bottom, 1,-(rB.bottom-rB.top),  PATINVERT);
 }


 840. Windows SDK: How to Put Vertical Separators in Menu Bar

 Question:

 I know that in a vertical menu, you can use the word SEPARATOR to
 create a horizontal bar separating the menu items. Is there a way in a
 horizontal menu to draw a vertical line to separate menu items? I
 would like to use a menu item that is two words (such as "Change
 Font") in a horizontal menu. I would also like to separate it from the
 other words with a vertical line, as in the following example:

    Print | Change Font | Zoom

 Response:

 Use the HELP option described on Page 38 of the "Microsoft Windows
 Programming Tools" manual (resource compiler).

 The HELP option is incorrectly documented as saying it places the item
 "flush right on the menu bar with a vertical separator to its left."
 In fact, it just puts the vertical bar to its left but doesn't move
 the item's position on the menu bar. You can use the HELP option in
 both MENUITEM statements and POPUP statements, as follows:

 MENUITEM "&Alpha", IDM_ALPHA, HELP
 POPUP    "&Beta", HELP
 BEGIN
         ...
 END


 841. Windows SDK: DLL Entry Point Not Being Called

 Problem:

 My DLL's entry point used for initialization is not called if it is
 loaded, double-clicked from the MS-DOS Executive, and if large frame
 EMS memory is available.

 Response:

 DLLs were not designed to be the first item started from the MS-DOS
 Executive. The only workaround to this problem is to have an
 application that loads the DLL, or to put some application such as
 CLOCK or CALC on the "load=" line in the WIN.INI file.


 842. Custom EGA Fonts under Windows/386

 Problem:

 I am downloading my own custom font to my EGA. When I run my DOS
 application full screen under Windows/386 and then use ALT+ESC to
 return to this application from another one, Windows doesn't restore
 the previous font. Sending a refresh command to the application causes
 it to use its own font again, but it would be nice not to have to do
 this.

 Further, when I run my DOS application in a Window, I do not get my
 custom font at all.

 Response:

 These features currently are not supported under Windows/386. The EGA
 base font is restored automatically, but custom fonts are not.

 You can change the EGA font used in a DOS window by changing EGA80.FON
 on the Windows/386 setup disks (make copies first) and then running
 SETUP again. This will change the font for all applications. There is
 no way at present to change this font on the fly.


 843. Corrected Comm Driver Problems with Xon/Xoff Flow Control

 There are cases when Xon/Xoff flow control doesn't work correctly with
 the comm driver that is shipped with Windows Versions 2.10 and 2.11.
 There a corrected version of the comm driver in the Software Library.
 This file can be found in the Software/Data Library by searching on
 the keyword COMM211X, the Q number of this article, or S12347.
 COMM211X was archived using the PKware file-compression utility.

 To use this corrected version of the comm driver, make a copy of your
 Windows installation disks. Replace the COMM.DRV that was on these
 disks with the new COMM.DRV, and reinstall Windows.

 Please report any problems with this driver to Microsoft Product Support.


 844. How to Prevent a List Box from Sorting

 Question:

 How can I prevent a list box from sorting? I know that the
 LBS_STANDARD style includes LBS_SORT, but I'm not using LBS_STANDARD
 or LBS_SORT.

 Response:

 In addition to not using LBS_STANDARD and LBS_SORT, you need to use
 NOT LBS_SORT as one of the style bits for the list box in your dialog
 template. The following is an example:

 AboutDlg DIALOG 0, 0, 100, 200
      STYLE WS_POPUP | WS_DLGFRAME
      BEGIN
           LISTBOX, LISTBOXID, 0, 0, 30, 40, LBS_NOTIFY |
                     NOT LBS_SORT | WS_BORDER
      END


 845. Sharing File Handles in a DLL

 Problem:

 I am trying to use a DLL as a central place to access the contents of
 a file from different applications. To simplify the description of the
 problem, assume that there are two applications, A and B. Application
 A calls DLL to open the file. The file pointer (FILE * pFIle ) is
 stored on the data segment of the DLL. Application A calls a function
 in the DLL to read record "n" of this file properly. However, when
 Application B calls the same function in the DLL to read the same
 record, the record appears as random characters.

 Response:

 You cannot share file handles between applications or DLLs. Each
 application has its own file handle table. When an OpenFile occurs, a
 file is taken out of the application's PSP. For two separate
 applications to use a file, each must do its own OpenFile, file I/O
 calls, and close.


 846. No SAE Version for Windows/386

 Question:

 Is there a stand-alone environment (SAE) for Windows/386? If not, how
 can an SAE application take advantage of Windows/386's use of expanded
 memory?

 Response:

 There is no SAE for Windows/386. However, all is not lost. Windows/386
 converts EXTENDED memory to EXPANDED memory, which can be used by the
 Windows portion of Windows/386. There is another way to get this
 functionality. 386MAX by Qualitas is a device driver that takes
 extended memory and emulates it as expanded. If you install this
 driver before running your SAE run-time application, Windows will be
 able to take advantage of the emulated expanded memory.


 847. Demand Paging Old Applications

 Question:

 Does the Demand Paging scheme for Windows Version 3.00 apply to DOS
 applications (i.e., old applications) as well as standard Windows
 applications?

 Response:

 Non-Windows applications will be demand paged ONLY IN LIMITED
 CIRCUMSTANCES. They will always be demand paged when paging is turned
 on and the application is not running (i.e., it is not running in the
 foreground with the focus of input AND it is not set up to run in the
 background). It will be demand paged in other circumstances ONLY when
 special hard-disk support is added to Windows/386, i.e., the OEM (or
 possibly Microsoft, for a limited set of hard disks) will supply a VHD
 (virtual hard disk device) that supports the functionality necessary
 to demand page non-Windows applications. We have not yet determined
 which hard disks we will support in this way.


 848. sopen Not Working in a DLL

 Problem:

 I have been using sopen in my Windows application without any
 problems. When I moved the routine to a DLL, it stopped passing the
 third parameter.

 Response:

 Sopen is looking at the _osmajor to see if it is running MS-DOS 3.00
 or later. If not, then no file sharing is provided. The reason this
 fails is that the DLL's init routine is not setting up the _osmajor
 variable correctly. Since Windows Versions 2.10 and later require
 MS-DOS 3.00 or later to run, then, in your DLLs init routine, you can
 set the variable to 3. The following is how it should appear:

    unsigned char   _osmajor;

    LibInit(...)
    {
      _osmajor = 3;

    }


 849. ClipText Sample Application Returns Unreadable Characters

 Problem:

 The PASTE function returns unreadable characters when I do the
 following:

 1. Start ClipText.

 2. Perform a Copy.

 3. Perform a Paste. "Hello Windows!" appears correctly.

 4. Exit ClipText.

 5. Restart ClipText.

 6. Perform a Paste. Unreadable characters appear.

 Response:

 For the ClipText sample, in the Paste section, you must move the
 CloseClipboard() function AFTER the GlobalUnlock() function.


 850. Getting Information About Extended Keys

 Question:

 I am writing a terminal emulator to run under Microsoft Windows and I
 need to distinguish between the right and left CTRL keys. Is there a
 way to do this?

 Response:

 Currently, there is no way to detect the difference between the left
 and right CTRL keys, or any of the duplicate extended keys such as
 ENTER, HOME, PGUP, PGDN, END, DEL, or INS. This feature is under
 review and will be considered for inclusion in a future release.


 851. Using SetMessageQueue() with SAE Application

 If an SAE application calls the SetMessageQueue() function at the
 beginning of the application, the application may hang when loading.

 One workaround for this is to write a stub application that spawns the
 application that uses the SetMessageQueue() function.


 852. Determining If MS-DOS Application Is Running under Windows

 Question:

 Is there a way for a DOS program to detect if it is running under
 Windows/286? I know how to detect Windows/386 using INT 2F, but I need
 to know how to determine if the program is running under any kind of
 Windows version.

 Response:

 You must use two different interrupt calls. Use INT 2F with AX = 1600H
 to determine if Windows/386 is running. Use INT 2F with AX = 1700H to
 determine if Windows/286 is running. The version number can be
 obtained via AX = 1700H. The following sample program provides this
 information:

 ---------------------- inwin.c
 #include <dos.h>

 union  REGS inregs, outregs;

 main()
 {
     // check if win386 is running
     inregs.x.ax = 0x1600;
     int86( 0x2F, &inregs, &outregs );
     if( outregs.h.al == 0x80 || outregs.h.al == 0) {
       // win386 is not running so
       // check if win286 is running via clipboard info
       inregs.x.ax = 0x1700;
       int86( 0x2F, &inregs, &outregs );
       if( outregs.x.ax == 0x1700 )
         printf ("\n\rWindows is NOT running!");
       else
         printf ("\n\rWindows 286 v%d,%d is running!",
                  outregs.h.al, outregs.h.ah);
     }
     else
       printf ("Windows 386 is running!");

 }
 ------------------------- end inwin.c

 For more information on INT 2F, please refer to Page 173 of the
 "Microsoft Windows Software Development Kit Programming Tools" manual,
 in the "Old App Support" section.


 853. Windows SDK: _lopen() Only Opens Existing Files

 Question:

 I am using the "undocumented" file I/O functions from Charles
 Petzold's "Programming Windows" book, but the _lopen() function always
 fails. Are these functions fully implemented?

 Response:

 The _lopen() should function correctly. These operations are used in
 the CardFile application located on the Sample Source Code disk in the
 Windows Software Development Kit (SDK) Versions 1.0x, 2.03, and 2.10.

 Unlike the OpenFile() function, _lopen() only opens existing files;
 this means you must first create the file with _lcreat() if it doesn't
 exist.


 854. Use of LINK4 /align Option

 Question:

 What are the trade-offs of using the LINK4 /align option? It seems
 that a small align size results in a smaller .EXE file, so why not
 always use something like /align:2?

 Response:

 According to the definition, the LINK4 /align option "directs the
 linker to align segment data in the executable file along the
 boundaries specified by 'size'." The "size" parameter is in bytes and
 must be a power of 2. Specifying /align:16 on the LINK line will align
 things on 16-byte boundaries; this is the recommended size for Windows
 applications because the default size is 512. When the linker creates
 an .EXE file, if the previous segment in your file did not end on a
 16-byte boundary, that segment is padded with extra bytes so that the
 current segment begins on a boundary that is a multiple of 16.

 If you have several small segments and you specify an align size of
 512, you will have a lot of wasted space in the segments, resulting in
 an unnecessarily large .EXE. The wasted space similar to the
 following:

    waste = align - (segment modulo align)

 Therefore, if you have an align size of 512 and a segment size of 514
 bytes, 510 bytes are wasted. If, on the other hand, your align size is
 2 bytes, 0 (zero) bytes are wasted.

 Problems can occur when LINK is creating a very large .EXE file using
 a small align value. This is because the size of the .EXE may exceed
 the range of values representable by the .EXE header segment table.

 As an example of how things can go wrong, assume that we have a very
 large .EXE file and we used an align size of 2. When the linker
 creates the .EXE file, it may have put segment 42 at file offset
 380,000. It records this information in a special format in the New
 EXE Segment Table. The format is as follows:

    Offset  Length  Contents
    ------  ------  --------

       0h      2    Offset of segment relative to beginning of file
                    after shifting value left by alignment shift count.
      02h      2    Length of segment (000h for segment of 65536 bytes)
      04h      2    Segment flag word
      06h      2    Minimum allocation size for segment; that is, amount
                    of space Windows reserves in memory for the segment
                    (0000h for minimum allocation size of 65536 bytes).

 In our case, the linker tries to put (380,000 >> 1) = 190,000 as the
 offset of the segment. The value 190,000 is too large to be stored in
 a 16-bit word (the maximum is 65,535), so 58,928 (the low 16 bits of
 190,000) gets stored there. Unfortunately, LINK doesn't warn you that
 data loss is occurring during this conversion of a 32-bit value to a
 16-bit value; it just stuffs the lower 16-bits into the Segment Table
 record and proceeds as if nothing went wrong. However, when Windows
 tries to read segment 42 from the .EXE file at run-time, it will take
 the value 58,928 and left-shift it by 1, with a result of 117,856,
 which certainly is NOT the same as 380,000 (the actual location of the
 segment in the file).

 Much of the above information was obtained from the "MS-DOS
 Encyclopedia" on Pages 1488-1490 (the "New Executable Header Format"
 section). Note especially the alignment shift count on Page 1489 and
 the Segment Table on Page 1490, and the "Microsoft Windows Software
 Development Kit Programming Tools" manual, Page 86 (/alignment
 switch).


 855. LoadMenuIndirect() Explanation and Example

 Question:

 In Version 2.00 of the Windows Software Development Kit (SDK), the
 description for LoadMenuIndirect() is confusing [I'm getting it
 confused with the description for LoadMenu()]. I have the following
 questions:

 1. What is the argument: a name of a resource or an in-memory menu
    template?

 2. What does the LoadMenuIndirect() function do? Does it load a
    resource, or does it take an in-memory template and set the window's
    menu to match it?

 Response:

 The following are answers to the questions above:

 1. The argument is a pointer to an in-memory template, which has been
    loaded in and locked.

 2. The function returns a menu handle to the resource template pointed
    to by lpMenuResource.

 Below is a simplified (no error checking) code fragment of how to use
 LoadMenuIndirect(). We will assume that this case statement is under
 the WM_COMMAND message processing, and the menu we wish to load in is
 called "DynaMenu" in the .RC file:

 ==========================================================================
 case IDM_NEWMENU:

   hResInfo       = FindResource ( hInstance,                     // Find it
                                   (LPSTR)"DynaMenu", RT_MENU );
   hResourceMenu  = LoadResource ( hInstance, hResInfo );         // Load it
   lpResourceMenu = LockResource ( hResourceMenu );               // Lock it
   hMenu          = LoadMenuIndirect ( lpResourceMenu );          // LoadMenu

   DestroyMenu ( GetMenu ( hWnd ));               // Get rid of old menu
   SetMenu ( hWnd, hMenu );                       // Apply new menu

   UnlockResource ( hResourceMenu );              // Free up global memory
   FreeResource ( hResourceMenu );

   break;
 =========================================================================

 As you can see, the parameter to LoadMenuIndirect() is a pointer to an
 in-memory resource table.

 A sample application demonstrating this can be found in the
 Software/Data Library by searching on the keyword MENUDYN or
 the Q number of this article.


 856. Sharing Global Memory between Applications

 For information on how to share memory between applications, use the
 following query in this Knowledge Base:

    sharing and global and memory

 There is also a sample application in the Software Library called TALK
 that demonstrates how to share data. Please note that you must use
 large frame EMS to see the memory that does not use the GMEM_DDESHARE
 flag fail to change the global memory.

 TALK can be found in the Software/Data Library by searching on the
 keyword TALK, the Q number of this article, or S12370. TALK was
 archived using the PKware file-compression utility.


 857. Metafiles Losing Brushes and Limitations of Metafiles

 Question:

 I have written a metafile that may require a huge number of different
 brushes (around 600). This large number of brushes causes problems
 when we play back a metafile, due to memory limitations.

 Response:

 You are running out of brushes because GDI's Data Segment is getting
 full of brushes. Each brush takes up 88 bytes. To get around this,
 since you are not really using more than 600 different brushes, set up
 a table that contains a LONG and an HBRUSH. If the LONG RGB value is
 one that you already have, then select in its HBRUSH; otherwise,
 create a new brush and add it to the list.

 The same problem will occur with PENs. You are attempting to create
 nonsolid pens, many of which map to the same solid color. Here, you
 should use GetNearestColor() to see what you actually need to select
 in.

 There is a sample program called PLAYMETA in the Software Library that
 plays a metafile back one record at a time and displays any errors on
 a debugging terminal. This file can be found in the Software/Data
 Library by searching on the keyword PLAYMETA, the Q number of this
 article, or S12367. PLAYMETA was archived using the PKware
 file-compression utility.


 858. Putting Double Quotation Mark in String in STRINGTABLE

 Question:

 How can I include a double quotation mark character in a string in the
 string table? I tried \", but RC assumed that the double quotation
 mark (") was the end of the string and not a character within the
 string.

 Response:

 You can put double quotation marks in your RC strings by putting two
 double quotation mark characters next to each other, as in the
 following example:

 The following string in your .RC file

    "The letter ""Q"" is quoted."

 Results in the following string in your .RES file:

    The letter "Q" is quoted.


 859. Placeable Metafile Format Extends Normal Windows Metafiles

 Two companies, Aldus and Micrografx, have created an extended version
 of the Windows metafile format called the "Placeable Metafile Format."
 In this extended format, 22 bytes of extra information have been added
 to the standard metafile header. This extra information allows the
 metafile to tell an application where it is to be placed.

 Placeable metafiles are incompatible with the normal Windows GDI
 metafile procedures, and normal Windows GDI metafiles are incompatible
 with PageMaker and other applications that use placeable metafiles.

 Applications can take two courses of action with regard to the
 Placeable Metafile Format:

 1. Ignore the format. Don't try to read or write .WMF files in this
    format. If you want to be able to access these metafiles without
    making your application "PMF-aware," you can convert placeable
    metafiles into normal metafiles. The source code for a utility
    that converts placeable metafiles into normal Windows metafiles can
    be found in the Software Library by searching on the keyword
    CONVPMF, the Q number of this article, or S12418. CONVPMF (Convert
    Placeable Metafile) was archived using the PKware file-compression
    utility and contains the following files:

       File           Purpose
       ----           -------

       CONVPMF.TXT    A discussion of the conversion utility
       CONVPMF.C      The actual source code to the converter
       CONVPMF.MAK    The makefile for CONVPMF.C
       PLAYMETA.C     A sample function that displays the converted
                      metafiles

 2. Add code to your application to make it "PMF-aware." Such code
    could include prompts in your "Save..." dialog boxes that ask the
    user if he or she wishes to save the file as a normal Windows
    metafile or as a placeable metafile. Information necessary to
    manipulate placeable metafiles can be found in the Software/Data
    Library by searching on the keyword PMF, the Q number of this
    article, or S12419. PMF contains PMF.WRI, a Windows Write file that
    describes the Placeable Metafile Format and lists its restrictions
    and features. PMF was archived using the PKware file-compression
    utility.


 860. How to Cold Boot from a Windows Application

 Question:

 How do you perform a cold boot from a Windows application?

 I downloaded article Q32279 ("How to Reboot without Calling INT 19H")
 from the Knowledge Base and called its ColdBoot() assembly routine
 from within a Windows application. However, it hung the machine. When
 called from a DOS application, it correctly reboots the machine.

 Response:

 The ColdBoot() assembly routine contained in Q32279 will work
 correctly only in Windows/286. To be able to cold boot from within
 both Windows/286 and Windows/386, you need to force the 8042 (keyboard
 controller) to pulse the reset line so the processor will execute a
 cold boot. To achieve this, send 0xFE to port 0x64. For more
 information on this technique, query on the following words in this
 Knowledge Base:

    o_os2sdk and reboot and 0x00FE and cold boot

 The following is an assembler routine that demonstrates how to force a
 cold boot using this technique:

 ----------------------------------------------------------------------

 ; void ReBoot (void) - perform a cold boot on the system by forcing
 ;                      keyboard to pulse system reset pin.

 _TEXT   segment byte public 'code'
         assume  cs:_TEXT
         assume  ds:nothing
         assume  es:nothing
         assume  ss:nothing

         public  _ReBoot
 _ReBoot proc  near

         mov al, 0FEh
         out 64h, al

 _ReBoot endp

 _TEXT   ends

         end

 ----------------------------------------------------------------------


 861. Dynamically Changing Code Segments from FIXED to DISCARDABLE

 Question:

 How can I change my code segment from FIXED to DISCARDABLE while my
 program is running?

 Response:

 Windows does not allow you to dynamically change the flags for code
 segments; it uses the segment attributes specified in the .DEF file.

 Since all you really want to do is load the code segment in as FIXED
 when you need it and then discard it when you are done, what you will
 need to do is put that code in a DLL that is marked FIXED; this
 prevents Windows from ever moving these code segments. You can then
 manage the code loading and freeing of this library with the
 LoadLibrary() and FreeLibrary() functions.

 Below is sample code on how to do this:

 ----------
 HANDLE  hLibrary;
 FARPROC lpprocFunctionInLibrary;

   hLibrary = LoadLibrary ("MYLIB.EXE");
   if (hLibrary < 32) {     /* load failed */
     MessageBox (GetFocus(),"LoadLibrary", "Failed", MB_OK);
     break;
   }
   else
     lpprocFunctionInLibrary = GetProcAddress(hLibrary, "FunctionInLibrary");

   if (lpprocFunctionInLibrary == NULL) {
     MessageBox (GetFocus(),"GetProcAddress", "Failed", MB_OK);
   }
   else {
     hDialog = (*lpprocFunctionInLibrary) (hWnd,unsigned int,WORD,LONG);
     FreeLibrary( hLibrary );
     hLibrary = NULL;       /*  clear */
   }
 ----------
 Another way is to make it MOVEABLE and then use the GetCodeHandle and
 then use GlobalWire.  Make sure that you then do  a GlobalUnWire when
 you are done.



 862. Windows SDK: Documentation for SetErrorMode()

 There is a function called SetErrorMode() that will disable the system
 modal dialog boxes that are put up in response to system errors such
 as file I/O. This function was omitted from the Windows documentation
 but can be found in the "Windows 2.1 SDK Update" manual and is
 included in WINDOWS.H.

 Prototype
 ---------

    BOOL SetErrorMode (WORD wMode);

 Description
 -----------

 Windows intercepts all INT 24h errors. If SetErrorMode() is called
 with wMode set to zero and an INT 24h occurs, Windows puts up the
 standard INT 24h error message box. If SetErrorMode() is called with
 wMode set to 1 and an INT 24h occurs, Windows does not put up the
 standard INT24h error message box but, rather, fails the original INT
 21h call back to the application. In this manner, the application is
 able to handle disk errors itself using INT 21h, ah=59h (get extended
 error) as appropriate.


 863. Windows SDK: GlobalUnlock() Causes FatalExit 0x02F0

 If you GlobalUnlock() too many times [i.e., more times than you
 GlobalLock()], the debugging version of Windows will RIP with a
 FatalExit 0x02F0. The error message printed is "GlobalUnlock: Object
 usage count underflow."

 Under the retail version of Windows, you will not realize that you
 have GlobalUnlock()'ed the handle too many times because it will not
 FatalExit, and the function will return the normal value of 0.


 864. Handling System Modal Errors without Operator Intervention

 Question:

 I need to have a program run during the night without operator
 intervention for responding to file I/O errors. Is there a way I can
 do this?

 Response:

 Windows' default action is to put up a system modal dialog box when a
 file I/O error occurs. However, you can suppress this by using the
 function called SetErrorMode(). This function is not documented in the
 Version 2.03 and 2.10 SDK manuals but is included in WINDOWS.H and is
 discussed elsewhere in this Knowledge Base. For more information,
 query on the following word:

    SetErrorMode

 [SetErrorMode() is documented in the "Microsoft Windows 2.1 Software
 Development Kit Update," Page 14.]


 865. Lost Mouse Cursor When Debugging Unmarked Applications

 Using CVW Version 3.00 to debug an unmarked Windows 2.x application
 may cause the mouse cursor to disappear on the Windows screen (not the
 debugging screen).

 If you encounter this problem, use the MARK utility to mark your
 Windows 2.x application as "Windows 3.0 Aware" before debugging it
 with CVW 3.00.


 866. Failure to Load Resources When All File Handles Used

 A Windows application should not use all available handles. Doing so
 may prevent Windows from being able to load the application's
 resources.

 When Windows has to open the application's .EXE file to retrieve a
 resource (such as the icon's bitmap) it uses one of the application's
 file handles. If the application has used all available file handles,
 Windows cannot load the resource.

 In Windows 2.11 (Retail and Debug versions), failure to load an icon's
 bitmap due to lack of available file handles hangs the system.


 867. Use of the ENTER and TAB Keys in Multiline Edit Controls

 The behavior for the ENTER and TAB keys in a multiline edit control
 inside a dialog box has changed in Windows Version 3.00 to meet CUA
 (Common User Access) specifications.

 This change applies only to multiline edit controls within dialog
 boxes.

 In Windows Versions 2.x (2.03 and 2.10), if the dialog box edit
 control specifies DLGC_WANTALLKEYS, the edit control can then receive
 the ENTER or TAB keys. This feature is useful for including the ENTER
 and TAB keys in the edit field.

 However, in Windows Version 3.00, the ENTER key is always mapped to
 the default button and the TAB key always moves the focus to the next
 control. To include the ENTER and TAB keys in the edit field, you must
 use CTRL+ENTER and CTRL+TAB.

 This design also resolves problems for those who do not have a mouse,
 and therefore, cannot get out of the edit control.


 868. Size Limits for GlobalAlloc()'d Blocks of Memory

 Question:

 Is there a maximum size that a single block of GlobalAlloc()'d memory
 can be?  Why is it that I sometimes cannot GlobalAlloc() as much
 memory as GlobalCompact() indicates is available?

 Response:

 In 286 protected mode, you can only allocate 1 MB at a time. This
 is due to the limits of the 80286 processor. However, in 386 protected
 mode, you can allocate as much (virtual) memory as you have (although
 there is no guarantee that you will get as much as you want).

 The best way to proceed is to try to GlobalAlloc() the amount that
 GlobalCompact() returns; if this fails, try allocating successively
 smaller amounts. You may not get all that GlobalCompact() shows as
 available because of the amount of disk space, along with the amount
 of paging that has already been done, and other global memory
 management factors.


 869. C Run-Time Function intdosx() Causes GP Fault

 Problem:

 My application calls the intdosx() function. Before calling the
 function, the values necessary for the call are placed in the inregs
 and segregs structures. When the call is made in 286 and 386 protected
 mode, a general protection violation occurs; in real mode it works
 correctly.

 Response:

 You are probably passing an invalid selector in one of the segment
 registers (segregs). Under protected mode, all of the registers in
 segregs must be set to valid selectors. Your code should use the
 segread() function to get the current segment selectors before setting
 the values necessary for the call. For example:

    segread(&segregs);          // this insures that valid selectors are
                                // passed for the registers you don't set.
    ...
    // set applicable inregs and segregs values
    ...
    intdosx(&inregs, &outregs, &segregs);


 870. Guidelines for Using Far Pointers to Strings

 Question:

 I defined a global variable above the WinMain() routine, as follows:

      #include <windows.h>
      LPSTR Str = "testing";

 In my window procedure, I used the TextOut() function to display Str.
 In this case, I obtained garbage on the screen.

 I then tried defining Str in the same manner, but this time as a local
 variable in my window routine. When I print it out using TextOut(), it
 works.

 Why is this? Can I not define a string constant globally? I did define
 other global constants, such as: int i=20; and this I was able to use
 in my window procedure.

 Response:

 This problem is a result of the way the string was declared. By
 declaring the Str as an LPSTR, you have declared it statically as a
 far pointer. The far pointer is resolved when the application is first
 loaded. If DGROUP moves [which can happen when calling LocalAlloc()
 when yielding control to other applications with Yield() or
 GetMessage() or PeekMessage()], Str will become invalid. This happens
 to work for the local variable because the pointer is resolved when
 the function is entered, and you aren't yielding in the function.
 Therefore, the pointer remains valid throughout the life of the
 function call.

 Pointers should be declared and used as follows:

 1. Declare pointers to data in your DS as NEAR; for example:

       char * Str = "testing";

 2. Cast the pointer to FAR when making the following function call:

       TextOut(hDC, x, y, (LPSTR) Str, nCount);

    (Actually, if you have a prototype for the function, you don't need
    to cast the argument, since the prototype will perform an implicit
    cast.) The reason it works to dynamically cast a NEAR pointer to a
    FAR is that the compiler generates code to PUSH DS as the function
    call is being made. Even if the DS changes from time to time (as it
    often does in Windows), the code will always push the CURRENT value
    of DS on the stack when calling a function.

 3. You should declare pointers as FAR only if they will be assigned
    values dynamically:

       char * Str = "testing";
       LPSTR lpStr;
       ...
           lpStr = (LPSTR) Str;
           TextOut(hDC, x, y, lpStr, nCount);


 871. How to Submit Problem Reports for Windows 3.00

 This article describes Microsoft's preferred format for submitting
 problem reports on the Windows Version 3.00 Pre-Release product. Using
 this format will help us understand the precise context of your
 problem report, so that we can get better answers to you and give
 better problem reports to our development staff.

 Your problem report should be structured as follows:

    SEVERITY:
    ENVIRONMENT:
    DESCRIPTION:
    CONFIGURATION:
       Hardware:
       Drivers:
       Software:
    DEBUG INFORMATION:

 Each of these sections is described below:

 SEVERITY: 1, 2, 3, or 4. Use the following guidelines for severity:

    1  Critical -- Software does not work at all (crashes) or causes
       loss/corruption of data. Any situation where the system hangs or
       requires a cold or warm boot.

    2  Major -- A feature/function does not operate as designed. Any
       command or function that produces incorrect results or renders a
       portion of the product unusable.

    3  Minor -- Problems are generally minor in nature and do not
       prevent program from running (spelling errors, screen display
       errors, etc.), or the results are misleading and/or difficult
       for the user to understand.

    4  Enhancement -- Problem is a design or feature fault that will be
       addressed in a future release.

 ENVIRONMENT:
 WINDOWS REAL or 286 PROTECT or 386 PROTECT release Version x.xx

    (Include debug info line from the bottom right of screen)

 DESCRIPTION:
 Below is a description of the "ideal" problem report. The best problem
 reports are clear, complete, and concise. These are some points that
 will make it easier to track problem reports and enter them in our
 problem database.

 The report should state the steps necessary to reproduce the problem,
 the results, and (if available) a summary of the debug output. Try to
 include enough information that we can reproduce the problem here, but
 also try to keep the reports concise.

 The configuration should be at the end of the problem report so that
 people reading the problems can get the gist of the problem without
 having to wade through information that may not be relevant (but it's
 there in case it is relevant).

 CONFIGURATION:
 Hardware: CPU/Memory/Monitor/keyboard/printer/modem/etc.
 Drivers: Installed device drivers
 Software: TSRs/Network drivers/etc.

 DEBUG INFORMATION:
 The following is a sample of debug output that can be helpful in
 locating a problem:

 GENERAL PROTECTION VIOLATION
 CS=0EED SS=0F35 DS=0F35 ES=OOOO FS=0000 GS=0000
    -- NV UP EI PL NZ NA PE NC
 0EED:00000387  MOV     AL,BYTE PTR     AL,BYTE PTR [BX]
    DS:23D0=INV:0003
 ln
 No symbols found

 .dg cs
 004D: 0063p CODE NOTEPAD  (0EEE) 1065,103D
 103D: 01FFp CODE USER     (05BE) 104D,102D
 1165: 0061p CODE GDI      (03AE) 101D,100D
 115D: 001Bp FREE  009D,0075
 07B5: 000Bp CODE KEYBOARD (018E) 035D,0FE5


 872. STRINGTABLEs and Combined Resource Files

 Problem:

 We're developing a modular Windows application that includes multiple
 resource files. Each of the RC files contains several STRINGTABLE
 statements. Our application has gotten quite large, and when we now
 compile and run it, several of our "STRINGTABLE messages" are being
 randomly omitted. How can we overcome this problem?

 Response:

 The identifier values for STRINGTABLE resources must be chosen with
 care. Multiple RC files may contain many STRINGTABLE statements, but
 the different RC files may not share the same ranges of identifier
 values. This is due to the method Windows uses to store and reference
 STRINGTABLE resources.

 STRINGTABLE data is broken up into separate segments, each containing
 16 strings. A modulo-16 algorithm is used to offset into a segment to
 select a specific string resource. The actual identifier values are
 used in constructing these segments. For example, consider the
 following RC file fragments:

             /* Table 1 */          /* Table 2 */

             STRINGTABLE            STRINGTABLE
             BEGIN                  BEGIN
                1, "Hello"             3, "Error"
                2, "GoodBye"           4, "Memory"

             END                    END

 If Tables 1 and 2 are in a single RC file, the resulting RES file will
 contain a single STRINGTABLE segment containing Strings 1 through 4.
 The problem with this setup occurs when Tables 1 and 2 are compiled
 separately and combined with the DOS COPY command. Each original RES
 file will contain its own STRINGTABLE segment, but produces an
 overlapping set of segments when combined during the binary copy
 process. A LoadString() will fail to access the correct string since
 it looks in the wrong place for the requested string.

 The solution is to use identifiers that do not share the same
 modulo-16 range over different RC files. To reflect this rule, the
 above example should look like the following:

             /* Table 1 */          /* Table 2 */

             STRINGTABLE            STRINGTABLE
             BEGIN                  BEGIN
                1, "Hello"             17, "Error"
                2, "GoodBye"           18, "Memory"

             END                    END

 In this form, Tables 1 and 2 could be compiled separately and produce
 a correctly formed RES with the DOS COPY command.


 873. DLL-Compatible Version of C Run-Time Function gcvt()

 The Software Library file GCVT demonstrates how a C run-time function
 can be rewritten to work in a DLL. The program calls a function in a
 DLL that contains code to reproduce the same functionality as the C
 run-time version of gcvt(). The original C run-time function gcvt()
 crashes the system when used in a DLL.

 This example should give you some ideas on how to rewrite other C
 run-time functions that originally did not work correctly in a DLL.

 GCVT can be found in the Software/Data Library by searching on the
 keyword GCVT, the Q number of this article, or S12493. GCVT was
 archived using the PKware file-compression utility.


 874. Changing Printer Resolution from an Application

 Question:

 Is there a way for a Windows application to change the resolution for
 a printer?

 Response:

 Setting the DPI of a printer is done internally by the printer driver.
 An application cannot access this directly. You can change the DPI in
 one of the following ways:

 1. Spawn the Windows Control Panel when the user chooses an item from
    the menu.

 2. Use the code provided in the Software Library file DEVMODE, which
    allows you to bring up the printer driver's internal dialog box.

    DEVMODE can be found in the Software/Data Library by searching on
    the keyword DEVMODE, the Q number of this article, or S12211.
    DEVMODE was archived using the PKware file-compression utility.

 The same method must be used for setting paper orientation and font
 cartridge settings, as well as other settings internal to the printer
 driver.


 875. Reasons Why CVW 2.10 Issues "Not Enough Memory" Error Message

 Question:

 Exactly what causes the "Not enough memory" error message from
 CodeView for Windows (CVW)?

 Response:

 CVW Version 2.10 usually issues the "Not enough space" error when it
 cannot allocate from EMS memory. CVW uses the resident EMS driver (via
 INT 67h), and if the EMS driver returns an error for some reason, CVW
 notices this and issues the "Not enough memory" error.

 CVW Version 2.10 will issue the "Not enough memory" error in the
 following cases:

 1. CVW cannot allocate EMS memory for the following:

    a. The module it's trying to load

    b. Publics

    c. Types

    d. Symbols

    e. Line number information

    f. Screen memory (for flipping/swapping)

 2. An attempt is made to allocate more than 0xBFFF bytes from EMS.

 3. Too many EMS handles are in use.

 4. CVW cannot allocate space from its near heap (non-EMS) to hold a
    structure of approximately 20 bytes.


 876. Debugging Unmarked 2.x Application May Hang System

 Through trial and error, it has been determined that debugging an
 unmarked Windows Version 2.x application using Microsoft CodeView for
 Windows (CVW) Version 3.00 may cause the system to hang in strange
 places.

 The solution to this problem is to mark the 2.x application as being
 3.00 compatible before debugging it.

 The application that exhibited this behavior hung the system in one of
 its first calls to GetMessage() when run under CodeView for Windows
 Version 3.00; running it outside of the CodeView environment, the
 system didn't hang.

 Using the MARK.EXE utility to tell Windows that the application is
 3.00 compatible solves this debugging problem.


 877. SizeofResource() Rounds to Alignment Size

 SizeofResource() returns the resource size rounded up to the alignment
 size. Therefore, if you have your own resource types, you cannot use
 SizeofResource() to get the actual resource byte count.

 It has been suggested that this be changed to reflect the actual
 number of bytes in the resource so that applications can use
 SizeofResource() to determine the size of each resource. This
 suggestion is under review and will be considered for inclusion in a
 future release of the Windows Software Development Kit (SDK).


 878. EMS Support in Windows 3.00

 This article contains four questions and answers about EMS support in
 Windows Version 3.00:

 1. Does Windows 3.00 provide EMS?

    Windows 3.00 has three modes: real mode, standard mode, and
    enhanced mode. Of the three, only enhanced mode provides EMS. This
    is because "LIMULATION" (simulation of LIM) requires hardware
    support that only the Intel 80386 provides; the 8086 and 80286
    don't have the necessary combination of protected mode addressing
    and virtual 8086 emulation (provided by the 80386's V86 mode).

 2. Can Windows 3.00 utilize the services of an already-installed LIM
    4.0 EMS driver, such as a "LIMULATOR" or hardware EMS board?

    Real mode Windows can take advantage of either a LIMULATOR or a
    hardware EMS board.

    Standard mode (which runs in 80286 protected mode) cannot use a
    LIMULATOR because these drivers run in 80386 protected mode,
    thereby conflicting with the standard mode's need to switch into
    protected mode. However, if your machine has a hardware EMS board
    and associated LIM 4.0 driver installed, any EMS calls made will be
    serviced by the EMS driver; however, Windows itself will not use
    the EMS memory in any way. Only small frame EMS is supported in
    protected mode.

    Enhanced mode is incompatible with preinstalled EMS drivers that
    cannot be "shut off" by Windows. If the EMS driver is a LIMULATOR
    that cannot be shut off, Windows will not be able to run in
    enhanced mode because the 80386 is already running in protected
    mode; LIMULATORS that can be shut off respond to Windows as it
    boots, turning themselves off and letting Windows manage the EMS
    that they had been managing.

    Physical LIM memory is not supported by 386 enhanced mode Windows.
    Software running in Virtual 8086 mode in any VM, including the
    system VM (which contains all the Windows applications), can make
    LIM 4.0 calls, but the calls will be handled by Windows/386, not by
    any previously installed LIM driver. No LIM calls are reflected;
    they are all handled (error or successful completion) by V86MMGR.
    As mentioned above, only small frame EMS is supported in protected
    mode.

 3. Can non-Windows applications make EMS calls? If so, are they
    limited to the LIM 3.2 API?

    If EMS is present, non-Windows applications have the full LIM 4.0
    specification available for their use. Running in enhanced mode,
    part of the mappable page array may not be usable, so applications
    have to be sensitive to what is usable in the mappable page array
    and what is not; they cannot assume that all of the mappable page
    array is available.

 4. Can Windows applications make EMS calls? Should they?

    If EMS is present, the normal small-frame EMS calls work as they do
    in Windows 2.x (that is, applications can make LIM 3.2 calls plus
    LIM 4.0's realloc function). This allows both Windows 2.x and 3.00
    applications to use EMS memory.

    CAUTION: In protected mode, the standard technique of querying for
    the presence of an EMS driver by examining the contents of
    interrupt vector 67h will fail due to virtualization of memory and
    virtualization of the interrupt description table (IDT). Therefore,
    applications will have to use the alternate technique of opening a
    file handle using the name of the EMS device driver. For more
    information on these techniques, please consult Chapter 9 of Ray
    Duncan's "Advanced MS-DOS" book (Microsoft Press, 1986), which is
    the chapter on memory management under DOS. Chapter 9 contains a
    discussion of EMS that describes how to check for the presence of
    an EMS driver.

    Windows 3.00 applications that use EMS when running in protected
    mode will be slower than non-EMS-using applications, and may run
    out of memory faster. If you need to run a Windows 2.x application
    that requires EMS under Windows 3.00, you can do this easily in
    enhanced mode. When designing a new application for Windows 3.00,
    however, you should take advantage of the new protected mode
    addressing scheme rather than trying to use EMS.

    CAUTION: In protected mode, the value returned as the location of
    the page frame is a selector, not a segment. Therefore,
    applications cannot make any assumptions about that value. Many
    EMS-using applications, besides encountering the inefficiency of
    using EMS, will probably run into other restrictions that will not
    allow them to run in protected mode. For example, they cannot put
    code into EMS unless they implement special handling in protected
    mode to create a code selector alias for the page frame selector.

    For enhanced mode, there are SYSTEM.INI entries that correspond to
    the same entries in the PIF file. The Control Panel does not touch
    them. We do not expect many people to need to do anything with
    these at all. See the PIFEDIT documentation for information on the
    meanings of possible values.

 Example
 -------

 ; The following variables perform the equivalent of the XMS and EMS
 ; memory PIF settings for the SYS VM:
 ;
 SYSVMEMSLIMIT           max value (default -1)
 SYSVMEMSREQUIRED        min value (default -1)
 SYSVMEMSLOCKED          is the EMS touched under interrupt or needed to
                         be in memory at all times for some other reason?
                         (default NO)
 SYSVMXMSLIMIT           max value (default -1)
 SYSVMXMSREQUIRED        min value (default -1)
 SYSVMXMSLOCKED          is the EMS touched under interrupt or needed to
                         be in memory at all times for some other reason?
                         (default NO)

 Related switch:

 EMMSIZE                 controls the maximum amount of memory
                         that will be used for EMS across all
                         VMs (default is -1, no limit)


 879. Windows 3.00 Icon and Cursor Compatibility with 2.10

 Question:

 According to the documentation, SDKPaint automatically converts
 Windows Version 2.10 bitmaps, cursors, and icons to Windows 3.00
 format. Are these bitmaps, cursors, and icons downward compatible to
 Windows 2.10 after having been converted by SDKPaint?

 Response:

 No. After being converted into Windows 3.00 format, the icons,
 cursors, and bitmaps will then be composed of device independent
 bitmaps (DIBs). This is a new bitmap structure that is incompatible
 with Windows Version 2.10. Also, Windows 3.00 allows multiple
 definitions of the same icon or cursor, which allows Windows to select
 the best icon or cursor for the current display. Windows 2.10 is not
 able to read this new format, and this is another reason icons and
 cursors are not downwardly compatible.

 Therefore, you should use Windows 2.10 SDK tools to create code and
 resources for applications to run under both Windows 2.10 and Windows
 3.00. Applications built with Windows 3.00 tools will only run under
 Windows 3.00 (and later).


 880. Switching Between Single and Multiple Selection List Boxes

 Question:

 After I create a list box with CreateWindow(), I want to change
 the list box from a single selection to a multiple selection.
 I have tried the following:

    lStyle = GetWindowLong (hWnd, GWL_STYLE);
    lStyle |= LBS_MULTIPLESEL;
    SetWindowLong(hWnd, GWL_STYLE, lStyle);

 This doesn't work. Why doesn't this work, and how can I accomplish
 this switch from single selection mode to multiple selection mode?

 Response:

 This doesn't work because there are more factors involved than just
 dynamically changing the style bit. We recommend that you create two
 hidden list boxes in your .RC file and, during the WM_INITDIALOG
 routine, determine which one is to be displayed and use that. You can
 then change between the two by making one hidden and the other one
 visible using the ShowWindow() function.


 881. WDEB386 Debugger's Use of COM Port

 Question:

 How can I change WDEB386 to run at a different baud rate? I have tried
 to set up the port with the MS-DOS MODE command, but my settings seem
 to be ignored.

 Response:

 WDEB386's COM port settings cannot be altered except for specifying
 which COM port to use (via the /C: command-line switch). It does not
 use any WIN.INI or SYSTEM.INI settings because it is not a Windows
 application. Unlike CodeView, it runs outside the Windows environment.

 WDEB386 reprograms the COM port before each and every character is
 sent. This feature prevents other programs or parts of programs that
 use or abuse the COM port -- such as the DOS MODE command you are
 trying to use -- from having any effect on the operation of WDEB386.
 This is done to prevent misbehaved applications from affecting your
 ability to communicate with the debugger.

 WDEB386 is a systems-level debugger for the Windows 3.00
 protected-mode environment (enhanced mode and standard mode).
 We provided it in the very first prerelease SDK because CodeView was
 not ready yet. Later versions of the prerelease only included WDEB386
 with the DDK, not with the SDK. In the final retail SDK, WDEB386 is
 included. However for most uses, CodeView is the protected-mode
 debugger of choice. SYMDEB is the only real mode debugger.

 For additional information See Chapter 9 "Advanced Debugging in
 Protectd Mode: 80386 Debugger" in the Microsoft Windows Software
 Development Kit "Tools" manual.



 882. Don't Use the DOS Append Utility in Windows

 Do not use the DOS APPEND utility in Windows.

 The APPEND utility is fundamentally hostile to the operation of
 Windows. Windows relies on being able to build well formed pathnames
 of all of the files it opens.

 If Windows opens the file WINWORD.EXE, it will record internally the
 well formed pathname of this file so that the file can be reopened
 later without having to worry about having to deal with the fact that
 at this later time the current drive and current directory may be
 different, due to USER or APPLICATION activity. Thus, Windows records
 internally the well-formed pathname of this file:

    D:\WINWORD\WINWORD.EXE

 The problem with APPEND is that it prevents Windows from being able to
 reliably determine the proper well formed pathname of files. APPEND
 may cause the filename to be mistakenly built as follows, if C:\EXCEL
 is the current drive/directory at the time the open is performed, and
 the D:\WINWORD directory is on the APPEND search path:

    C:\EXCEL\WINWORD.EXE

 Thus, at a later time, when Windows goes to open this file again, it
 gets an error because the file is not actually located in the
 drive/directory that the well formed pathname indicates. This results
 in the following error message:

                    Change Disk

    Cannot find WINWORD.EXE, Please insert in drive A:

                       < OK >

 When running DOS applications, APPEND causes similar problems for
 WINOLDAP, which can result in unexpected "File Not Found" errors. This
 will result in a failure to start a DOS application, or a failure when
 the DOS application exits or when the user tries to switch back to
 Windows.

 The APPEND utility is not really needed. The current versions of all
 of the major application software on the market are not in need of the
 services provided by the APPEND utility. One of the major difficulties
 with APPEND is that the DOS Version 4.00 or 4.01 Install program will
 usually add APPEND to the user's configuration. This takes up valuable
 application memory space with no real benefit to the end user. APPEND
 can usually be found in the AUTOEXEC.BAT file and it should simply be
 removed or commented out by placing "REM " at the beginning of the
 APPEND line.


