09/12/95 NOTE:

The number of variations on the standard help file
has grown larger than I can support, so there are no
.ann files in this installment. I am working on a utility
that will automatically add these annotations correctly
to whatever version of win31wh.hlp you own, and hope to
have the utility ready for next month's installment.
I apologize for the inconvenience...

 Ron Burk


----------

This is the Windows/DOS Developer's Journal SDK Annotation File, a
growing collection of useful annotations for the standard online Windows
API help file, win31wh.hlp, which is included with every Windows C++
compiler.  You can either cut and paste these annotations into your own
SDK help file (use ALT-E-A to create a help topic annotation), or you
can copy the accompanying .ann file to your main Windows directory. 
WinHelp does not support any automated way of merging annotation files,
so you either have to add our annotations by hand, or clobber your own
annotations (if any) by copying in our .ann file. 

Microsoft shipped a slightly changed (but not much improved) version of
the standard API help file with Visual C++ v1.5.  If you are using that
version of win31wh.hlp, you can copy the included file named win31vc.ann
to win31wh.ann in your Windows directory.  If you have problems, send me
(Ron Burk) mail at 70302.2566@compuserve.com. 

If you have a useful annotation you would like to submit to our
collection, send it to:

  CompuServe: 70302,2566 (Internet: 70302.2566@compuserve.com)

If we use your submission, we will send you a free Windows/DOS
Developer's Journal T-shirt, emblazoned on the back with an original
drawing by Kansas artist T.  Watson Bogaard!

    Windows/DOS Developer's Journal

    "Advanced.  Serious.  Technical."

Windows/DOS Developer's Journal is the monthly publication for advanced
Windows programmers.  We cover a variety of topics such as VxDs,
undocumented functions, Windows NT, MFC, WinHelp programming, graphics,
communications, and so on -- usually in the form of reusable code that
you can use immediately.  In addition, each month contains regular
features like these:

* SDK Annotations
    (they appear in the magazine before they appear in this file)
* Bug++ of the Month
    (nasty bugs in popular C++ compilers)
* Tech Tips
    (user-submitted tips and techniques)
* Books in Brief
    (quick looks at recent books)

and more.  W/DDJ costs $34.99/year in the US, $45/year Canada/Mexico, or
$64/year overseas.  For more information, or to subscribe, contact

    Miller Freeman
    1601 W. 23rd St., Suite 200
    Lawrence, KS 66046
    tel:  (913) 841-1631
    fax: (913) 841-2624
    wdsub@rdpub.com

To subscribe by email or fax, include your name, address, phone number,
MasterCard or Visa number, and expiration date.  From CompuServe, that
email address would be something like this:

   >INTERNET:wdsub@rdpub.com





  
------------------------------------------------------------

W/DDJ SDK Annotation #1
TOPIC: Dialog Boxes

If a listbox is the first control in a dialog's tab order
and the dialog box was not created with the WS_VISIBLE
style, the listbox does not correctly draw itself with the
focus. 

Reference: p35, March 1993 Windows/DOS Developer's Journal.

------------------------------------------------------------

W/DDJ SDK Annotation #2
TOPIC: Property Lists

The oft-quoted maxim that using window properties is slower
than using class or window extra bytes (via
GetWindowWord()/SetWindowWord()) is totally false if you use
a global atom rather than a string to name the property. 
Properties were about 20% slower than window words under
Windows 3.0, but under Windows 3.1 GetProp() is about 300%
faster than GetWindowWord(). 

Reference: p 49, March 1993 Windows/DOS Developer's Journal.

------------------------------------------------------------

W/DDJ SDK Annotation #3
TOPIC: WM_MOUSEMOVE (2.x)

The documentation incorrectly states that the x and y
arguments are in screen coordinates.  They are in client
coordinates. 


------------------------------------------------------------

W/DDJ SDK Annotation #4 (revised)
TOPIC: SetWindowsHookEx (3.1)

SetWindowsHookEx() has a bug: using it to install a
task-specific hook can cause various failures.  The
workaround is to pass it a module handle rather than an
instance handle.  You can obtain a module handle from
GetModuleHandle().  If you only have the current instance
handle and not the name of the module, use the following
undocumented hack under Windows 3.1:

GetModuleHandle(MAKELP(0,hInstance));

If you #include <windowsx.h>, you can instead use the macro
GetInstanceModule(hInstance).  Under Windows NT, just pass a
NULL to obtain the handle of the current process.  Note that
the MSDN News article on this subject has the arguments to
MAKELP() backwards. 


Reference: MSDN News #1, 1993
Revised by: Tom Nolan

------------------------------------------------------------

W/DDJ SDK Annotation #5
TOPIC: CS_BYTEALIGNWINDOW 0x2000

The documentation makes it sound like this style bit is the
one you want for efficient bitblts.  In fact, most of your
bitblts will be to the client area of the window, not the
non-client area, so CS_BYTEALIGNCLIENT is the style bit you
should set if you are concerned about bitblt operation
efficiency.  Unaligned windows are slower at VGA resolution,
but typically not an issue with higher resolution
adapters (such as 256-color SVGA). 

Reference: p65, December 1993 Windows/DOS Developer's Journal.


------------------------------------------------------------

W/DDJ SDK Annotation #6
TOPIC: CODE Module Definition Statement

The documentation for the FIXED attribute is incorrect. 
Under Windows 3.1, if you mark code or data segments in your
.exe FIXED, the loader ignores that attribute -- the
segments will be moveable.  If you mark code or data
segments in your .dll FIXED, however, the loader will make
them fixed and will page lock them as well.  Due to the
implementation of GlobalPageLock(), that can result in your
segments using up precious DOS memory, eventually preventing
Windows from spawning new applications (since each new
application needs at least 512 bytes of DOS memory for a
task database entry).  The October 1994 Windows/DOS
Developer's Journal provides code to allocate fixed memory
without using up precious conventional memory. 


------------------------------------------------------------

W/DDJ SDK Annotation #7
TOPIC: WS_EX_TRANSPARENT 0x00000020L

