PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

MS KnowledgeBase - MS Windows

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

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.