Please send us your annotations! Just email them to 70302.2566@compuserve.com and indicate the topic and the help file that your annotation refers to. Help us build the SDK annotation database! 5/8/96 NOTE: The AnnTater utility mentioned below has been mutated to work with the InfoViewer that is integrated in the Visual C++ IDE. Instead of running anntater.exe, run mstater.exe if you want to annotate the IDE InfoViewer (rather than any .hlp files you may have). Let me know if you have problems. 12/1/95 NOTE: The WDJ annotation package now includes anntater.exe. AnnTater contains all of our annotations and you can use it to automatically place our annotations in your help file. Although I've tried to test this software in a variety of situations, USE IT AT YOUR OWN RISK. If you have annotations of your own, you may want to back up your .ann file before running AnnTater. To use AnnTater, follow these steps. If you don't have annotations of your own, the easiest route is to delete your .ann file each time you run anntater.exe on a new installment of our annotations. 1) invoke anntater.exe 2) select a type of help file (Windows 3.1 API or Win32 API) 3) if necessary, press the Locate button to specify the path where this helpfile exists 4) select one or more annotations that you want to install 5) press the Copy button 6) press Done Note that the program displays a checkmark beside each annotation that it believes it has successfully installed during the current session. ---------- This is the Windows 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 use anntater.exe to install them automatically for you. If you have a useful annotation you would like to submit to our collection, send it to: 70302.2566@compuserve.com If we use your annotation, you will be listed as the contributor. Our thanks to the dozens of readers whose contributions help us continue to build this database of useful information for the Windows programming community. Windows Developer's Journal "The Magazine for Windows Developers" Windows Developer's Journal is the monthly publication for advanced Windows programmers. We cover a variety of topics such as Windows 95 programming, NT programming device drivers, undocumented functions, compiler bugs, operating system bugs, graphics, MFC, multimedia, communications, and so on -- usually in the form of reusable code that you can use immediately (most of our code has been compiled with both Borland and Microsoft C/C++ compilers). 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) * Understanding NT (covers NT-specific programming issues) and more. WDJ costs $34.99/year in the US, $45/year Canada/Mexico, or $64/year overseas. To order a subscription, contact Miller Freeman P.O. Box 56565 Boulder, CO 80322-6565 (800) 365-1425 (303) 678-0439 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 ------------------------------------------------------------ WDJ SDK Annotation #1 TYPE: Win3.1 TOPIC: Dialog Boxes KEYWORD: 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. ------------------------------------------------------------ WDJ SDK Annotation #2 TYPE: Win3.1 TOPIC: Property Lists KEYWORD: 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. ------------------------------------------------------------ WDJ SDK Annotation #3 TYPE: Win3.1 TOPIC: WM_MOUSEMOVE (2.x) KEYWORD: WM_MOUSEMOVE The documentation incorrectly states that the x and y arguments are in screen coordinates. They are in client coordinates. ------------------------------------------------------------ WDJ SDK Annotation #4 (revised) TYPE: Win3.1 TOPIC: SetWindowsHookEx (3.1) KEYWORD: SetWindowsHookEx 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 , 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 ------------------------------------------------------------ WDJ SDK Annotation #5 TYPE: Win3.1 TOPIC: CS_BYTEALIGNWINDOW 0x2000 KEYWORD: CS_BYTEALIGNWINDOW 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. ------------------------------------------------------------ WDJ SDK Annotation #6 TYPE: Win3.1 TOPIC: CODE Module Definition Statement KEYWORD: 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 Developer's Journal provides code to allocate fixed memory without using up precious conventional memory. ------------------------------------------------------------ WDJ SDK Annotation #7 TYPE: Win3.1 TOPIC: WS_EX_TRANSPARENT 0x00000020L KEYWORD: WS_EX_TRANSPARENT 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. ------------------------------------------------------------ WDJ SDK Annotation #8 TYPE: Win3.1 TOPIC: WM_NCHITTEST (2.x) KEYWORD: WM_NCHITTEST 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. ------------------------------------------------------------ WDJ SDK Annotation #9 TYPE: Win3.1 TOPIC: WM_TIMER (2.x) KEYWORD: WM_TIMER 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. ------------------------------------------------------------ WDJ SDK Annotation #10 TYPE: Win3.1 TOPIC: MemoryWrite (3.1) KEYWORD: MemoryWrite 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 ------------------------------------------------------------ WDJ SDK Annotation #11 TYPE: Win3.1 TOPIC: EscapeCommFunction (2.x) KEYWORD: EscapeCommFunction 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 ------------------------------------------------------------ WDJ SDK Annotation #12 TYPE: Win3.1 TOPIC: MessageBox (2.x) KEYWORD: MessageBox 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. ------------------------------------------------------------ WDJ SDK Annotation #13 TYPE: Win3.1 TOPIC: LB_ADDSTRING (2.x) KEYWORD: LB_ADDSTRING 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 ------------------------------------------------------------ WDJ SDK Annotation #14 TYPE: Win3.1 TOPIC: RegisterWindowMessage (2.x) KEYWORD: RegisterWindowMessage 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 ------------------------------------------------------------ WDJ SDK Annotation #15 TYPE: Win3.1 TOPIC: WM_CHAR (2.x) KEYWORD: WM_CHAR 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 ------------------------------------------------------------ WDJ SDK Annotation #16 TYPE: Win3.1 TOPIC: WinHelp (3.0) KEYWORD: WinHelp 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. ------------------------------------------------------------ WDJ SDK Annotation #17 TYPE: Win3.1 TOPIC: WM_ENTERIDLE (2.x) KEYWORD: WM_ENTERIDLE 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. ------------------------------------------------------------ WDJ SDK Annotation #18 TYPE: Win3.1 TOPIC: OPENFILENAME (3.1) KEYWORD: OPENFILENAME 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 ------------------------------------------------------------ WDJ SDK Annotation #19 TYPE: Win3.1 TOPIC: CreateRoundRectRgn (3.0) KEYWORD: CreateRoundRectRgn 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 ------------------------------------------------------------ WDJ SDK Annotation #20 TYPE: Win3.1 TOPIC: EnableMenuItem (2.x) KEYWORD: EnableMenuItem 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(). ------------------------------------------------------------ WDJ SDK Annotation #21 TYPE: Win3.1 TOPIC: DrawText (2.x) KEYWORD: DrawText 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 ------------------------------------------------------------ WDJ SDK Annotation #22 TYPE: Win3.1 TOPIC: WM_MEASUREITEM (3.0) KEYWORD: WM_MEASUREITEM 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 ------------------------------------------------------------ WDJ SDK Annotation #23 TYPE: Win3.1 TOPIC: WINDOWPOS (3.1) KEYWORD: WINDOWPOS 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. ------------------------------------------------------------ WDJ SDK Annotation #24 TYPE: Win3.1 TOPIC: Shell Dynamic-Data Exchange Interface Overview (3.1) KEYWORD: Shell Dynamic-Data Exchange Interface Overview 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'. ------------------------------------------------------------ WDJ SDK Annotation #25 TYPE: Win3.1 TOPIC: TrackPopupMenu (3.0) KEYWORD: TrackPopupMenu 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. ------------------------------------------------------------ WDJ SDK Annotation #26 TYPE: Win3.1 TOPIC: SetDlgItemText (2.x) KEYWORD: SetDlgItemText 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 ------------------------------------------------------------ WDJ SDK Annotation #27 TYPE: Win3.1 TOPIC: GetMenuItemID (2.x) KEYWORD: GetMenuItemID 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. ------------------------------------------------------------ WDJ SDK Annotation #28 TYPE: Win3.1 TOPIC: GetMetaFile (2.x) KEYWORD: GetMetaFile 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 ------------------------------------------------------------ WDJ SDK Annotation #29 TYPE: Win3.1 TOPIC: WritePrivateProfileString (3.0) KEYWORD: WritePrivateProfileString 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 ------------------------------------------------------------ WDJ SDK Annotation #30 TYPE: Win3.1 TOPIC: GetMenuState (2.x) KEYWORD: GetMenuState 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)) ------------------------------------------------------------ WDJ SDK Annotation #31 TYPE: Win3.1 TOPIC: GetWindowPlacement (3.1) KEYWORD: GetWindowPlacement 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 ------------------------------------------------------------ WDJ SDK Annotation #32 TYPE: Win3.1 TOPIC: CreateCompatibleBitmap (2.x) KEYWORD: CreateCompatibleBitmap The description of height and width parameters states that these values are in bits, when actually they are in pixels. Submitted by Charles Leamon ------------------------------------------------------------ WDJ SDK Annotation #33 TYPE: Win3.1 TOPIC: LoadCursor (2.x) KEYWORD: LoadCursor 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 ------------------------------------------------------------ WDJ SDK Annotation #34 TYPE: Win3.1 TOPIC: lstrcpyn (3.1) KEYWORD: lstrcpyn 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.) WDJ SDK Annotation #35 TYPE: Win3.1 TOPIC: ExitWindows (3.0) KEYWORD: ExitWindows 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 ------------------------------------------------------------ WDJ SDK Annotation #36 TYPE: Win3.1 TOPIC: ExitWindows (3.0) KEYWORD: ExitWindows 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 ------------------------------------------------------------ WDJ SDK Annotation #37 TYPE: Win3.1 TOPIC: OpenFile (2.x) KEYWORD: OpenFile WDJ 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 ------------------------------------------------------------ WDJ SDK Annotation #38 TYPE: Win3.1 TOPIC: WM_NCHITTEST (2.x) KEYWORD: WM_NCHITTEST 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 ------------------------------------------------------------ WDJ SDK Annotation #39 TYPE: Win3.1 TOPIC: TabbedTextOut (3.0) KEYWORD: TabbedTextOut 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 ------------------------------------------------------------ WDJ SDK Annotation #40 TYPE: Win3.1 TOPIC: LoadIcon (2.x) KEYWORD: LoadIcon 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 ------------------------------------------------------------ WDJ SDK Annotation #41 TYPE: Win3.1 TOPIC: COMPAREITEMSTRUCT (3.0) KEYWORD: COMPAREITEMSTRUCT 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 ------------------------------------------------------------ WDJ SDK Annotation #42 TYPE: Win3.1 TOPIC: GetProcAddress (2.x) KEYWORD: GetProcAddress 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 ------------------------------------------------------------ WDJ SDK Annotation #43 TYPE: Win3.1 TOPIC: SetScrollRange (2.x) KEYWORD: SetScrollRange 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 ------------------------------------------------------------ WDJ SDK Annotation #44 TYPE: Win3.1 TOPIC: LoadLibrary (2.x) KEYWORD: LoadLibrary 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^ ------------------------------------------------------------ WDJ SDK Annotation #45 TYPE: Win3.1 TOPIC: LoadLibrary (2.x) KEYWORD: LoadLibrary 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. ------------------------------------------------------------ WDJ SDK Annotation #46 TYPE: Win3.1 TOPIC: DdeClientTransaction (3.1) KEYWORD: DdeClientTransaction 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. ------------------------------------------------------------ WDJ SDK Annotation #47 TYPE: Win3.1 TOPIC: WinHelp (3.0) KEYWORD: WinHelp 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 ------------------------------------------------------------ WDJ SDK Annotation #48 TYPE: Win3.1 TOPIC: _fpmath (2.x) KEYWORD: _fpmath 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 ------------------------------------------------------------ WDJ SDK Annotation #49 TYPE: Win3.1 TOPIC: VerQueryValue (3.1) KEYWORD: VerQueryValue 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 ------------------------------------------------------------ WDJ SDK Annotation #50 TYPE: Win3.1 TOPIC: GetProfileString (2.x) KEYWORD: GetProfileString 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 ------------------------------------------------------------ WDJ SDK Annotation #50 TYPE: Win3.1 TOPIC: GetPrivateProfileString (2.x) KEYWORD: GetPrivateProfileString 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 ------------------------------------------------------------ WDJ SDK Annotation #51 TYPE: Win3.1 TOPIC: GetTickCount (2.x) KEYWORD: GetTickCount 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. ------------------------------------------------------------ WDJ SDK Annotation #52 TYPE: Win3.1 TOPIC: GetWinFlags (3.0) KEYWORD: GetWinFlags 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. ------------------------------------------------------------ WDJ SDK Annotation #53 TYPE: Win3.1 TOPIC: KillTimer (2.x) KEYWORD: KillTimer 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. ------------------------------------------------------------ WDJ SDK Annotation #54 TYPE: Win3.1 TOPIC: DEVMODE (3.0) KEYWORD: DEVMODE 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. ------------------------------------------------------------ WDJ SDK Annotation #55 TYPE: Win3.1 TOPIC: EN_CHANGE (2.x) KEYWORD: EN_CHANGE 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. ------------------------------------------------------------ WDJ SDK Annotation #56 TYPE: Win3.1 TOPIC: EnumFonts (2.x) KEYWORD: EnumFonts 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. ------------------------------------------------------------ WDJ SDK Annotation #57 TYPE: Win3.1 TOPIC: PtInRect (2.x) KEYWORD: PtInRect 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. ------------------------------------------------------------ WDJ SDK Annotation #58 TYPE: Win3.1 TOPIC: SetTimer (2.x) KEYWORD: SetTimer 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. ------------------------------------------------------------ WDJ SDK Annotation #59 TYPE: Win3.1 TOPIC: lstrcpy (2.x) KEYWORD: lstrcpy 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 ------------------------------------------------------------ WDJ SDK Annotation #60 TYPE: Win3.1 TOPIC: GetModuleFileName (2.x) KEYWORD: GetModuleFileName 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. ------------------------------------------------------------ WDJ SDK Annotation #61 TYPE: Win3.1 TOPIC: CB_GETDROPPEDCONTROLRECT (3.1) KEYWORD: CB_GETDROPPEDCONTROLRECT 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 ------------------------------------------------------------ WDJ SDK Annotation #62 TYPE: Win3.1 TOPIC: NotifyUnRegister (3.1) KEYWORD: NotifyUnRegister 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. ------------------------------------------------------------ WDJ SDK Annotation #63 TYPE: Win3.1 TOPIC: CreateCompatibleDC (2.x) KEYWORD: CreateCompatibleDC 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 ------------------------------------------------------------ WDJ SDK Annotation #64 TYPE: Win3.1 TOPIC: Device Contexts KEYWORD: 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 ------------------------------------------------------------ WDJ SDK Annotation #65 TYPE: Win3.1 TOPIC: UngetCommChar (2.x) KEYWORD: UngetCommChar 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. ------------------------------------------------------------ WDJ SDK Annotation #66 TYPE: Win3.1 TOPIC: TabbedTextOut (3.0) KEYWORD: TabbedTextOut 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) WDJ SDK Annotation #67 TYPE: Win3.1 TOPIC: GetRgnBox (3.0) KEYWORD: GetRgnBox 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. ------------------------------------------------------------ WDJ SDK Annotation #68 TYPE: Win3.1 TOPIC: DRAWITEMSTRUCT (3.0) KEYWORD: DRAWITEMSTRUCT 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. ------------------------------------------------------------ WDJ SDK Annotation #69 TYPE: Win3.1 TOPIC: DDEDATA (2.x) KEYWORD: DDEDATA 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. ------------------------------------------------------------ WDJ SDK Annotation #70 TYPE: Win3.1 TOPIC: EnableCommNotification (3.1) KEYWORD: EnableCommNotification 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. ------------------------------------------------------------ WDJ SDK Annotation #71 TYPE: Win3.1 TOPIC: GetMsgProc (3.1) KEYWORD: GetMsgProc 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 ------------------------------------------------------------ WDJ SDK Annotation #72 TYPE: Win3.1 TOPIC: GetInstanceData (2.x) KEYWORD: GetInstanceData 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. ------------------------------------------------------------ WDJ SDK Annotation #73 TYPE: Win3.1 TOPIC: MENUITEMTEMPLATE (3.0) KEYWORD: MENUITEMTEMPLATE 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. ------------------------------------------------------------ WDJ SDK Annotation #74 TYPE: Win3.1 TOPIC: AddAtom (2.x) KEYWORD: AddAtom 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 ------------------------------------------------------------ WDJ SDK Annotation #75 TYPE: Win3.1 TOPIC: DCB (2.x) KEYWORD: DCB 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. ------------------------------------------------------------ WDJ SDK Annotation #76 TYPE: Win3.1 TOPIC: RegisterRoutine WinHelp macro KEYWORD: 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. ------------------------------------------------------------ WDJ SDK Annotation #77 TYPE: Win3.1 TOPIC: RTF Tokens KEYWORD: 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. ------------------------------------------------------------ WDJ SDK Annotation #78 TYPE: Win3.1 TOPIC: WM_ENTERIDLE (2.x) KEYWORD: WM_ENTERIDLE 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. ------------------------------------------------------------ WDJ SDK Annotation #79 TYPE: Win3.1 TOPIC: WM_SYSCOMMAND (2.x) KEYWORD: WM_SYSCOMMAND 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. ------------------------------------------------------------ WDJ SDK Annotation #80 TYPE: Win3.1 TOPIC: wsprintf KEYWORD: 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. ------------------------------------------------------------ WDJ SDK Annotation #81 TYPE: Win3.1 TOPIC: _lread (2.x) KEYWORD: _lread 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. ------------------------------------------------------------ WDJ SDK Annotation #82 TYPE: Win3.1 TOPIC: WM_COMPAREITEM (3.0) KEYWORD: WM_COMPAREITEM 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. ------------------------------------------------------------ WDJ SDK Annotation #83 TYPE: Win3.1 TOPIC: DOCINFO (3.1) KEYWORD: DOCINFO 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. ------------------------------------------------------------ WDJ SDK Annotation #84 TYPE: Win3.1 TOPIC: SetAbortProc function KEYWORD: SetAbortProc 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. ------------------------------------------------------------ WDJ SDK Annotation #85 TYPE: Win3.1 TOPIC: BN_DISABLE (2.x) KEYWORD: BN_DISABLE 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. ------------------------------------------------------------ WDJ SDK Annotation #86 TYPE: Win3.1 TOPIC: BN_DOUBLECLICKED (2.x) KEYWORD: BN_DOUBLECLICKED 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. ------------------------------------------------------------ WDJ SDK Annotation #87 TYPE: Win3.1 TOPIC: WM_DROPFILES (3.1) KEYWORD: WM_DROPFILES 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 ------------------------------------------------------------ WDJ SDK Annotation #88 TYPE: Win3.1 TOPIC: List box messages KEYWORD: 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 ------------------------------------------------------------ WDJ SDK Annotation #89 TYPE: Win3.1 TOPIC: SetTimer (2.x) KEYWORD: SetTimer Though the documentation does not mention this, you can change the timer interval after you create the timer. If you call SetTimer() with a window handle and a timer ID equal to an existing timer and specify a different time interval, SetTimer() updates the timer to the new time interval. Example: Use the following line to set a timer to a particular time interval. UINT nTimerID = SetTimer (hWnd, TIMER_ID, TIME_INTERVAL, NULL) Calling the following line will reset the timer to twice the time interval. nTimerID = SetTimer (hWnd, TIMER_ID, 2*TIME_INTERVAL, NULL) Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #90 TYPE: Win3.1 TOPIC: TranslateAccelerator (2.x) KEYWORD: TranslateAccelerator Contrary to the documentation, both WM_INITMENU and WM_INITMENUPOPUP get sent, even if the menu item is disabled, and even if the window is minimized and the keystroke matches no menu items. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #91 TYPE: Win3.1 TOPIC: GetPrivateProfileInt (3.0) KEYWORD: GetPrivateProfileInt The documentation says you must use a positive integer in the range 0 through 32,767 (0x7FFF) for the third argument, which is the default value the function returns if it cannot locate the desired entry in the .ini file. In fact, you can pass any legal integer value for this parameter -- it's just that since the function's return type is defined to be UINT, you must cast the result to int if you want to treat the result as a negative number. For example, the following code works correctly, despite what the documentation says: int Val = (int)GetPrivateProfileInt( "MyLib", "Debug", -1, "MyLib.ini"); if(Val == -1) /* then no such entry found */ ------------------------------------------------------------ WDJ SDK Annotation #92 TYPE: Win32 TOPIC: WM_NOTIFY KEYWORD: WM_NOTIFY AND QuickInfo If your dialog procedure handles a WM_NOTIFY that requires a return value, note that you MUST both return a non-zero value (to indicate to the dialog manager that you wish to specify a return value for the message) AND store the desired return value for the message in the window field DWL_MSGRESULT. The standard Windows header file windowsx.h provides a macro called SetDlgMsgResult() that makes this easy: #include //... case WM_NOTIFY : { NMHDR* Head = (NMHDR*)lParam; if(Head->code == LVN_ENDLABELEDIT) // allow user to edit listview labels return SetDlgMsgResult(Dialog, WM_NOTIFY, TRUE); } ------------------------------------------------------------ WDJ SDK Annotation #92 TYPE: Win32 TOPIC: LVN_ENDLABELEDIT KEYWORD: LVN_ENDLABELEDIT AND QuickInfo If your dialog procedure handles a WM_NOTIFY that requires a return value (as this one does), note that you MUST both return a non-zero value (to indicate to the dialog manager that you wish to specify a return value for the message) AND store the desired return value for the message in the window field DWL_MSGRESULT. The standard Windows header file windowsx.h provides a macro called SetDlgMsgResult() that makes this easy: #include //... case WM_NOTIFY : { NMHDR* Head = (NMHDR*)lParam; if(Head->code == LVN_ENDLABELEDIT) // allow user to edit listview labels return SetDlgMsgResult(Dialog, WM_NOTIFY, TRUE); } ------------------------------------------------------------ WDJ SDK Annotation #92 TYPE: Win32 TOPIC: LVN_BEGINLABELEDIT KEYWORD: LVN_BEGINLABELEDIT AND QuickInfo If your dialog procedure handles a WM_NOTIFY that requires a return value (as this one does), note that you MUST both return a non-zero value (to indicate to the dialog manager that you wish to specify a return value for the message) AND store the desired return value for the message in the window field DWL_MSGRESULT. The standard Windows header file windowsx.h provides a macro called SetDlgMsgResult() that makes this easy: #include //... case WM_NOTIFY : { NMHDR* Head = (NMHDR*)lParam; if(Head->code == LVN_BEGINLABELEDIT) // allow user to edit listview labels return SetDlgMsgResult(Dialog, WM_NOTIFY, FALSE); } ------------------------------------------------------------ WDJ SDK Annotation #93 TYPE: Win3.1 TOPIC: GetWindowText KEYWORD: GetWindowText AND QuickInfo GetWindowText() and GetDlgItemText() will not work when applied to a standard edit control that belongs to another application. The problem is that GetWindowText() and GetDlgItemText() attempt to optimize by directly examining the window caption of the target window. An edit control stores an empty string in its caption, not the edit control text, so this fails. You can avoid this bug by sending a WM_GETTEXT explicitly: HWND OtherEdit; char Buffer[256]; SendMessage(OtherEdit, WM_GETTEXT, sizeof(Buffer), (LPARAM)Buffer); ------------------------------------------------------------ WDJ SDK Annotation #93 TYPE: Win3.1 TOPIC: GetDlgItemText KEYWORD: GetDlgItemText AND QuickInfo GetWindowText() and GetDlgItemText() will not work when applied to a standard edit control that belongs to another application. The problem is that GetWindowText() and GetDlgItemText() attempt to optimize by directly examining the window caption of the target window. An edit control stores an empty string in its caption, not the edit control text, so this fails. You can avoid this bug by sending a WM_GETTEXT explicitly: HWND OtherEdit; char Buffer[256]; SendMessage(OtherEdit, WM_GETTEXT, sizeof(Buffer), (LPARAM)Buffer); ------------------------------------------------------------ WDJ SDK Annotation #94 TYPE: Win32 TOPIC: LVM_GETCOLUMNWIDTH KEYWORD: LVM_GETCOLUMNWIDTH AND QuickInfo The documentation claims that this message returns the column width if successful, "or zero otherwise". In fact, if you specify an invalid column number, this function returns garbage. Therefore, you cannot use this message to count the number of columns in a listview control. The associated message, LVM_GETCOLUMN, does correctly return zero when passed an invalid column number, so you can use LVM_GETCOLUMN to determine the number of columns in a listview control. ------------------------------------------------------------ WDJ SDK Annotation #95 TYPE: Win3.1 TOPIC: SystemParametersInfo (3.1) KEYWORD: SystemParametersInfo AND QuickInfo The documentation says that the screen saver time out (SPI_GETSCREENSAVETIMEOUT) is specified in milliseconds. In fact, the value returned is in seconds. Submitted by: Carlton Guc ------------------------------------------------------------ WDJ SDK Annotation #96 TYPE: Win32 TOPIC: LVN_ENDLABELEDIT KEYWORD: LVN_ENDLABELEDIT AND QuickInfo The documentation claims that there is no return value for this message. In fact, you should return FALSE if you want to reject the user's editing, or TRUE if you want to allow the changes to the listview item. If you are handling this notification inside a dialog procedure, remember that you must return a non-zero result AND store the message result (either TRUE or FALSE) in DWL_MSGRESULT, using either SetWindowLong() or the SetDlgMsgResult() macro (defined in windowsx.h). Submitted by: Poul A. Costinsky ------------------------------------------------------------ WDJ SDK Annotation #98 TYPE: Win32 TOPIC: RegSetValueEx KEYWORD: RegSetValueEx AND QuickInfo For string-based data types, such as REG_SZ, this function behaves differently under Win95 and NT. Under Win95, if you pass a value of "abcd" and a length of 4, the function will actually append a null byte, and if you retrieve the value later, you will find it has a length of 5. Under NT, this function stores exactly what you tell it to store. Submitted by Paula Tomlinson. ------------------------------------------------------------ WDJ SDK Annotation #97 TYPE: Win32 TOPIC: LVN_ITEMCHANGING KEYWORD: LVN_ITEMCHANGING AND QuickInfo The documentation incorrectly claims that you have to return TRUE to allow the change, or FALSE to prevent it. In fact, you have to return FALSE to allow the change or TRUE to prevent it. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #99 TYPE: Win32 TOPIC: LV_DISPINFO KEYWORD: LV_DISPINFO AND QuickInfo The documentation says you can set the LVIF_DI_SETITEM flag in the mask member to have Windows store the string and not ask for it again. It fails to point out that this only works for subitem 0 (the first column), so you would still have to handle requests for the text of the other columns. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #100 TYPE: Win32 TOPIC: LVM_EDITLABEL KEYWORD: LVM_EDITLABEL AND QuickInfo Note that the listview control implements in-place editing by creating an edit control on the fly. Unfortunately, it assigns that edit control a child ID of IDOK, which means that the parent of the listview control will receive edit control notifications that it might not expect. For example, if your listview control is in a dialog box, and your dialog procedure contains code like this: // inside WM_COMMAND handler... if(ControlId == IDOK) EndDialog(Dialog, TRUE); Then it may terminate the dialog when the user starts editing a listview item (since the transient edit control will send notifications like EN_CHANGE, with a control ID of IDOK). The above code should read: // inside WM_COMMAND handler... if(ControlId == IDOK && NotifyCode == BN_CLICKED) EndDialog(Dialog, TRUE); to be safe. ------------------------------------------------------------ WDJ SDK Annotation #101 TYPE: Win3.1 TOPIC: GetWindowTextLength (2.x) KEYWORD: GetWindowTextLength AND QuickInfo Due to a bug in Windows 3.1, both GetWindowTextLength() and WM_GETTEXTLENGTH return -1 if you use it on a combobox of style CBS_DROPDOWNLIST. Submitted by Mircea Neacsu ------------------------------------------------------------ WDJ SDK Annotation #102 TYPE: Win32 TOPIC: LB_ADDSTRING KEYWORD: LB_ADDSTRING AND QuickInfo If you are planning to add a large number (more than 100) of strings by calling LB_ADDSTRING, LB_INSERTSTRING, LB_DIR, or LB_ADDFILE, consider first sending the new Win32 message LB_INITSTORAGE to give the listbox a chance to preallocate memory, thus speeding up the process. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #102 TYPE: Win32 TOPIC: LB_ADDSTRING KEYWORD: LB_ADDSTRING AND QuickInfo If you are planning to add a large number (more than 100) of strings by calling LB_ADDSTRING, LB_INSERTSTRING, LB_DIR, or LB_ADDFILE, consider first sending the new Win32 message LB_INITSTORAGE to give the listbox a chance to preallocate memory, thus speeding up the process. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #103 TYPE: Win32 TOPIC: LoadString KEYWORD: LoadString AND QuickInfo Even though resource strings are stored as Unicode for both Win95 and NT programs, Win95 does not provide an implementation of the Unicode version of LoadString() (LoadStringW()). This usually trips up NT programmers who want to create a single .exe for both operating systems and yet still use Unicode under NT. In that case, you must at runtime detect that you are running under Win95 and, in that case, explicitly call LoadStringA(). Otherwise, if you have compiled with _UNICODE defined, LoadString() will expand into LoadStringW(), which is just a stub that fails under Win95. Submitted by Paula Tomlinson. ------------------------------------------------------------ WDJ SDK Annotation #104 TYPE: Win32 TOPIC: LVN_DELETEITEM KEYWORD: LVN_DELETEITEM AND QuickInfo The documentation implies that this notification arrives after the item is deleted. In fact, it arrives before the item is deleted from the listview control. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #105 TYPE: Win32 TOPIC: IsWindow KEYWORD: IsWindow AND QuickInfo The documentation claims this function returns TRUE if the given handle is a valid window handle. That was true under Windows 3.x and is true under Windows NT, but Windows 95 returns a large non-zero value that is not equal to 1 (nor is it equal to the window handle). In an un-indexed help topic in the initial Win95 SDK, Microsoft reveals that Win95 functions with BOOL return types are only guaranteed to return non-zero when the documentation claims they return TRUE. Windows NT appears to behave as documented. Submitted by: David Lowndes ------------------------------------------------------------ WDJ SDK Annotation #105 TYPE: Win32 TOPIC: GetClientRect KEYWORD: GetClientRect AND QuickInfo The documentation claims this function returns TRUE if the given handle is a valid window handle. That was true under Windows 3.x and is true under Windows NT, but Windows 95 returns a large non-zero value that is not equal to 1 (nor is it equal to the window handle). In an un-indexed help topic in the initial Win95 SDK, Microsoft reveals that Win95 functions with BOOL return types are only guaranteed to return non-zero when the documentation claims they return TRUE. Windows NT appears to behave as documented. Submitted by: Ron Scott ------------------------------------------------------------ WDJ SDK Annotation #106 TYPE: Win32 TOPIC: WM_GETDLGCODE KEYWORD: WM_GETDLGCODE AND QuickInfo The documentation WM_GETDLGCODE states that this message has no parameters. But the truth is that if the user presses a key, lParam will contain a pointer to a MSG structure containing information about the event that made Windows send the WM_GETDLGCODE message. This is indirectly documented in windowsx.h, where the HANDLE_WM_GETDLGCODE() macro passes two arguments to the handler: the window handle and (MSG FAR*)(lParam). Submitted by: Patrick Tennberg Reference: Microsoft Knowledge Base article Q83302 ------------------------------------------------------------ WDJ SDK Annotation #107 TYPE: Win32 TOPIC: DBT_DEVICEQUERYREMOVE KEYWORD: DBT_DEVICEQUERYREMOVE AND QuickInfo The documentation incorrectly says that you should return FALSE if you want to veto the device removal. In fact, you have to return BROADCAST_QUERY_DENY; returning FALSE will allow the device removal to proceed. Submitted by Paula Tomlinson. ------------------------------------------------------------ WDJ SDK Annotation #108 TYPE: Win3.1 TOPIC: LZOpenFile KEYWORD: LZOpenFile AND QuickInfo However, if lpszFile contains only a filename and extension (i.e. no path is specified), then LZOpenFile() use the same searching logic as OpenFile(). If no file with the matching name (and extension) is found, then it searches for the compressed filename. The compressed file has a _ as the last character (e.g., the compressed version of "readme.txt" is "readme.tx_"). Therefore if you use LZOpenFile() to open a file called "readme.txt", it searches as follows: 1) it looks for "readme.txt" using the same search algorithm as OpenFile(). 2) if the file was not found, it uses the same algorithm to search for "readme.tx_". That means LZOpenFile() might open a "readme.tx_" file from a directory other than the one you expected. Even if you specify the complete filename (such as "c:\demo\readme.txt"), LZOpenFile() first searches for the specified file, and if the specified filename is not found, it searches for the compressed file in the same directory ("c:\demo\readme.tx_"). Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #109 TYPE: Win32 TOPIC: SetCapture KEYWORD: SetCapture AND QuickInfo This help topic does not document the fact that when the mouse is captured, menu hotkeys (for example, Alt+F for accessing the File menu) and other keyboard accelerators do not work (ex: Alt+F4). This is because DefWindowProc() handles the WM_SYSCHAR and WM_SYSCOMMAND messages differently if the mouse has been captured (by any window). DefWindowProc() processes the WM_SYSCHAR message to check if the pressed key is a menu hotkey (Alt+F for example). If it is, then it causes the menu to drop down. However, if the mouse is captured, it does not do the above action. Whenever DefWindowProc() sees a WM_SYSCOMMAND and finds that the mouse is captured, then it does nothing. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #110 TYPE: Win32 TOPIC: RegCreateKey KEYWORD: RegCreateKey AND QuickInfo The documentation for RegCreateKey(), RegCreateKeyEx(), RegOpenKey(), and RegOpenKeyEx() all list several predefined handles you can use, such as HKEY_CURRENT_USER. However, they fail to document the predefined key HKEY_CURRENT_CONFIG, which has a structure similar to the registry tree under HKEY_LOCAL_MACHINE, but is for storing information specific to the current hardware profile. Submitted by Paula Tomlinson. ------------------------------------------------------------ WDJ SDK Annotation #111 TYPE: Win32 TOPIC: CreateFile KEYWORD: CreateFile AND QuickInfo The documentation claims CreateFile() returns a handle that can be used to access the object. However, under both NT and Win95, CreateFile() will appear to succeed and return a valid handle if you attempt to open a file with GENERIC_WRITE permissions on a read-only medium (e.g., protected floppy or CD-ROM). If you then try to perform a write with the returned handle, that will fail. Submitted by: David Lowndes ------------------------------------------------------------ WDJ SDK Annotation #112 TYPE: Win32 TOPIC: GetWindowText KEYWORD: GetWindowText AND QuickInfo The third argument is the "maximum numbers of characters to copy". It may not be clear that this number must include the NULL byte so, for example, it never makes sense to set this argument to 1 since all you could get back is a NULL byte. If you want to use GetWindowText() to retrieve a single character (e.g., from an edit control), you would have to specify a length of 2 -- one for the character and one for the terminating NULL byte. Submitted by: Tony Yuricich ------------------------------------------------------------ WDJ SDK Annotation #113 TYPE: Win32 TOPIC: keybd_event KEYWORD: keybd_event AND QuickInfo You can use this function to toggle keys such as the Caps Lock, Scroll Lock, and Num Lock. Unfortunately, though toggling these three keys works correctly under NT, you cannot use this function to toggle the Num Lock key under Win95. ------------------------------------------------------------ WDJ SDK Annotation #114 TYPE: Win3.1 TOPIC: WM_CTLCOLOR (2.x) KEYWORD: WM_CTLCOLOR AND QuickInfo The documentation incorrectly states that "the return value from this message has no effect on a button with the BS_PUSHBUTTON or BS_DEFPUSHBUTTON style." In fact, returning a brush handle in response to this message appears to determine the color of the pushbutton window background, which is visible as the tiny areas in the corners of the pushbutton window. Note that under Windows 95, pushbuttons fill the entire client area of their windows, thus hiding the window background from view. Submitted by: Forest Wilkinson ------------------------------------------------------------ WDJ SDK Annotation #115 TYPE: Win32 TOPIC: GlobalDeleteAtom KEYWORD: GlobalDeleteAtom AND QuickInfo The documentation for GlobalDeleteAtom states: "The only way to ensure that an atom has been deleted from the atom table is to call this function repeatedly until it fails." Unfortunately, a 32-bit program running under Win NT 3.51 never fails, making for an infinite loop (Windows 95 functions as documented). Microsoft says this may be fixed in the next release (NT 4.0). Submitted by Ken Brown ------------------------------------------------------------ WDJ SDK Annotation #116 TYPE: Win32 TOPIC: LB_ADDSTRING KEYWORD: LB_ADDSTRING AND QuickInfo If your listbox has the WS_HSCROLL style, and if you are adding a string wider than the listbox, you may have to send a LB_SETHORIZONTALEXTENT message to make the horizontal scrollbar appear. The listbox does not automatically check each newly added string and add the horizontal scrollbar when a too-wide string is added or inserted. ------------------------------------------------------------ WDJ SDK Annotation #117 TYPE: Win32 TOPIC: LB_DIR KEYWORD: LB_DIR AND QuickInfo If you pass a long filename to LB_DIR, it works under NT 3.51, but fails under Win95 because it relies on the 16-bit listbox which was not changed to handle long filenames. You can avoid the error by first translating the filename to a short filename by calling GetShortPathName(). The LB_DIR will not fail, but it will still display only the short versions of any long filenames in the subdirectory. Reference: Microsoft Knowledge Base article Q131286 ------------------------------------------------------------ WDJ SDK Annotation #118 TYPE: Win32 TOPIC: ReadFile KEYWORD: ReadFile AND QuickInfo The documentation correctly points out that Win95 does not support OVERLAPPED I/O. However, it incorrectly states that you must pass a pointer to a structure of type OVERLAPPED if the file was created (or opened) with the flag FILE_FLAG_OVERLAPPED. In fact, if you pass a pointer (rather than NULL) under Windows 95, ReadFile() returns FALSE, and GetLastError() returns ERROR_INVALID_PARAMETER, whether or not the file was created/opened with the FILE_FLAG_OVERLAPPED flag. That means that if you want to code a call to ReadFile() that works correctly for both synchronous and asynchronous I/O, you must detect at runtime that you're running under Win95 and treat that case differently. ----------------------------------------------------------------------- WDJ SDK Annotation #119 TYPE: Win32 TOPIC: WinMain KEYWORD: WinMain AND QuickInfo Note that the string passed in lpCmdLine is not the same as the string returned by GetCommandLine(). The string in lpCmdLine contains the command line arguments only, but GetCommandLine() returns the program name followed by the arguments. If you specify the following command: 'winword abc.doc', and winword exists in d:\msoffice\winword.exe, lpCmdLine will contain 'abc.doc', while GetCommandLine will return '"d:\msoffice\winword.exe" abc.doc' Note the double quotes around the exe name. Submitted by Phil Rodgers. ---------------------------------------------------------------------- WDJ SDK Annotation #119 TYPE: Win32 TOPIC: GetCommandLine KEYWORD: GetCommandLine AND QuickInfo Note that the string passed in the lpCmdLine parameter to WinMain is not the same as the string returned by GetCommandLine(). GetCommandLine() returns the complete program name enclosed in double quotes, followed by the arguments; whereas the lpCmdLine parameter of WinMain contains the command line arguments only. If you specify the following command: 'winword abc.doc', and winword exists in d:\msoffice\winword.exe, lpCmdLine will contain 'abc.doc', while GetCommandLine will return '"d:\msoffice\winword.exe" abc.doc' Note the double quotes around the exe name. Submitted by Phil Rodgers. ---------------------------------------------------------------------- WDJ SDK Annotation #120 TYPE: Win32 TOPIC: ShellAbout KEYWORD: ShellAbout AND QuickInfo The documentation says that in Windows 95 ShellAbout will prepend "Microsoft Windows" to the title of the application. This is not true. Actually, this function prepends only "Microsoft", so if your app is called "MyApp", the title will read "Microsoft MyApp". Submitted by Luis A. Ramos. ---------------------------------------------------------------------- WDJ MFC Annotation #121 TYPE: MFC 4 TOPIC: CPropertySheet::SetTitle KEYWORD: SetTitle AND QuickInfo The parameters to this function are interchanged: it should actually read: void SetTitle (LPCTSTR lpszText, UINT nStyle); Submitted by Margaret M. O'Connell. ---------------------------------------------------------------------- WDJ MFC Annotation #122 TYPE: MFC 1.5x TOPIC: CCmdTarget::GetIDispatch KEYWORD: GetIDispatch AND QuickInfo The MFC 1.5x documentation incorrectly states that this function takes no parameters. It should read: LPDISPATCH GetIDispatch (BOOL bAddRef) bAddRef - specifies whether to increment the reference count for the object. This has been corrected in the MFC 4.0 documentation. Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #123 TYPE: Win16 TOPIC: LB_SELECTSTRING KEYWORD: LB_SELECTSTRING win31wh.hlp fails to mention that this message works only with single select listboxes. For multi-select listboxes it returns LB_ERR. Submitted by Dino Esposito. ---------------------------------------------------------------------- WDJ SDK Annotation #124 TYPE: Win32 TOPIC: AdjustWindowRect KEYWORD: AdjustWindowRect AND QuickInfo The documentation for AdjustWindowRect() and AdjustWindowRectEx() has a number of problems. 1. Though the documentation says that window titles and borders are not taken into account, they function does account for these styles. 2. Add 1 to the rect.bottom returned by this function. There is a one-off error in the y direction. 3. The meaning of the rect parameter is not well explained. The input to AdjustWindowRectEx is the window coordinates of the top-left and bottom-right corners of the desired client area. AdjustWindowRectEx inflates the specified rectangle to include the caption, border and other non-client objects specified by the style parameter. Reference: MSDN Dr.GUI #10 - Calculated Client Window Size. Submitted by Etay Szekely. ---------------------------------------------------------------------- WDJ SDK Annotation #124 TYPE: Win32 TOPIC: AdjustWindowRectEx KEYWORD: AdjustWindowRectEx AND QuickInfo The documentation for AdjustWindowRect() and AdjustWindowRectEx() has a number of problems. 1. Though the documentation says that window titles and borders are not taken into account, they function does account for these styles. 2. Add 1 to the rect.bottom returned by this function. There is a one-off error in the y direction. 3. The meaning of the rect parameter is not well explained. The input to AdjustWindowRectEx is the window coordinates of the top-left and bottom-right corners of the desired client area. AdjustWindowRectEx inflates the specified rectangle to include the caption, border and other non-client objects specified by the style parameter. Reference: MSDN Dr.GUI #10 - Calculated Client Window Size. Submitted by Etay Szekely. ---------------------------------------------------------------------- WDJ MFC Annotation #125 TYPE: MFC 3.1 and 3.2 TOPIC: CImageList::DeleteObject KEYWORD: DeleteObject AND QuickInfo VC++ 2.1 and 2.2 help files incorrectly documented this function to delete image lists, while it actually never existed. The correct function to delete an image list is CImageList::DeleteImageList(); it returns non-zero if successful and zero if it fails. This error is corrected in the VC++ 4.0 documentation. Submitted by Barry Tannenbaum. ---------------------------------------------------------------------- WDJ SDK Annotation #126 TYPE: Win32 TOPIC: AVIFileOpen KEYWORD: AVIFileOpen The documentation specifies that using the OF_CREATE flag will cause an existing file to be truncated to zero length. In reality, it has no effect: the file length remains the same, and the old data is intact once the file is closed. Submitted by Tim Lesher. ---------------------------------------------------------------------- WDJ SDK Annotation #127 TYPE: Win16 TOPIC: CBTProc KEYWORD: CBTProc The CBTProc documentation fails to mention that you can return 0 to allow the operation and 1 to prevent it for HCBT_SETFOCUS notification also. The documentation has been corrected in the Win32 help file. Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #128 TYPE: Win32 TOPIC: GetDialogBaseUnits KEYWORD: GetDialogBaseUnits AND QuickInfo GetDialogBaseUnits() does not return correct dialog base units if the dialog is not using the system font. Use the following function instead: DWORD WDJ_GetDialogBaseUnits (HWND Dialog) { int BaseX, BaseY; RECT R; SetRect (&R, 0, 0, 4, 8); MapDialogRect (Dialog, &R); BaseX = R.right; BaseY = R.bottom; return (DWORD)MAKELONG (BaseX, BaseY); } Reference: p54, January 1996 Windows Developer's Journal.