Note that this bit does not really create transparent
windows.  If you create a window with this style, it is true
that the windows below it will show through as its
background.  However, if you then move your new window, it
will have the same background as it did in its original
position -- blotting out whatever it is covering in its new
position. 

------------------------------------------------------------

W/DDJ SDK Annotation #8
TOPIC: WM_NCHITTEST (2.x)

You can process this message to allow the user to drag a
window that does not have a title bar.  When you receive a
WM_NCHITTEST message and the mouse is in your client area
(or whatever conditions you want to start the drag), just
return HTCAPTION rather than passing the message on to
DefWindowProc(). 

Reference: p 37, March 1993 Windows/DOS Developer's Journal.


------------------------------------------------------------

W/DDJ SDK Annotation #9
TOPIC: WM_TIMER (2.x)

Although it sounds odd, you can use a WM_TIMER message as a
way to kill another application, if you can obtain the
handle of the main of the application you want to kill. 
Create a timer callback function that does nothing but pass
its first argument (window handle) to DestroyWindow().  Then
use PostMessage() to post (to the other app's main window) a
WM_TIMER message that points to your callback function. 
Your timer callback will get executed in the context of the
target application. 

Reference: p 64, September 1992 Windows/DOS Developer's Journal.



------------------------------------------------------------

W/DDJ SDK Annotation #10
TOPIC: MemoryWrite (3.1)

MemoryWrite() has a bug in it: it trashes the high 16 bits
of the EDI register (the 32-bit version of the DI register). 
The workaround is to save the register before calling
MemoryWrite() and restore it afterward. 

Reference: p. 71, April 1994 Windows/DOS Developer's Journal



------------------------------------------------------------

W/DDJ SDK Annotation #11
TOPIC: EscapeCommFunction (2.x)

The documentation omits one potential value for the
nFunction parameter, although it is defined in windows.h. 
The name is GETBASEIRQ and it returns the base address of
the COM port in the lower word and the IRQ setting in the
high word.  If the high word is -1 the port doesn't exist;
if it is 0, the comm driver does not support this escape
(which is the case, for example with some kinds of enhanced
serial boards). 

Submitted by: Thomas Zeisluft

------------------------------------------------------------

W/DDJ SDK Annotation #12
TOPIC: MessageBox (2.x)

Do not call MessageBox() from within the LibMain() of an
implicitly-linked DLL.  It will fail because the application
will not yet have a message queue at that point, and
MessageBox() (or anything else) cannot create its window
when the message queue does not yet exist.  Once the
application executes its internal startup code and calls
InitApp(), then it has a message queue and can safely call
functions that create windows. 

------------------------------------------------------------

W/DDJ SDK Annotation #13
TOPIC: LB_ADDSTRING (2.x)

If you are changing the contents of a listbox (for example,
by adding or deleting multiple strings), you may want to
minimize screen redrawing and maximize speed by disabling
window redrawing during your operation.  Follow these steps:

1) Send a WM_SETREDRAW with wParam equal to FALSE to the listbox.
2) Perform your adds or deletes.
3) Send a WM_SETREDRAW with wParam equal to TRUE to the listbox.
4) Use InvalidateRect() to force the listbox to redraw itself.

Revised by V. Ramachandran

------------------------------------------------------------

W/DDJ SDK Annotation #14
TOPIC: RegisterWindowMessage (2.x)

Do you really need to register a private window message?
Probably not, if all you need is an intra-app message number
that does not conflict with any Windows message numbers. 
Microsoft has revised its statement about what message
numbers are available for your private use.  Microsoft now
guarantees that you can use message numbers 0x8000 through
0xBFFF and they will not conflict with any system messages. 
They also claim the next SDK (Chicago?) will define WM_APP
equal to 0x8000 in windows.h. 

Reference: Microsoft Knowledge Base article Q86835



------------------------------------------------------------

W/DDJ SDK Annotation #15
TOPIC: WM_CHAR (2.x)

The documentation incorrectly claims that wParam is the
virtual key code.  In fact, it is the ASCII value of the key
pressed.  For example, pressing the '$' (ASCII 0x5B) key
produces a wParam equal to 0x5B -- if you interpreted that
as a virtual key code, you would incorrectly believe that
the user had pressed VK_HOME!

Submitted by: Brent Rector

------------------------------------------------------------

W/DDJ SDK Annotation #16
TOPIC: WinHelp (3.0)

The documentation says that the return value is nonzero if
WinHelp() is successful.  In fact, WinHelp() only returns
failure for systemic problems, like being unable to allocate
global memory, or being unable to spawn winhelp.exe.  If
WinHelp() successfully passes your request to winhelp.exe,
it returns success, period.  So, for example, if the named
help file is invalid, or you try to jump to a help topic
that does not exist, or any number of other logical errors,
WinHelp() still returns success. 

------------------------------------------------------------

W/DDJ SDK Annotation #17
TOPIC: WM_ENTERIDLE (2.x)

The documentation says that this messsage gets sent to your
application's "main window".  In fact, a dialog sends the
WM_ENTERIDLE message to its own parent window, which may or
may not happen to be your application's main window. 


------------------------------------------------------------

W/DDJ SDK Annotation #18
TOPIC: OPENFILENAME (3.1)

The documentation does not completely describe the behavior
when selecting multiple files.  To allow the user to select
multiple files, you turn on the flag OFN_ALLOWMULTISELECT. 
If you do that and call GetOpenFileName(), and if the user then
selects multiple files, then GetOpenFileName() will copy (into
lpstrFile) the path, followed by a space, followed by
space-separated filenames.  For example, if the user
selected files "fred.1" and "fred.2" from directory
"c:\test", lpstrFile would then point to the following
string:

    "c:\test fred.1 fred.2"

However, the documentation does not point out that if the
user selects only one file, then the path is not kept
separate from the filename.  Using the previous example, if
the user selected only file "fred.1", then lpstrFile would
point to this:

    "c:\test\fred.1"

Submitted by Julian Templeman



------------------------------------------------------------

W/DDJ SDK Annotation #19
TOPIC: CreateRoundRectRgn (3.0)

This function has a bug.  It will produce a GP fault if the
region rectangle is empty (either nLeftRect equals
nRightRect, or nBottomRect equals nTopRect).  About the only
workaround is to create a wrapper function that first checks
whether the rectangle is empty. 

Reference: p67, June 1994 Windows/DOS Developer's Journal
Submitted by Chris Mason



------------------------------------------------------------

W/DDJ SDK Annotation #20
TOPIC: EnableMenuItem (2.x)

When you are making changes to a window menu, the menu bar
is not immediately updated.  To force those changes (such as
enabling/disabling menu items) to be visible right away,
make sure you call DrawMenuBar(). 

------------------------------------------------------------

W/DDJ SDK Annotation #21
TOPIC: DrawText (2.x)

DrawText() has an off-by-one error that can result in a GP
fault.  The bug is evoked when you use pass an explicit
string length instead of NULL-terminating the text string. 
The bug is not evoked if you use the DT_NOPREFIX flag, or if
you NULL-terminate the text string (the easiest workaround). 

Reference: p53, August 1994 Windows/DOS Developer's Journal

------------------------------------------------------------

W/DDJ SDK Annotation #22
TOPIC: WM_MEASUREITEM (3.0)

Windows supports owner-draw menus, but only popup owner-draw
menus work correctly.  If you try to create an owner-draw
menubar for a window, Windows will not send you the
WM_MEASUREITEM message as it should. 

Reference: Microsoft Knowledge Base article Q69969

------------------------------------------------------------

W/DDJ SDK Annotation #23
TOPIC: WINDOWPOS (3.1)

The documentation says that y is "the position of the right
edge of the window".  It is, of course, the position of the
top edge of the window. 


------------------------------------------------------------

W/DDJ SDK Annotation #24
TOPIC: Shell Dynamic-Data Exchange Interface Overview (3.1)

The documentation says you can use DDE to get a list of
Program Manager groups by "issuing a request for the Group
item." In fact, that is not the correct item name -- you
must use 'Groups', not 'Group'. 



------------------------------------------------------------

W/DDJ SDK Annotation #25
TOPIC: TrackPopupMenu (3.0)

The documentation incorrectly states that you can pass the
TPM_RIGHTBUTTON flag if you want the menu to respond to the
right (secondary) mouse button instead of the left (primary)
mouse button.  In fact, passing TPM_RIGHTBUTTON causes the
menu to respond to the right mouse button as well as the
left.  There is apparently no combination of bits that cause
the menu to respond only to the right mouse button. 


------------------------------------------------------------

W/DDJ SDK Annotation #26
TOPIC: SetDlgItemText (2.x)

There is a bug in Windows that keeps SetWindowText() and
SetDlgItemText() from working correctly when applied to an
edit control owned by another application.  Rather than
sending a WM_SETTEXT to the edit control as they should,
these functions directly tinker with the target control's
internal window structure to change its title.  The bug,
then, is twofold:

 a) The target window is never notified that it needs to repaint.
 b) An edit control ignores its title, so changing its title does not
    affect the text it contains. 

The workaround is to use SendMessage() or PostMessage() to
deliver a WM_SETTEXT to the target window.  If you use
PostMessage(), make sure you pass a string pointer that is
somehow guaranteed to still be valid whenever the target
application gets around to fetching and processing the
message. 

Reference: p. 71, September 1993 Windows/DOS Developer's Journal


------------------------------------------------------------

W/DDJ SDK Annotation #27
TOPIC: GetMenuItemID (2.x)

The documentation says that this function returns 0 if the
specified menu item is a separator.  In fact, although the
resource compiler implicitly assigns separators an ID of 0,
you can assign them any 16-bit ID you like (with
ModifyMenu(), InsertMenu(), etc.) and this function will
return the correct ID, not just zero. 


------------------------------------------------------------

W/DDJ SDK Annotation #28
TOPIC: GetMetaFile (2.x)

Most programs that write a "Windows metafile" to disk use a
newer file format that this function does not understand. 
Most programs read and write "placeable" metafiles, a
metafile with a 22-byte header that contains a minimum
bounding rectangle for the figure.  If you want to read in a
metafile that may have been written by another application,
you should open the file yourself and check the header to
see if it is a placeable metafile.  See the SDK
documentation for a description of "placeable Windows
metafiles". 

Reference: p. 43, November 1994 Windows/DOS Developer's Journal


------------------------------------------------------------

W/DDJ SDK Annotation #29
TOPIC: WritePrivateProfileString (3.0)

The documentation for WritePrivateProfileString() and the
prototypes in windows.h, indicate that all strings passed in
are LPCSTR (32-bit pointer to unmodifiable character
string).  However, sometimes this function writes on your
input string anyway! For one example, if you pass in the
string

"Hello World     "

the function will remove the trailing spaces by writing a
NULL byte after the 'd'.  If you were passing in a constant
string that resided in a code segment, this aberrant
behavior could result in a GP fault.  This function
absolutely should not modify a string declared as LPCSTR,
but since it does, beware!

Submitted by Charles Leamon

------------------------------------------------------------

W/DDJ SDK Annotation #30
TOPIC: GetMenuState (2.x)

In addition to the bits noted in the documentation, this
function also correctly returns MF_POPUP for a submenu. 
Oddly, if you located the popup by position, the
MF_BYPOSITION flag will also be set in the returned flags,
although it is not on when used to locate normal menu items. 
Note that some of these flags equate to zero, so you cannot
just AND them with the returned flags to see if they are on. 
Here are the flags that equate to zero, along with the
expression you can use to infer their presence:

Zero Flag               Expression to test for flag
==============   =================================
MF_ENABLED       !(Flag&~(MF_DISABLED|MF_GRAYED))
MF_UNCHECKED     !(Flag&~MF_CHECKED)
MF_STRING        !(Flag&~(MF_BITMAP|MF_OWNERDRAW))


------------------------------------------------------------

W/DDJ SDK Annotation #31
TOPIC: GetWindowPlacement (3.1)

From the documentation, you might think the following code
would work:

WINDOWPLACEMENT Info;
GetWindowPlacement(hWnd, &Info);

In fact, it won't.  You must remember to initialize the
"length" field of the structure before calling this
function.  The following code works:

WINDOWPLACEMENT Info;
Info.length  = sizeof(Info);
GetWindowPlacement(hWnd, &Info);

Submitted by Pete Davis



------------------------------------------------------------

W/DDJ SDK Annotation #32
TOPIC: CreateCompatibleBitmap (2.x)

The description of height and width parameters states that
these values are in bits, when actually they are in pixels. 

Submitted by Charles Leamon


------------------------------------------------------------

W/DDJ SDK Annotation #33
TOPIC: LoadCursor (2.x)

The documentation says that you should call DestroyCursor()
for cursors loaded via LoadCursor().  That is wrong -- you
should only call DestroyCursor() for cursors created with
CreateCursor(). 

Submitted by Charles Leamon
Reference: Microsoft Knowledge Base article Q84779


------------------------------------------------------------

W/DDJ SDK Annotation #34
TOPIC: lstrcpyn (3.1)

The documentation for lstrcpyn() states that the last
parameter (cChars) is the number of characters to be copied,
when in fact the number of characters will be cChars-1. 
That's convenient, but it's not what it says and it's
inconsistent with the standard ANSI C run-time function
strncpy(), which does copy the specified count.  In other
words, unlike strncpy(), this function always
NULL-terminates the destination string. 

Submitted by Charles Leamon


------------------------------------------------------------

(Note, this annotation proved to be incorrect and therefore does not
appear in the .ann files anymore.)

W/DDJ SDK Annotation #35
TOPIC: ExitWindows (3.0)

In order for the EW_RESTARTWINDOWS feature to work,
ExitWindows() apparently must be able to locate WIN.COM. 
Otherwise, calling ExitWindows() just exits Windows without
restarting.  ExitWindows() doesn't seem to be very smart
about finding WIN.COM; having \WINDOWS on the DOS path
doesn't seem to do it, and it apparently doesn't search for
\WINDOWS on its own.  The application using ExitWindows() in
this fashion should therefore either reside in the \WINDOWS
directory, or should change its current directory into the
\WINDOWS directory before calling ExitWindows() with
EW_RESTARTWINDOWS.  Note that the user may have installed
Windows in a directory other than \WINDOWS, so you may want
to call GetWindowsDirectory() first. 

Submitted by Stephen Posey


------------------------------------------------------------

W/DDJ SDK Annotation #36
TOPIC: ExitWindows (3.0)

The documentation is incomplete.  To just terminate Windows
and return control to DOS, pass a zero in the dwReturnCode
parameter. 

Submitted by Charles Leamon
Reference: Microsoft Knowledge Base article Q100359


------------------------------------------------------------

W/DDJ SDK Annotation #37
TOPIC: OpenFile (2.x)

W/DDJ SDK Annotation #37

The descriptions for OF_CANCEL and OF_PROMPT are incorrect. 
OF_CANCEL does not add a cancel button to the 'File not
found' (OF_PROMPT) dialog.  Even if it did, how would the
caller know the user pressed the cancel button (only one
error return is defined for OpenFile())? The OF_PROMPT
dialog does not prompt the user to insert a diskette into
drive A:

Submitted by Charles Leamon



------------------------------------------------------------

W/DDJ SDK Annotation #38
TOPIC: WM_NCHITTEST (2.x)

The documentation claims that this message is sent to the
window that used SetCapture() to capture mouse input.  That
is totally false.  The window whose handle is passed to
SetCapture() will never receive any WM_NCHITTEST messages
(no matter where you move the moust) while the mouse is
captured. 

Submitted by V. Ramachandran


------------------------------------------------------------

W/DDJ SDK Annotation #39
TOPIC: TabbedTextOut (3.0)

The documentation claims that the tab stops are in device
units (pixels), but that is not true.  The tab stops are
treated as logical units, not device units. 

Submitted by Dan Miser
Reference: Microsoft Knowledge Base article Q113253


------------------------------------------------------------

W/DDJ SDK Annotation #40
TOPIC: LoadIcon (2.x)

The documentation says that you should call DestroyIcon()
for cursors loaded via LoadIcon().  That is wrong -- you
should only call DestroyIcon() for icons created with
CreateIcon(). 

Submitted by Charles Leamon
Reference: Microsoft Knowledge Base article Q84779


------------------------------------------------------------

W/DDJ SDK Annotation #41
TOPIC: COMPAREITEMSTRUCT (3.0)

Note that Windows has to send a WM_COMPAREITEM message when
a new item is added to the list, in order to determine its
correct position.  That means it does not know the position
of the new item yet, so (contrary to the documentation) the
itemID2 field in this structure will be -1 -- do not assume
it will be a legal index value. 

Submitted by V. Ramachandran


------------------------------------------------------------

W/DDJ SDK Annotation #42
TOPIC: GetProcAddress (2.x)

In attempting to locate the named function in the target
module, GetProcAddress() converts the function name to
uppercase and then performs a case-sensitive search.  That
means that GetProcAddress() cannot locate functions exported
with names containing lowercase characters.  Normally,
that's not a problem, as the __pascal calling sequence
forces uppercase names.  However, depending on the compiler
and linker tools and options you use, it is possible to
export __cdecl calling sequence functions with lowercase
characters, resulting in a function that GetProcAddress()
cannot locate. 

Submitted by Keith Bluestone

------------------------------------------------------------

W/DDJ SDK Annotation #43
TOPIC: SetScrollRange (2.x)

The documentation says you can use this function to hide or
show standard scroll bars, but does not tell you how!
Basically, if you specify the same value for both the
minimum (nMin) and maximum (nMax) scrolling positions, the
function will hide the scroll bar.  If the two positions are
not equal, the function will display the scroll bar. 

Submitted by Paul Bonneau


------------------------------------------------------------

W/DDJ SDK Annotation #44
TOPIC: LoadLibrary (2.x)

If LoadLibrary() cannot find the library, it may display an
error message to the user, depending upon the state of
Windows' "error mode".  If you want to handle that case
yourself and make sure Windows does not display the error
message, see the documentation for the function
SetErrorMode().  For example, the following code attempts to
load the library ctl3d.dll, but does not emit an error
message if it is not found. 

        HINSTANCE Ctl3d;
        UINT OldFlag = SetErrorMode(SEM_NOOPENFILEERRORBOX);
        Ctl3d        = LoadLibrary("ctl3d.dll");
        SetErrorMode(OldFlag);  // restore previous mode
        if(Ctl3d <= HINSTANCE_ERROR)
            // LoadLibrary() failed for some reason


------------------------------------------------------------

W/DDJ SDK Annotation #45
TOPIC: LoadLibrary (2.x)


The documentation claims this function returns an error code
of 0 if "System was out of memory, executable file was
corrupt, or relocations were invalid".  However, if a
library's LibMain() function returns 0 (signifying some
logical error during initialization), LoadLibrary() also
returns 0.  Therefore, do not assume that a 0 error code
means the system was out of memory or that the module was
corrupt in some way. 


------------------------------------------------------------

W/DDJ SDK Annotation #46
TOPIC: DdeClientTransaction (3.1)

The documentation does not say so, but the cbData argument
(length of data) must include the NULL byte if the data is a
string.  In other words, if lpvData is a string, cbData must
be set to strlen(lpvData)+1.  Otherwise, bad things may
happen in DDEML when you perform an XTYP_POKE or
XTYP_EXECUTE. 

Submitted by Mark Reha
Reference: Microsoft Knowledge Base article Q107387.


------------------------------------------------------------

W/DDJ SDK Annotation #47
TOPIC: WinHelp (3.0)

The WinHelp() API function normally allows one to execute
macros, jumps, popups, and so on.  However, if WinHelp was
started with WinExec() (i.e.  from Program Manager or File
Manager) instead of the WinHelp() API function, you will not
be able to execute macros or jumps on that help file without
starting up a second instance of the help file using the
WinHelp() API function. 

There is one way around this.  You can create a DLL with an
LDLLHandler that gets the callback address for the FAPI()
function from WinHelp.  FAPI() has the same parameters as
the WinHelp() API function except that the first parameters
(HWND) is not in FAPI() (so FAPI() only has 3 parameters). 
The FAPI() function will allow you to execute macros, jumps,
popups, etc regardless of how WinHelp was launched.  For
more information, get the Windows Help Authors Guide from
the MSDN CD-ROM or see Jim Mischel's book "The Developer's
Guide to WINHELP.EXE". 

Submitted by Pete Davis


------------------------------------------------------------

W/DDJ SDK Annotation #48
TOPIC: _fpmath (2.x)

When setting the handler for coprocessor error exceptions
(function 3), the documentation incorrectly says you should
place the address of your exception handler in DS:AX.  The
correct registers to use for this 32-bit address are DX:AX. 

Submitted by Manfred Keul


------------------------------------------------------------

W/DDJ SDK Annotation #49
TOPIC: VerQueryValue (3.1)

Amazingly, even though the second parameter to this function
is declared const (LPCSTR), VerQueryValue() modifies that
string anyway! Apparently the code replaces a '\' in your
string with a NULL byte temporarily and then puts it back. 
This is a bug.  For example, suppose you use a compiler
option that places constant strings in read-only code
segments (for Microsoft, "/Gf"; for Borland "-dc").  In that
case, passing such a constant string as the second argument
to this function results in a GP fault. 

Submitted by David Lowndes

------------------------------------------------------------

W/DDJ SDK Annotation #50
TOPIC: GetProfileString (2.x)

This applies to both GetProfileString() and
GetPrivateProfileString().  If the default value for these
functions contains trailing blanks and the default value is
used because the key did not appear in the INI file, Windows
will null-terminate the string (even though it is declared
const!) at the first trailing blank.  If that string is a
literal string and the code is compiled with some
optimizations then the string will end up in a code segment,
resulting in a GPF when the API function attempts to modify
it.

GetProfileString() and GetPrivateProfileString() also strip
out any leading spaces, and any leading and trailing quotes
(single or double quotes).

Submitted by Michael E. Kropp.
Revised by Kai Riihioja




------------------------------------------------------------

W/DDJ SDK Annotation #51
TOPIC: GetTickCount (2.x)

GetTickCount() may return units of milliseconds, but its
resolution is much worse than one millisecond under Windows
3.1.  For more accurate timings, call the function
timeGetTime(), which is defined in mmsystem.h. 



------------------------------------------------------------

W/DDJ SDK Annotation #52
TOPIC: GetWinFlags (3.0)

GetWinFlags() can also tell you if your 16-bit Windows 3.1
application is running under Windows NT:

    if(GetWinFlags() & 0x04000)
        // then we are running under Windows NT

Submitted by Paula Tomlinson.

Reference: "The Ultimate Windows Version Detector",
Windows/DOS Developer's Journal, February 1995. 


------------------------------------------------------------

W/DDJ SDK Annotation #53
TOPIC: KillTimer (2.x)

Under at least one condition, KillTimer() does not remove a
pending WM_TIMER message from the message queue as
documented.  First, understand that Windows just sets a bit
when a timer fires; it does not generate a WM_TIMER message
at that time.  A timer message is secretly added to your
input queue when you call GetMessage() or PeekMessage() and
a timer event is pending and no other messages are in the
queue. 

If you are using a PeekMessage() call with the PM_NOREMOVE
flag and if the timer bit is on at that point, PeekMessage()
will place a WM_TIMER message in the queue, but won't remove
it.  If you then call KillTimer(), it will ensure the timer
bit is off but won't remove the WM_TIMER message.  The next
call to GetMessage() or PeekMessage() returns this WM_TIMER
message.  One workaround is to use code like this to
terminate a timer:

    KillTimer (hWnd, ID);

    if (LOWORD (GetQueueStatus (QS_TIMER)) & QS_TIMER)
         PeekMessage (&msg, hWnd, WM_TIMER, WM_TIMER, PM_REMOVE);

Submitted by Mike Mast.


------------------------------------------------------------

W/DDJ SDK Annotation #54
TOPIC: DEVMODE (3.0)

The documentation claims that DMCOLOR_COLOR is defined to be
1 and DMCOLOR_MONOCHROME is defined to be 2.  In fact, if
you look in print.h you discover that the reverse is true. 

Submitted by Bill Liu.



------------------------------------------------------------

W/DDJ SDK Annotation #55
TOPIC: EN_CHANGE(2.x)

The documentation implies this notification only arises from
actions by the user.  In fact, this notification also arises
from programmatic changes, such as from sending a WM_SETTEXT
message to the edit control, or using SetWindowText() (which
sends a WM_SETTEXT message).  Not knowing this, you might
code an EN_CHANGE handler that attempts to modify the edit
control text, resulting in another EN_CHANGE notification --
an infinite loop!

Submitted by Scott Smith.


------------------------------------------------------------

W/DDJ SDK Annotation #56
TOPIC: EnumFonts (2.x)

The 3.1 documentation for this function specifies that the
third argument is of type FONTENUMPROC.  This was true in
previous versions, but in 3.1 this call is deprecated in
favour of the new EnumFontFamilies() API function.  The
definition of the FONTENUMPROC type has been updated to take
a NEWTEXTMETRIC parameter, and therefore no longer quite
matches the prototype specified for EnumFonts.  Microsoft
provids a new type, OLDFONTENUMPROC, which corresponds to
the STRICT definition of EnumFonts in windows.h, but this is
not reflected in the documentation.  In other words, if you
compile with STRICT defined for Windows 3.1 and want to use
EnumFonts(), make sure the third parameter is of type
OLDFONTENUMPROC. 

Submitted by David W. Gillett.


------------------------------------------------------------

W/DDJ SDK Annotation #57
TOPIC: PtInRect (2.x)

There is no mention of this, but the rectangle MUST be
normailized before this function is called.  In other words,
you have to make sure that lprc->right is greater than
lprc->left, and that lrpc->bottom is greater than lrpc->top. 
Otherwise, the point will never be considered inside of the
rectangle.  By contrast, the function RectInRegion() does
accept and correctly handle all rectangles, whether
normalized or not. 

Submitted by Peter Ritchie.


------------------------------------------------------------

W/DDJ SDK Annotation #58
TOPIC: SetTimer (2.x)

The documentation makes it sound like Windows either posts a
message (if you supply no callback function) or else calls
your callback function directly when the timer expires.  In
fact, when the timer fires, Windows sets a bit in your
message queue which gets transformed into a WM_TIMER message
by either GetMessage() or PeekMessage() when they find no
other messages in the input queue.  The WM_TIMER message
contains the address of your callback function (if any),
which will be called by DefWindowProc() after the message is
dispatched to your window.  The key point here is that the
timers created by SetTimer() are always message based
(Windows does not call your timer procedure asynchronously)
and of a lower priority than any other Windows message. 

Submitted by Alan M. Carroll.



------------------------------------------------------------

W/DDJ SDK Annotation #59
TOPIC: lstrcpy (2.x)

Believe it or not, this function (and apparently other
similar functions) examine the limit of the selector of the
output string, and even attempt to silently recover if the
operation causes a GP fault.  As a consequence, you should
not count on this function being a real speed demon. 

Submitted by Vivek Venugopalan

------------------------------------------------------------

W/DDJ SDK Annotation #60
TOPIC: GetModuleFileName (2.x)

Windows 3.1 has a bug that causes this function to sometimes
return relative paths instead of absolute (fully qualified)
paths.This error occurs if a relative path is specified in
the PATH variable, and the DLL is implicitly loaded from
this directory.  For example, if the PATH variable is:

        PATH=C:\DOS;D:.;C:\UTILS

and an application running from any other directory but
"D:.", loads a DLL (test.dll) in "D:." implicitly (since it
is in the path), then a call to GetModuleFileName() with the
DLL instance will return "D:.\test.dll". 

Submitted by V. Ramachandran
Reference: MSKB PSS ID Number: Q85330.

------------------------------------------------------------

W/DDJ SDK Annotation #61
TOPIC: CB_GETDROPPEDCONTROLRECT (3.1)

The documentation claims that this function retrieves the
screen coordinates of the listbox portion of a combo box. 
In fact, it retrieves the screen coordinates of the
rectangle that encloses the ENTIRE combo box in its
dropped-down state.  That means the rectangle retrieved is
both bit taller and a bit wider than the rectangle that the
help file claims is returned. 

Submitted by V. Ramachandran


------------------------------------------------------------

W/DDJ SDK Annotation #62
TOPIC: NotifyUnRegister (3.1)

As the documentation says, you can set the htask argument to
NULL to refer to the current task.  This is probably not a
good practice, however.  If more than one application can
load your DLL, then there is typically some scenario under
which the task that you called NotifyRegister() for has died
before you call NotifyUnRegister(), in which case passing
NULL would refer to the wrong task.  It's probably safer to
explicitly store that task that was passed to
NotifyRegister() and make sure you pass the same task to
NotifyUnregister(). 

Submitted by Paul Dolphin.


------------------------------------------------------------

W/DDJ SDK Annotation #63
TOPIC: CreateCompatibleDC (2.x)

You might think from the name that this function returns a
device context whose attributes are the same as the source
device context.  In fact, attributes such as the mapping
mode will be set to their defaults (e.g., the mapping mode
will always be MM_TEXT) in the returned device context, not
to the attribute values of the source device context. 

Submitted by Stuart Patterson
Reference: p.  624, "Programming Windows 3.1, 3rd Edition",
by Charles Petzold



------------------------------------------------------------

W/DDJ SDK Annotation #64
TOPIC: Device Contexts

The help file says CreateCompatibleDC() creates a device
context that "has the same attributes" as the source device
context.  In fact, the device context's attributes (such as
mapping mode) will have their default values, no matter what
value they had in the source device context. 

Submitted by Stuart Patterson
Reference: p.  624, "Programming Windows 3.1, 3rd Edition",
by Charles Petzold

------------------------------------------------------------

W/DDJ SDK Annotation #65
TOPIC: UngetCommChar (2.x)

Do not use this function under Windows 3.1 -- it causes lost
characters or even GP faults!

Submitted by Manfred Keul.
Reference: Microsoft Knowledge Base article Q100183.



------------------------------------------------------------

W/DDJ SDK Annotation #66
TOPIC: TabbedTextOut (3.0)

If a tab character is the last character in the string, then
all of the area to the next tab stop is filled with the
current background color.  This may or may not be the
behavior you want in any given situation. 

Submitted by Tim English.

------------------------------------------------------------

(Note: due to a bug in WinHelp, it is not possible to display
or annotate the GetRgnBox topic in the Visual C++ v1.5
version of the helpfile)

W/DDJ SDK Annotation #67
TOPIC: GetRgnBox (3.0)

The documentation claims GetRgnBox() returns COMPLEXREGION
when the region has overlapping borders.  In fact,
GetRgnBox() apparently returns COMPLEXREGION if the region
is simply non-rectangular, whether overlapping borders are
involved or not. 

Submitted by Jason Douglas.

------------------------------------------------------------

W/DDJ SDK Annotation #68
TOPIC: DRAWITEMSTRUCT (3.0)

The itemID field in this structure is set to a negative value
for an empty listbox or combobox.  Watch out, though -- since
this field is defined to be unsigned (UINT), a statement like
this:

    if(lpdis->itemID >= 0) // if listbox not empty
        // ... some code

will always evaluate true.  To check for an empty listbox or
combobox, either cast the field to int or check the high bit:

    if( (int) lpdis->itemID  >= 0)  // this works correctly
        //... some code

Submitted by Aaron O'Neil.


------------------------------------------------------------


W/DDJ SDK Annotation #69
TOPIC: DDEDATA (2.x)

In this help topic, the description for the fResponse field
actually describes the fAckReq field, and vice versa. 

Submitted by Sudhir Menon.
Reference: Microsoft Knowledge Base article Q93372.

------------------------------------------------------------

W/DDJ SDK Annotation #70
TOPIC: EnableCommNotification (3.1)

Due to bugs in Windows 3.1, you will probably want to always set
both cbWriteNotify and cbOutQueue to -1, thus disabling the
CN_TRANSMIT and CN_RECEIVE notifications.  If you do not set
them to -1, spurious WM_COMMNOTIFY messages can be sent,
resulting in a system crash at higher baud rates. 

Submitted by Manfred Keul.
Reference: Microsoft Knowledge Base article Q101420.



------------------------------------------------------------

W/DDJ SDK Annotation #71
TOPIC: GetMsgProc (3.1)

The help file fails to mention that your hook function gets
called by PeekMessage(), not just by GetMessage().  The
documentation also contradicts itself, saying first that wParam
is undefined and later saying that wParam is NULL.  The
Microsoft Knowledge Base, on the other hand, reveals that wParam
contains the PM_ flags that were used in the call to
PeekMessage(), so your message hook can, for example, determine
if the message was being removed or not with code like this:

    if(wParam & PM_REMOVE)
        //... then message is being removed
    else

        //... message is not being removed

Note that your hook may want to ignore the message if it is not
being removed, since your hook will get called again when the
same message is removed by some future call to GetMessage() or
PeekMessage(). 

Submitted by V. Ramachandran.
Reference: Microsoft Knowledge Base article Q104068


------------------------------------------------------------

W/DDJ SDK Annotation #72
TOPIC: GetInstanceData (2.x)

Both the documentation and windows.h declare the second
parameter as a BYTE*.  Unfortunately, that declaration is only
correct if you are using a memory model with near data (small or
medium memory models), and will be incorrect for large or huge
memory models.  The correct declaration for this argument is
BYTE NEAR*. 

Submitted by Martin Cooper.


------------------------------------------------------------

W/DDJ SDK Annotation #73
TOPIC: MENUITEMTEMPLATE (3.0)

Missing from the list of bits you can turn on in mtOption is
MF_END (0x0080), which indicates that the item terminates
the menu.

Submitted by V. Ramachandran.



------------------------------------------------------------

W/DDJ SDK Annotation #74
TOPIC: AddAtom (2.x)

AddAtom() handles strings that begin with "#" specially:
it expects the string following the "#" to be a string
of digits, and returns an atom is value is the 16-bit
binary representation of that string of digits. Unfortunately,
if your first call to AddAtom() is with a string like "#nondigits",
it will produce a divide by zero fault. Two workarounds
are possible: either make sure your first call to AddAtom()
does not contain such a string, or call InitAtomTable()
before calling AddAtom() for the first time.

Submitted by V. Ramachandran.
Reference: MSKB PSS ID Number: Q103036


------------------------------------------------------------


W/DDJ SDK Annotation #75
TOPIC: DCB (2.x)

When setting the BaudRate field, do not use the constant
CBR_14400; Windows 3.1's COMM.DRV has a bug that will produce
communications problems due to a bad table entry for that
constant.  Instead, set BaudRate to the integer 14400 to
communicate at 14400 baud. 

Submitted by Manfred Keul.
Reference: Microsoft Knowledge Base article Q83232.


------------------------------------------------------------

W/DDJ SDK Annotation #76
TOPIC: RegisterRoutine WinHelp macro

The documentation does not reveal how to specify the return type
of the function. You do this by inserting a type-specifying
character followed by an equal sign in the third parameter
string. For example, to register FindWindow() (which takes
two far strings and returns a 16-bit unsigned integer), you
might use:

    RR("USER", "FindWindow", "u=SS");

Note that if you do not specify a return type when you register
a function, you cannot use that function in an IfThen or
IfThenElse macro.

Submitted by Sudhir Menon.

------------------------------------------------------------

W/DDJ SDK Annotation #77
TOPIC: RTF Tokens

Strangely, the tokens "emc", "eml", and "emr" are
misspelled in the online help -- they should be
"ewc", "ewl", and "ewr", where the "ew" stands for
Embedded Window. Note that this are not really RTF
tokens, but literal text. The help compiler scans
for any text you have entered of the form "{ewx commands}"
in order to detect embedded window commands. Such text,
when translated by your word processor into RTF, looks
like this: "\{ewx commands\}".

Submitted by John Sawyer.


------------------------------------------------------------

W/DDJ SDK Annotation #78
TOPIC: WM_ENTERIDLE (2.x)

Note that you can elect to suppress the WM_ENTERIDLE
message for a particular modal dialog box by defining
it with the DS_NOIDLEMSG window style bit.

Submitted by V. Ramachandran.


------------------------------------------------------------

W/DDJ SDK Annotation #79
TOPIC: WM_SYSCOMMAND (2.x)

You can use this message with SC_MENUKEY to simulate the
user selecting a menu with an accelerator key.  For example,
to simulate the user accessing the "File" menu, you might
use the following code:

PostMessage(hWnd, WM_SYSCOMMAND, SC_MENUKEY, MAKELPARAM('f',0));

Submitted by Jay Giganti.


------------------------------------------------------------

W/DDJ SDK Annotation #80
TOPIC: wsprintf

The documentation says the second parameter is an LPSTR,
but it is actually an LPCSTR (and so declared in windows.h),
so it's safe to use a string constant.

Submitted by: V. Ramachandran.


------------------------------------------------------------

W/DDJ SDK Annotation #81
TOPIC: _lread (2.x)

If you call _lread() to read a floppy when there is no
diskette in the drive, Windows puts up a system error
message box ("Cannot Read from Drive...") with Retry and
Cancel buttons.  If the user presses the Cancel button,
_lread() returns a non-negative number, indicating success. 
It should return HFILE_ERROR instead. 

To avoid this error, call SetErrorMode(SEM_NOOPENFILEERRORBOX)
before calling _lread(), then it will correctly return -1 on
failure. 

Submitted by: V. Ramachandran.
Reference: MSDN PSS ID No. Q111587.


------------------------------------------------------------

W/DDJ SDK Annotation #82
TOPIC: WM_COMPAREITEM (3.0)

The documentation for WM_COMPAREITEM says that the parent of
owner-drawn listboxes with the LBS_SORT (or CBS_SORT) styles
will get this message in order to determine the relative
position.  However, Windows will not send the WM_COMPAREITEM
message if the LBS_HASSTRINGS style bit is set, even if it
is an owner-drawn listbox with the LBS_SORT style. The same
is true for comboboxes.

Submitted by: V. Ramachandran.


------------------------------------------------------------

W/DDJ SDK Annotation #83
TOPIC: DOCINFO (3.1)

The documentation fails to point out that lpszOutput is
limited to 32 characters, including the null terminating
byte.  If you use a string longer than that, the trailing
characters will be ignored.  For example, if you use a
string containing the too-long path:

    c:\usr\ts\issues\pending\12345678\abcdefgh.out

the actual file that gets created will be:

    c:\usr\ts\issues\pending\123456

Submitted by V. Ramachandran.




------------------------------------------------------------

W/DDJ SDK Annotation #84
TOPIC: SetAbortProc (3.1)


SetAbortProc() returns a negative value, which is documented
as indicating failure.  However, the return value from
SetAbortProc does not indicate success or failure of the
function, so don't depend on the return value for anything.

Submitted by:	V. Ramachandran.
Reference:	MSDN PSS ID No. Q109540.


------------------------------------------------------------

W/DDJ SDK Annotation #85
TOPIC: BN_DISABLE (2.x)

The documentation claims the button sends this notification
whenever it gets disabled. In fact, Windows 3.1 buttons do
not appear to ever send this notification!

Submitted by: V. Ramachandran.






------------------------------------------------------------

W/DDJ SDK Annotation #86
TOPIC: BN_DOUBLECLICKED (2.x)

The documentation fails to point out that this notification
is only sent for buttons that have the BS_OWNERDRAW or
BS_RADIOBUTTON styles. No other types of buttons generate
this notification

Submitted by: V. Ramachandran.



------------------------------------------------------------

W/DDJ SDK Annotation #87
TOPIC: WM_DROPFILES (3.1)

Documentation for the undocumented handle has since been
published by Microsoft in Microsoft Systems Journal.  This
handle points to a structure like this:

typedef struct {
    int     wSize;              // Number of bytes in this structure
    POINT   ptMousePos;         // Mouse position
    BOOL    fInNonClientArea;   // TRUE if mouse was in client area
    // Pathnames begin after structure each one zero-terminated
    // Zero-length pathname used to indicate the end
} DROPFILESTRUCT, FAR *LPDROPFILESTRUCT;

The Win32 version of this structure is slightly different.  The
first field becomes a 4-byte rather than a 2-byte integer, and
the structure contains an additional BOOL field at the end that
is TRUE if the pathnames are in Unicode rather than ANSI
strings. 

Submitted by V. Ramachandran.
Reference:  May/June 1992 Microsoft Systems Journal
            February 1994 Microsoft Systems Journal



------------------------------------------------------------

W/DDJ SDK Annotation #88
TOPIC: List box messages 

Internally, listboxes maintain two 32-bit DWORDs for each
listbox item.  The first DWORD points to the text for the item
and the second DWORD contains whatever custom data you would
like; you can get it via LB_GETITEMDATA or set it via
LB_SETITEMDATA.  Some messages refer to one or the other of
these DWORDs, depending on whether the LBS_HASSTRINGS style is
set:

Message             LBS_HASSTRINGS?   Refers to:
==========================================================
LB_ADDSTRING        Yes             text pointer (lParam)
LB_ADDSTRING        No              custom data  (lParam)
LB_INSERTSTRING     Yes             text pointer (lParam)
LB_INSERTSTRING     No              custom data  (lParam)
LB_GETTEXT          Yes             returns text pointer
LB_GETTEXT          No              returns custom data
LB_GETITEMDATA      either          returns custom data
LB_SETITEMDATA      either          sets custom data (lParam)
WM_DRAWITEM         either          custom data (in itemData field)

In other words, if LBS_HASSTRINGS is not set, you cannot access
the normal text pointer -- all messages operate on the custom
data.  But if LBS_HASSTRINGS is set, you can access both the
normal text as well as the extra DWORD of custom data. 

Submitted by V. Ramachandran



